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