lib/render/schema.rb in render-0.0.2 vs lib/render/schema.rb in render-0.0.3

- old
+ new

@@ -1,114 +1,100 @@ -# The Schema defines a collection of attributes. -# It is responsible for returning its attributes' values back to its Graph. +# The Schema defines a collection of properties. +# It is responsible for returning its properties' values back to its Graph. require "net/http" require "json" require "render" require "render/attribute" +require "render/array_attribute" +require "render/hash_attribute" +require "render/dottable_hash" module Render class Schema + DEFAULT_TITLE = "untitled".freeze + attr_accessor :title, :type, - :attributes, - :schema + :definition, + :array_attribute, + :hash_attributes, + :universal_title, + :raw_data, + :serialized_data, + :rendered_data - # The schema need to know where its getting a value from - # an Attribute, e.g. { foo: "bar" } => { foo: { type: String } } - # an Archetype, e.g. [1,2,3] => { type: Integer } # could this be a pass-through? - # an Attribute-Schema, e.g. { foo: { bar: "baz" } } => { foo: { type: Object, attributes: { bar: { type: String } } } - # an Attribute-Array, e.g. [{ foo: "bar" }] => { type: Array, elements: { type: Object, attributes: { foo: { type: String } } } } - # and we need to identify when given { ids: [1,2] }, parental_mapping { ids: id } means to make 2 calls - def initialize(schema_or_title) - self.schema = schema_or_title.is_a?(Hash) ? schema_or_title : find_schema(schema_or_title) - self.title = schema[:title] - self.type = Render.parse_type(schema[:type]) + # TODO When given { ids: [1,2] }, parental_mapping { ids: id } means to make 2 calls + def initialize(definition_or_title) + Render.logger.debug("Loading #{definition_or_title}") - if array_of_schemas?(schema[:elements]) - self.attributes = [Attribute.new({ elements: schema[:elements] })] + self.definition = determine_definition(definition_or_title) + title_or_default = definition.fetch(:title, DEFAULT_TITLE) + self.title = title_or_default.to_sym + self.type = Render.parse_type(definition[:type]) + self.universal_title = definition.fetch(:universal_title, nil) + + if definition.keys.include?(:items) + self.array_attribute = ArrayAttribute.new(definition) else - definitions = schema[:attributes] || schema[:elements] - self.attributes = definitions.collect do |key, value| - Attribute.new({ key => value }) + self.hash_attributes = definition.fetch(:properties).collect do |name, attribute_definition| + HashAttribute.new({ name => attribute_definition }) end end end - def array_of_schemas?(definition = {}) - return false unless definition - definition.keys.include?(:attributes) + def serialize!(explicit_data = nil) + if (type == Array) + self.serialized_data = array_attribute.serialize(explicit_data) + else + self.serialized_data = hash_attributes.inject({}) do |processed_explicit_data, attribute| + explicit_data ||= {} + value = explicit_data.fetch(attribute.name, nil) + serialized_attribute = attribute.serialize(value) + processed_explicit_data.merge!(serialized_attribute) + end + end end - def render(options = {}) - endpoint = options.delete(:endpoint) - data = Render.live ? request(endpoint) : options - { title.to_sym => serialize(data) } + def render!(options_and_explicit_data = nil) + endpoint = options_and_explicit_data.delete(:endpoint) if options_and_explicit_data.is_a?(Hash) + self.raw_data = Render.live ? request(endpoint) : options_and_explicit_data + serialize!(raw_data) + yield serialized_data if block_given? + self.rendered_data = DottableHash.new(hash_with_title_prefixes(serialized_data)) end - def serialize(data) - # data.is_a?(Array) ? to_array(data) : to_hash(data) - (type == Array) ? to_array(data) : to_hash(data) - end - private - def find_schema(title) - loaded_schema = Render.schemas[title.to_sym] - raise Errors::Schema::NotFound.new(title) if !loaded_schema - loaded_schema + def determine_definition(definition_or_title) + if (definition_or_title.is_a?(Hash) && !definition_or_title.empty?) + definition_or_title + else + Render.definition(definition_or_title) + end end + def hash_with_title_prefixes(data) + if universal_title + { universal_title => { title => data } } + else + { title => data } + end + end + def request(endpoint) + default_request(endpoint) + end + + def default_request(endpoint) response = Net::HTTP.get_response(URI(endpoint)) if response.kind_of?(Net::HTTPSuccess) - response = JSON.parse(response.body).recursive_symbolize_keys! - if (response.is_a?(Array) || (response[title.to_sym] == nil)) - response - else - response[title.to_sym] - end + JSON.parse(response.body).recursive_symbolize_keys! else raise Errors::Schema::RequestError.new(endpoint, response) end rescue JSON::ParserError => error raise Errors::Schema::InvalidResponse.new(endpoint, response.body) end - def to_array(elements) - # elements.first.is_a?(Hash) ? to_array_of_schemas(elements) : to_array_of_elements(elements) - attributes.first.schema_value? ? to_array_of_schemas(elements) : to_array_of_elements(elements) - end - - def to_array_of_elements(elements) - (elements = stubbed_array) if !Render.live && (!elements || elements.empty?) - archetype = attributes.first # there should only be one in the event that it's an array schema - elements.collect do |element| - archetype.serialize(element) - end - end - - def to_array_of_schemas(elements) - (elements = stubbed_array) if !Render.live && (!elements || elements.empty?) - elements.collect do |element| - attributes.inject({}) do |attributes, attribute| - attributes.merge(attribute.to_hash(element)).values.first - end - end - end - - def to_hash(explicit_values = {}) - explicit_values ||= {} # !Render.live check - attributes.inject({}) do |accum, attribute| - explicit_value = explicit_values[attribute.name] - hash = attribute.to_hash(explicit_value) - accum.merge(hash) - end - end - - def stubbed_array - elements = [] - rand(1..3).times { elements << nil } - elements - end end end