lib/happymapper.rb in nokogiri-happymapper-0.5.9 vs lib/happymapper.rb in nokogiri-happymapper-0.6.0

- old
+ new

@@ -107,12 +107,12 @@ # the object will be converted upon parsing # @param [Hash] options additional parameters to send to the relationship # def element(name, type, options={}) element = Element.new(name, type, options) + attr_accessor element.method_name.intern unless @elements[name] @elements[name] = element - attr_accessor element.method_name.intern end # # The elements defined through {#element}, {#has_one}, and {#has_many}. # @@ -183,20 +183,36 @@ def has_many(name, type, options={}) element name, type, {:single => false}.merge(options) end # + # The list of registered after_parse callbacks. + # + def after_parse_callbacks + @after_parse_callbacks ||= [] + end + + # + # Register a new after_parse callback, given as a block. + # + # @yield [object] Yields the newly-parsed object to the block after parsing. + # Sub-objects will be already populated. + def after_parse(&block) + after_parse_callbacks.push(block) + end + + # # 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. # # @param [String] namespace the namespace to set as default for the class # element. # def namespace(namespace = nil) @namespace = namespace if namespace - @namespace + @namespace if defined? @namespace end # # @param [String] new_tag_name the name for the tag # @@ -247,13 +263,11 @@ # The callback defined through {.with_nokogiri_config}. # # @return [Proc] the proc to pass to Nokogiri to setup parse options. nil if empty. # - def nokogiri_config_callback - @nokogiri_config_callback - end + attr_reader :nokogiri_config_callback # Register a config callback according to the block Nokogori expects when calling Nokogiri::XML::Document.parse(). # See http://nokogiri.org/Nokogiri/XML/Document.html#method-c-parse # # @param [Proc] the proc to pass to Nokogiri to setup parse options @@ -271,11 +285,11 @@ # :namespace is the namespace to use for additional information. # def parse(xml, options = {}) # create a local copy of the objects namespace value for this parse execution - namespace = @namespace + namespace = (@namespace if defined? @namespace) # If the XML specified is an Node then we have what we need. if xml.is_a?(Nokogiri::XML::Node) && !xml.is_a?(Nokogiri::XML::Document) node = xml else @@ -408,32 +422,36 @@ elements.each do |elem| obj.send("#{elem.method_name}=",elem.from_xml_node(n, namespace, namespaces)) end - if @content + if (defined? @content) && @content obj.send("#{@content.method_name}=",@content.from_xml_node(n, namespace, namespaces)) end # If the HappyMapper class has the method #xml_value=, # attr_writer :xml_value, or attr_accessor :xml_value then we want to # assign the current xml that we just parsed to the xml_value if obj.respond_to?('xml_value=') n.namespaces.each {|name,path| n[name] = path } - obj.xml_value = n.to_xml + obj.xml_value = n.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML) end # If the HappyMapper class has the method #xml_content=, # attr_write :xml_content, or attr_accessor :xml_content then we want to # assign the child xml that we just parsed to the xml_content if obj.respond_to?('xml_content=') n = n.children if n.respond_to?(:children) - obj.xml_content = n.to_xml + obj.xml_content = n.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML) end + # Call any registered after_parse callbacks for the object's class + + obj.class.after_parse_callbacks.each { |callback| callback.call(obj) } + # collect the object that we have created obj end @@ -478,23 +496,27 @@ # that it can be called recursively by classes that are also HappyMapper # classes, allowg for the composition of classes. # # @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. - # @param [String] tag_from_parent the xml tag to use on the element when being + # @param [String] default_namespace The name of the namespace which is the + # default for the xml being produced; this is the namespace of the + # parent + # @param [String] namespace_override The namespace specified with the element + # declaration in the parent. Overrides the namespace declaration in the + # element class itself 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,tag_from_parent = nil) + def to_xml(builder = nil, default_namespace = nil, namespace_override = 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 @@ -537,11 +559,11 @@ # # Attributes that have a nil value should be ignored unless they explicitly # state that they should be expressed in the output. # if not value.nil? || attribute.options[:state_when_nil] - attribute_namespace = attribute.options[:namespace] || default_namespace + attribute_namespace = attribute.options[:namespace] [ "#{attribute_namespace ? "#{attribute_namespace}:" : ""}#{attribute.tag}", value ] else [] end @@ -574,43 +596,48 @@ builder.doc.root.add_namespace(name,href) end end # - # If the object we are persisting has a namespace declaration we will want + # If the object we are serializing has a namespace declaration we will want # to use that namespace or we will use the default namespace. # When neither are specifed we are simply using whatever is default to the # builder # + namespace_for_parent = namespace_override if self.class.respond_to?(:namespace) && self.class.namespace - xml.parent.namespace = builder.doc.root.namespace_definitions.find { |x| x.prefix == self.class.namespace } - elsif default_namespace - xml.parent.namespace = builder.doc.root.namespace_definitions.find { |x| x.prefix == default_namespace } + namespace_for_parent ||= self.class.namespace end + namespace_for_parent ||= default_namespace + xml.parent.namespace = + builder.doc.root.namespace_definitions.find { |x| x.prefix == namespace_for_parent } + # # When a content has been defined we add the resulting value # the output xml # - if content = self.class.instance_variable_get('@content') + if self.class.instance_variable_defined?('@content') + if content = self.class.instance_variable_get('@content') - unless content.options[:read_only] - text_accessor = content.tag || content.name - value = send(text_accessor) + unless content.options[:read_only] + text_accessor = content.tag || content.name + value = send(text_accessor) - if on_save_action = content.options[:on_save] - if on_save_action.is_a?(Proc) - value = on_save_action.call(value) - elsif respond_to?(on_save_action) - value = send(on_save_action,value) + if on_save_action = content.options[:on_save] + if on_save_action.is_a?(Proc) + value = on_save_action.call(value) + elsif respond_to?(on_save_action) + value = send(on_save_action,value) + end end + + builder.text(value) end - builder.text(value) end - end # # for every define element (i.e. has_one, has_many, element) we are # going to persist each one @@ -672,11 +699,13 @@ # # 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],element.options[:tag] || nil) + item.to_xml(xml, self.class.namespace || default_namespace, + element.options[:namespace], + element.options[:tag] || nil) elsif !item.nil? item_namespace = element.options[:namespace] || self.class.namespace || default_namespace @@ -731,10 +760,10 @@ class AnonymousWrapperClassFactory def self.get(name, &blk) Class.new do include HappyMapper tag name - instance_eval &blk + instance_eval(&blk) end end end end