EGI-FCTF/rOCCI-core

View on GitHub
lib/occi/core/renderers/text/attributes.rb

Summary

Maintainability
A
45 mins
Test Coverage
require 'occi/core/renderers/text/base'

module Occi
  module Core
    module Renderers
      module Text
        # Implements methods needed to render instance attributes to text-based
        # renderings. This class (its instances) is usually called directly from
        # other classes in this module and not from the "outside".
        #
        # @author Boris Parak <parak@cesnet.cz
        class Attributes < Base
          # Attribute key constant
          ATTRIBUTE_KEY = 'X-OCCI-Attribute'.freeze

          # Known primitive attribute value types
          PRIMITIVE_TYPES = [String, Numeric, Integer, Float, Boolean].freeze
          IP_TYPES        = [IPAddr].freeze
          QUOTABLE_TYPES  = [URI].freeze
          JSONABLE_TYPES  = [Array, Hash].freeze

          # Renders `object` into plain text and returns the result
          # as `String`.
          #
          # @return [String] textual representation of Object
          def render_plain
            prepare_instance_attributes.collect { |attrb| "#{ATTRIBUTE_KEY}: #{attrb}" }.join("\n")
          end

          # Renders `object` into text for headers and returns the result
          # as `Hash`.
          #
          # @return [Hash] textual representation of Object for headers
          def render_headers
            { ATTRIBUTE_KEY => prepare_instance_attributes }
          end

          private

          # :nodoc:
          def prepare_instance_attributes
            object.select { |_, attribute| attribute.value? }
                  .collect { |name, attribute| "#{name}=#{prepare_instance_attribute(name, attribute)}" }
          end

          # :nodoc:
          def prepare_instance_attribute(name, attribute)
            unless attribute.respond_to?(:attribute_definition)
              raise Occi::Core::Errors::RenderingError, "Attribute #{name} does " \
                    'not expose its definition'
            end
            attr_def = attribute.attribute_definition
            valid_definition! name, attr_def

            prepare_instance_attribute_value(name, attr_def.type, attribute.value)
          end

          # :nodoc:
          def valid_definition!(name, attribute_definition)
            unless attribute_definition
              raise Occi::Core::Errors::RenderingError, 'Cannot render attribute ' \
                    "#{name} without a definition"
            end

            return if attribute_definition.type
            raise Occi::Core::Errors::RenderingError, 'Cannot render attribute ' \
                  "#{name} without a type"
          end

          # :nodoc:
          def prepare_instance_attribute_value(name, type, value)
            if ancestor_match?(IP_TYPES, type)
              value.host? ? "\"#{value}\"" : "\"#{value}/#{value.cidr_mask}\""
            elsif ancestor_match?(QUOTABLE_TYPES, type)
              "\"#{value}\""
            elsif ancestor_match?(JSONABLE_TYPES, type)
              value.to_json.inspect
            elsif ancestor_match?(PRIMITIVE_TYPES, type)
              value.inspect
            else
              raise Occi::Core::Errors::RenderingError, "Value #{value.inspect} " \
                    "for attribute #{name} cannot be rendered to text"
            end
          end

          # :nodoc:
          def ancestor_match?(defined_types, type)
            (defined_types & type.ancestors).any?
          end
        end
      end
    end
  end
end