lib/mida/item.rb in mida-0.2.0 vs lib/mida/item.rb in mida-0.3.0

- old
+ new

@@ -1,11 +1,13 @@ require 'nokogiri' +require 'mida' module Mida - # Class that holds each item/itemscope + # Class that holds a validated item class Item + # The vocabulary used to interpret this item attr_reader :vocabulary # The Type of the item attr_reader :type @@ -16,33 +18,30 @@ # A Hash representing the properties as name/values paris # The values will be an array containing either +String+ # or <tt>Mida::Item</tt> instances attr_reader :properties - # Create a new Item object + # Create a new Item object from an +Itemscope+ and validates + # its +properties+ # - # [itemscope] The itemscope that you want to parse. - # [page_url] The url of target used for form absolute url. - def initialize(itemscope, page_url=nil) - @itemscope, @page_url = itemscope, page_url - @type, @id = extract_attribute('itemtype'), extract_attribute('itemid') + # [itemscope] The itemscope that has been parsed by +Itemscope+ + def initialize(itemscope) + @type = itemscope.type + @id = itemscope.id @vocabulary = Mida::Vocabulary.find(@type) - @properties = {} - add_itemref_properties - parse_elements(extract_elements(@itemscope)) + @properties = itemscope.properties validate_properties end # Return a Hash representation # of the form: - # { vocabulary: 'http://example.com/vocab/review', - # type: 'The item type', + # { type: 'http://example.com/vocab/review', # id: 'urn:isbn:1-934356-08-5', # properties: {'a name' => 'avalue' } # } def to_h - {vocabulary: @vocabulary, type: @type, id: @id, properties: properties_to_h(@properties)} + {type: @type, id: @id, properties: properties_to_h(@properties)} end def to_s to_h.to_s end @@ -56,99 +55,112 @@ # Validate the properties so that they are in their proper form def validate_properties @properties = @properties.each_with_object({}) do |(property, values), hash| - if valid_property?(property, values) - hash[property] = valid_values(property, values) - end + valid_values = validate_values(property, values) + hash[property] = valid_values unless valid_values.nil? end end - # Return whether the number of values conforms to the spec - def valid_num_values?(property, values) - return false unless @vocabulary.prop_spec.has_key?(property) - property_spec = @vocabulary.prop_spec[property] - (property_spec[:num] == :many || - (property_spec[:num] == :one && values.length == 1)) + # Return whether the number of values conforms to +num+ + def valid_num_values?(num, values) + num == :many || (num == :one && values.length == 1) end + # Return whether this property name is valid def valid_property?(property, values) - [property, :any].any? {|prop| valid_num_values?(prop, values)} + [property, :any].any? do |prop| + @vocabulary.properties.has_key?(prop) + end end - def valid_values(property, values) - prop_types = if @vocabulary.prop_spec.has_key?(property) - @vocabulary.prop_spec[property][:types] + # Return valid values, converted to the correct +DataType+ + # or +Item+ and number if necessary + def validate_values(property, values) + return nil unless valid_property?(property, values) + prop_num = property_number(property) + return nil unless valid_num_values?(prop_num, values) + prop_types = property_types(property) + + valid_values = values.each_with_object([]) do |value, valid_values| + new_value = validate_value(prop_types, value) + valid_values << new_value unless new_value.nil? + end + + # Convert property to correct number + prop_num == :many ? valid_values : valid_values.first + end + + # Returns value converted to correct +DataType+ or +Item+ + # or +nil+ if not valid + def validate_value(prop_types, value) + if is_itemscope?(value) + valid_itemtype?(prop_types, value.type) ? Item.new(value) : nil + elsif (extract_value = datatype_extract(prop_types, value)) + extract_value + elsif prop_types.include?(:any) + value else - @vocabulary.prop_spec[:any][:types] + nil end + end - values.select {|value| valid_type(prop_types, value) } + # Return the correct type for this property + def property_types(property) + if @vocabulary.properties.has_key?(property) + @vocabulary.properties[property][:types] + else + @vocabulary.properties[:any][:types] + end end - def valid_type(prop_types, value) - if value.respond_to?(:vocabulary) - if prop_types.include?(value.vocabulary) || prop_types.include?(:any) - return true - end - elsif prop_types.include?(value.class) || prop_types.include?(:any) - return true + # Return the correct number for this property + def property_number(property) + if @vocabulary.properties.has_key?(property) + @vocabulary.properties[property][:num] + else + @vocabulary.properties[:any][:num] end - false end - def extract_attribute(attribute) - (value = @itemscope.attribute(attribute)) ? value.value : nil + def is_itemscope?(object) + object.kind_of?(Itemscope) end - def extract_elements(itemscope) - itemscope.search('./*') + # Returns whether the +itemtype+ is a valid type + def valid_itemtype?(valid_types, itemtype) + return true if valid_types.include?(:any) + + valid_types.find do |type| + type.respond_to?(:itemtype) && type.itemtype =~ itemtype + end end - # Find an element with a matching id - def find_with_id(id) - @itemscope.search("//*[@id='#{id}']") + # Returns the extracted value or +nil+ if none of the datatypes + # could extract the +value+ + def datatype_extract(valid_types, value) + valid_types.find do |type| + begin + return type.extract(value) if type.respond_to?(:extract) + rescue ArgumentError + end + end + nil end # The value as it should appear in to_h() def value_to_h(value) - case - when value.is_a?(Array) then value.collect {|element| value_to_h(element)} - when value.is_a?(Item) then value.to_h + if value.is_a?(Array) then value.collect {|element| value_to_h(element)} + elsif value.is_a?(Item) then value.to_h else value end end def properties_to_h(properties) properties.each_with_object({}) do |(name, value), hash| hash[name] = value_to_h(value) end - end - - # Add any properties referred to by 'itemref' - def add_itemref_properties - itemref = extract_attribute('itemref') - if itemref - itemref.split.each {|id| parse_elements(find_with_id(id))} - end - end - - def parse_elements(elements) - elements.each {|element| parse_element(element)} - end - - def parse_element(element) - itemscope = element.attribute('itemscope') - itemprop = element.attribute('itemprop') - internal_elements = extract_elements(element) - add_itemprop(element) if itemscope || itemprop - parse_elements(internal_elements) if internal_elements && !itemscope - end - - def add_itemprop(itemprop) - properties = Itemprop.parse(itemprop, @page_url) - properties.each { |name, value| (@properties[name] ||= []) << value } end end end