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