lib/happymapper.rb in jnunemaker-happymapper-0.2.0 vs lib/happymapper.rb in jnunemaker-happymapper-0.2.1

- old
+ new

@@ -3,19 +3,21 @@ require 'date' require 'time' require 'rubygems' -gem 'libxml-ruby', '>= 0.9.7' +gem 'libxml-ruby', '= 0.9.8' require 'xml' require 'libxml_ext/libxml_helper' class Boolean; end module HappyMapper - + + DEFAULT_NS = "happymapper" + def self.included(base) base.instance_variable_set("@attributes", {}) base.instance_variable_set("@elements", {}) base.extend ClassMethods end @@ -48,73 +50,83 @@ end def has_many(name, type, options={}) element name, type, {:single => false}.merge(options) end - - # Options: - # :root => Boolean, true means this is xml root - def tag(new_tag_name, o={}) - options = {:root => false}.merge(o) - @root = options.delete(:root) + + # Specify a namespace if a node and all its children are all namespaced + # elements. This is simpler than passing the :namespace option to each + # defined element. + def namespace(namespace = nil) + @namespace = namespace if namespace + @namespace + end + + def tag(new_tag_name) @tag_name = new_tag_name.to_s end def get_tag_name @tag_name ||= begin to_s.split('::')[-1].downcase end end - def is_root? - @root - end - - def parse(xml, o={}) - xpath, collection, options = '', [], {:single => false}.merge(o) - doc = xml.is_a?(LibXML::XML::Node) ? xml : xml.to_libxml_doc - node = doc.respond_to?(:root) ? doc.root : doc - - # puts doc.inspect, doc.respond_to?(:root) ? doc.root.inspect : '' - - unless node.namespaces.default.nil? - namespace = "default_ns:" - node.namespaces.default_prefix = namespace.chop - # warn "Default XML namespace present -- results are unpredictable" + def parse(xml, options = {}) + # locally scoped copy of namespace for this parse run + namespace = @namespace + + if xml.is_a?(XML::Node) + node = xml + else + if xml.is_a?(XML::Document) + node = xml.root + else + node = xml.to_libxml_doc.root + end + + root = node.name == get_tag_name end - - if node.namespaces.to_a.size > 0 && namespace.nil? && !node.namespaces.namespace.nil? - namespace = node.namespaces.namespace.prefix + ":" + + # This is the entry point into the parsing pipeline, so the default + # namespace prefix registered here will propagate down + namespaces = node.namespaces + if namespaces && namespaces.default + namespaces.default_prefix = DEFAULT_NS + namespace ||= DEFAULT_NS end - - # xpath += doc.respond_to?(:root) ? '' : '.' - xpath += is_root? ? '/' : './/' - xpath += namespace if namespace + + xpath = root ? '/' : './/' + xpath += "#{namespace}:" if namespace xpath += get_tag_name # puts "parse: #{xpath}" nodes = node.find(xpath) - nodes.each do |n| + collection = nodes.collect do |n| obj = new attributes.each do |attr| obj.send("#{attr.method_name}=", - attr.from_xml_node(n)) + attr.from_xml_node(n, namespace)) end elements.each do |elem| - elem.namespace = namespace obj.send("#{elem.method_name}=", - elem.from_xml_node(n)) + elem.from_xml_node(n, namespace)) end - collection << obj + + obj end # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354 nodes = nil GC.start - options[:single] || is_root? ? collection.first : collection + if options[:single] || root + collection.first + else + collection + end end end end require 'happymapper/item'