lib/happymapper.rb in nokogiri-happymapper-0.5.3 vs lib/happymapper.rb in nokogiri-happymapper-0.5.4

- old
+ new

@@ -5,16 +5,19 @@ class Boolean; end class XmlContent; end module HappyMapper + VERSION = "0.5.4" + DEFAULT_NS = "happymapper" def self.included(base) base.instance_variable_set("@attributes", {}) base.instance_variable_set("@elements", {}) base.instance_variable_set("@registered_namespaces", {}) + base.instance_variable_set("@wrapper_anonymous_classes", {}) base.extend ClassMethods end module ClassMethods @@ -194,11 +197,43 @@ # @return [String] the name of the tag as a string, downcased # def tag_name @tag_name ||= to_s.split('::')[-1].downcase end + + # There is an XML tag that needs to be known for parsing and should be generated + # during a to_xml. But it doesn't need to be a class and the contained elements should + # be made available on the parent class + # + # @param [String] name the name of the element that is just a place holder + # @param [Proc] blk the element definitions inside the place holder tag + # + def wrap(name, &blk) + # Get an anonymous HappyMapper that has 'name' as its tag and defined + # in '&blk'. Then save that to a class instance variable for later use + wrapper = AnonymousWrapperClassFactory.get(name, &blk) + @wrapper_anonymous_classes[wrapper.inspect] = wrapper + # Create getter/setter for each element and attribute defined on the anonymous HappyMapper + # onto this class. They get/set the value by passing thru to the anonymous class. + passthrus = wrapper.attributes + wrapper.elements + passthrus.each do |item| + class_eval %{ + def #{item.method_name} + @#{name} ||= self.class.instance_variable_get('@wrapper_anonymous_classes')['#{wrapper.inspect}'].new + @#{name}.#{item.method_name} + end + def #{item.method_name}=(value) + @#{name} ||= self.class.instance_variable_get('@wrapper_anonymous_classes')['#{wrapper.inspect}'].new + @#{name}.#{item.method_name} = value + end + } + end + + has_one name, wrapper + end + # # @param [Nokogiri::XML::Node,Nokogiri:XML::Document,String] xml the XML # contents to convert into Object. # @param [Hash] options additional information for parsing. :single => true # if requesting a single object, otherwise it defaults to retuning an @@ -387,17 +422,21 @@ # # @param [Nokogiri::XML::Builder] builder an instance of the XML builder which # is being used when called recursively. # @param [String] default_namespace the name of the namespace which is the # default for the xml being produced; this is specified by the element - # declaration when calling #to_xml recursively. + # declaration when calling #to_xml recursively. + # @param [String] tag_from_parent the xml tag to use on the element when being + # called recursively. This lets the parent doc define its own structure. + # Otherwise the element uses the tag it has defined for itself. Should only + # apply when calling a child HappyMapper element. # # @return [String,Nokogiri::XML::Builder] return XML representation of the # HappyMapper object; when called recursively this is going to return # and Nokogiri::XML::Builder object. # - def to_xml(builder = nil,default_namespace = nil) + def to_xml(builder = nil,default_namespace = nil,tag_from_parent = nil) # # If to_xml has been called without a passed in builder instance that # means we are going to return xml output. When it has been called with # a builder instance that means we most likely being called recursively @@ -452,16 +491,17 @@ end end.flatten attributes = Hash[ *attributes ] - + # - # Create a tag in the builder that matches the class's tag name and append + # Create a tag in the builder that matches the class's tag name unless a tag was passed + # in a recursive call from the parent doc. Then append # any attributes to the element that were defined above. # - builder.send("#{self.class.tag_name}_",attributes) do |xml| + builder.send("#{tag_from_parent || self.class.tag_name}_",attributes) do |xml| # # Add all the registered namespaces to the root element. # When this is called recurisvely by composed classes the namespaces # are still added to the root element @@ -573,11 +613,11 @@ # # Other items are convertable to xml through the xml builder # process should have their contents retrieved and attached # to the builder structure # - item.to_xml(xml,element.options[:namespace]) + item.to_xml(xml,element.options[:namespace],element.options[:tag] || nil) elsif item item_namespace = element.options[:namespace] || self.class.namespace || default_namespace @@ -622,13 +662,26 @@ # created for any HappyMapper children of this object. # # Params and return are the same as the class parse() method above. def parse(xml, options = {}) self.class.parse(xml, options.merge!(:update => self)) + end + + private + + # Factory for creating anonmyous HappyMappers + class AnonymousWrapperClassFactory + def self.get(name, &blk) + Class.new do + include HappyMapper + tag name + instance_eval &blk + end + end end end -require 'happymapper/item' -require 'happymapper/attribute' -require 'happymapper/element' -require 'happymapper/text_node' +require File.dirname(__FILE__) + '/happymapper/item' +require File.dirname(__FILE__) + '/happymapper/attribute' +require File.dirname(__FILE__) + '/happymapper/element' +require File.dirname(__FILE__) + '/happymapper/text_node'