diff --git a/lib/microformats2/collection.rb b/lib/microformats2/collection.rb index c9b4bf2..867a4d7 100644 --- a/lib/microformats2/collection.rb +++ b/lib/microformats2/collection.rb @@ -3,7 +3,8 @@ module Microformats2 attr_accessor :formats def parse(document) - formats = FormatParser.parse(document) + @formats = FormatParser.parse(document) + self end def to_hash diff --git a/lib/microformats2/format.rb b/lib/microformats2/format.rb index 72f08d2..18789bc 100644 --- a/lib/microformats2/format.rb +++ b/lib/microformats2/format.rb @@ -2,20 +2,50 @@ module Microformats2 class Format CLASS_REG_EXP = /^(h-)/ - attr_reader :element, :properties, :format_types + attr_reader :element def initialize(element) @element = element - @format_types = [] - @properties = [] + @added_methods = [] end def parse - properties << PropertyParser.parse(element) + properties format_types self end + def properties + @properties ||= create_properties + end + + def create_properties + PropertyParser.parse(element).map do |property| + save_method_name(property.name) + define_method(property.name) + set_value(property.name, property) + end + end + + def save_method_name(method_name) + unless @added_methods.include?(method_name) + @added_methods << method_name + end + end + + def define_method(method_name) + unless respond_to?(method_name) + self.class.class_eval { attr_accessor method_name } + end + end + + def set_value(method_name, value) + if current = send(method_name) + current << value + else + send("#{method_name}=", [value]) + end + end def format_types @format_types ||= element.attribute("class").to_s.split.select do |html_class| html_class =~ Format::CLASS_REG_EXP @@ -23,8 +53,8 @@ module Microformats2 end def to_hash - hash = { type: @format_types, properties: {} } - properties.each do |method_name| + hash = { type: format_types, properties: {} } + @added_methods.each do |method_name| value = send(method_name) value = value.is_a?(Array) ? value : [value] hash[:properties][method_name.to_sym] = value.map(&:to_hash) diff --git a/lib/microformats2/format_parser.rb b/lib/microformats2/format_parser.rb index 7dc096c..760e8b9 100644 --- a/lib/microformats2/format_parser.rb +++ b/lib/microformats2/format_parser.rb @@ -2,13 +2,14 @@ module Microformats2 class FormatParser class << self def parse(element) - parse_node(element) + parse_node(element).flatten.compact end def parse_node(node) case - when node.is_a?(Nokogiri::XML::NodeSet) then parse_nodeset(node) - when node.is_a?(Nokogiri::XML::Element) then parse_for_microformats(node) + when node.is_a?(Nokogiri::HTML::Document) then parse_node(node.children) + when node.is_a?(Nokogiri::XML::NodeSet) then parse_nodeset(node) + when node.is_a?(Nokogiri::XML::Element) then [parse_for_microformats(node)] end end @@ -25,22 +26,11 @@ module Microformats2 end def parse_microformat(element) - # only worry about the first format for now + # only create ruby object for first format class html_class = format_classes(element).first - # class-name -> class_name - method_name = html_class.downcase.gsub("-","_") - # class_name -> Class_name -> ClassName - constant_name = method_name.gsub(/^([a-z])/){$1.upcase}.gsub(/_(.)/){$1.upcase} + const_name = constant_name(html_class) + klass = find_or_create_ruby_class(const_name) - # find or create ruby class for microformat - if Object.const_defined?(constant_name) - klass = Object.const_get(constant_name) - else - klass = Class.new(Microformats2::Format) - Object.const_set constant_name, klass - end - - # parse microformat klass.new(element).parse end @@ -49,6 +39,21 @@ module Microformats2 html_class =~ Format::CLASS_REG_EXP end end + + def constant_name(html_class) + # html-Class -> html-class -> html_class -> Html_class -> HtmlClass + html_class.downcase.gsub("-","_").gsub(/^([a-z])/){$1.upcase}.gsub(/_(.)/){$1.upcase} + end + + def find_or_create_ruby_class(const_name) + if Object.const_defined?(const_name) + klass = Object.const_get(const_name) + else + klass = Class.new(Microformats2::Format) + Object.const_set const_name, klass + end + klass + end end end end diff --git a/lib/microformats2/property.rb b/lib/microformats2/property.rb index 8be740c..9204fae 100644 --- a/lib/microformats2/property.rb +++ b/lib/microformats2/property.rb @@ -5,7 +5,6 @@ module Microformats2 "p" => Text, "u" => Url, "dt" => DateTime, - "e" => Embedded - } + "e" => Embedded } end end diff --git a/lib/microformats2/property/foundation.rb b/lib/microformats2/property/foundation.rb index e82ccd4..fa781fe 100644 --- a/lib/microformats2/property/foundation.rb +++ b/lib/microformats2/property/foundation.rb @@ -1,21 +1,33 @@ module Microformats2 module Property class Foundation - attr_accessor :element, :value, :formats + attr_accessor :element, :name, :value, :formats - def initialize(element) + def initialize(element, html_class) @element = element - @formats = [] + @name = to_method_name(html_class) end + def to_method_name(html_class) + # p-class-name -> class_name + method_name = html_class.downcase.split("-")[1..-1].join("_") + # avoid overriding Object#class + method_name = "klass" if method_name == "class" + method_name + end + def parse - formats << FormatParser.parse(element) if format_classes.length >=1 + formats value self end + def formats + @formats ||= format_classes.length >=1 ? FormatParser.parse(element) : [] + end + def format_classes - element.attribute("class").to_s.split.select do |html_class| + @format_classes = element.attribute("class").to_s.split.select do |html_class| html_class =~ Format::CLASS_REG_EXP end end diff --git a/lib/microformats2/property_parser.rb b/lib/microformats2/property_parser.rb index ab731ab..45f8558 100644 --- a/lib/microformats2/property_parser.rb +++ b/lib/microformats2/property_parser.rb @@ -2,7 +2,7 @@ module Microformats2 class PropertyParser class << self def parse(element) - parse_node(element) + parse_node(element).flatten.compact end def parse_node(node) @@ -24,24 +24,18 @@ module Microformats2 end end - def parse_property(element, html_classes) - property_classes(element).each do |property_class| + def parse_property(element) + property_classes(element).map do |property_class| # p-class-name -> p prefix = property_class.split("-").first - # p-class-name -> class_name - method_name = property_class.split("-")[1..-1].join("_") - # avoid overriding Object#class - method_name = "klass" if method_name == "class" - # find ruby class for kind of property klass = Microformats2::Property::PREFIX_CLASS_MAP[prefix] - # parse property - klass.new(element).parse + klass.new(element, property_class).parse end end - def property_classes(element, regexp) + def property_classes(element) element.attribute("class").to_s.split.select do |html_class| html_class =~ Property::CLASS_REG_EXP end diff --git a/spec/lib/microformats2/collection_spec.rb b/spec/lib/microformats2/collection_spec.rb index 2aed1fc..45f6b00 100644 --- a/spec/lib/microformats2/collection_spec.rb +++ b/spec/lib/microformats2/collection_spec.rb @@ -25,21 +25,21 @@ describe Microformats2::Collection do describe "#parse" do it "creates ruby class HCard" do - @collection.first.should be_kind_of HCard + @collection.formats.first.should be_kind_of HCard end it "assigns .h-card .p-name to HCard#name" do - @collection.first.name.value.should == "Jessica Lynn Suttles" + @collection.formats.first.name.first.value.should == "Jessica Lynn Suttles" end it "assigns both .h-card .u-url to HCard#url" do urls = ["http://flickr.com/jlsuttles", "http://twitter.com/jlsuttles"] - @collection.first.url.map(&:value).should == urls + @collection.formats.first.url.map(&:value).should == urls end it "assings .h-card .dt-bday to HCard#bday" do - @collection.first.bday.value.should be_kind_of DateTime - @collection.first.bday.value.to_s.should == "1990-10-15T20:45:33-08:00" + @collection.formats.first.bday.first.value.should be_kind_of DateTime + @collection.formats.first.bday.first.value.to_s.should == "1990-10-15T20:45:33-08:00" end it "assigns .h-card .e-content to HCard#content" do - @collection.first.content.value.should == "Vegan. Cat lover. Coder." + @collection.formats.first.content.first.value.should == "Vegan. Cat lover. Coder." end end @@ -69,10 +69,10 @@ describe Microformats2::Collection do describe "#parse" do it "creates ruby class HEntry" do - @collection.first.should be_kind_of HEntry + @collection.formats.first.should be_kind_of HEntry end it "assigns .h-entry .p-author to HEntry#author" do - @collection.first.author.value.should == "Jessica Lynn Suttles" + @collection.formats.first.author.value.should == "Jessica Lynn Suttles" end end