lib/om/xml/dynamic_node.rb in om-1.8.1 vs lib/om/xml/dynamic_node.rb in om-1.9.0.pre1

- old
+ new

@@ -1,12 +1,12 @@ module OM module XML # # Provides a natural syntax for using OM Terminologies to access values from xml Documents # - # *Note*: All of these examples assume that @article is an instance of OM::Samples::ModsArticle. Look at that file to see the Terminology. - # + # *Note*: All of these examples assume that @article is an instance of OM::Samples::ModsArticle. Look at that file to see the Terminology. + # # @example Return an array of the value(s) "start page" node(s) from the second issue node within the first journal node # # Using DynamicNode syntax: # @article.journal(0).issue(1).pages.start # # Other ways to perform this query: # @article.find_by_terms({:journal => 0}, {:issue => 1}, :pages, :start) @@ -28,37 +28,47 @@ # class DynamicNode instance_methods.each { |m| undef_method m unless m.to_s =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/ } attr_accessor :key, :index, :parent, :addressed_node, :term - def initialize(key, index, document, term, parent=nil) # #TODO a real term object in here would make it easier to lookup + def initialize(key, index, document, term, parent=nil) ##TODO a real term object in here would make it easier to lookup self.key = key self.index = index @document = document self.term = term self.parent = parent end - # In practice, method_missing will respond 4 different ways: - # (1) ALL assignment operations are accepted/attempted as new nodes, - # (2) ANY operation with multiple arguments is accepted/attempted as a new node (w/ index), - # (3) With an auto-constructed sub DynamicNode object, - # (4) By handing off to val. This is the only route that will return NoMethodError. - # - # Here we don't have args, so we cannot handle cases 2 and 3. But we can at least do 1 and 4. - def respond_to_missing?(name, include_private = false) - /=$/.match(name.to_s) || val.respond_to?(name, include_private) || super + def method_missing (name, *args, &block) + if /=$/.match(name.to_s) + new_update_node(name, args) + elsif args.length > 1 + new_update_node_with_index(name, args) + else + child = term_child_by_name(term.nil? ? parent.term : term, name) + if child + OM::XML::DynamicNode.new(name, args.first, @document, child, self) + else + val.send(name, *args, &block) + end + end end - def method_missing(name, *args, &block) - return new_update_node(name.to_s.chop.to_sym, nil, args) if /=$/.match(name.to_s) - return new_update_node(name, args.shift, args) if args.length > 1 - child = term_child_by_name(term.nil? ? parent.term : term, name) - return OM::XML::DynamicNode.new(name, args.first, @document, child, self) if child - val.send(name, *args, &block) + def new_update_node(name, args) + modified_name = name.to_s.chop.to_sym + child = term.retrieve_term(modified_name) + node = OM::XML::DynamicNode.new(modified_name, nil, @document, child, self) + node.val=args end + def new_update_node_with_index(name, args) + index = args.shift + child = term.retrieve_term(name) + node = OM::XML::DynamicNode.new(name, index, @document, child, self) + node.val=args + end + def val=(args) @document.ng_xml_will_change! new_values = term.sanitize_new_values(args.first) new_values.keys.sort { |a,b| a.to_i <=> b.to_i }.each do |y| z = new_values[y] @@ -70,27 +80,36 @@ @document.term_value_update(xpath, y.to_i, z) end end end - def val + + def term_child_by_name(term, name) + if (term.kind_of? NamedTermProxy) + @document.class.terminology.retrieve_node(*(term.proxy_pointer.dup << name)) + else + term.retrieve_term(name) + end + end + + def val query = xpath trim_text = !query.index("text()").nil? val = @document.find_by_xpath(query).collect {|node| (trim_text ? node.text.strip : node.text) } term.deserialize(val) end def nodeset query = xpath trim_text = !query.index("text()").nil? - @document.find_by_xpath(query) + return @document.find_by_xpath(query) end def delete nodeset.delete end - + def inspect val.inspect end def ==(other) @@ -105,74 +124,60 @@ if self.index parent.nil? ? [{key => index}] : parent.to_pointer << {key => index} else ### A pointer parent.nil? ? [key] : parent.to_pointer << key end - end + end def xpath if parent.nil? @document.class.terminology.xpath_with_indexes(*(to_pointer << {})) ### last element is always filters else chain = retrieve_addressed_node( ) '//' + chain.map { |n| n.xpath}.join('/') end + end + class AddressedNode attr_accessor :xpath, :key, :pointer def initialize (pointer, xpath, key) self.xpath = xpath self.key = key self.pointer = pointer end end - + ## # This is very similar to Terminology#retrieve_term, however it expands proxy paths out into their cannonical paths def retrieve_addressed_node() chain = [] - chain += parent.retrieve_addressed_node() if parent + + if parent + chain += parent.retrieve_addressed_node() + end if (self.index) ### This is an index node = AddressedNode.new(key, term.xpath_relative, self) node.xpath = OM::XML::TermXpathGenerator.add_node_index_predicate(node.xpath, index) chain << node elsif (term.kind_of? NamedTermProxy) proxy = term.proxy_pointer.dup first = proxy.shift p = @document.class.terminology.retrieve_node(*first) chain << AddressedNode.new(p, p.xpath_relative, self) - until proxy.empty? + while !proxy.empty? first = proxy.shift p = p.retrieve_term(first) chain << AddressedNode.new(p, p.xpath_relative, self) end - else + else chain << AddressedNode.new(key, term.xpath_relative, self) end chain end - private - - # Only to be called by method_missing, hence the NoMethodError. - # We know term.sanitize_new_values would fail in .val= if we pass a nil term. - def new_update_node(name, index, args) - child = term.retrieve_term(name) - raise NoMethodError, "undefined method `#{name}' in OM::XML::DynamicNode for #{self}:#{self.class}" if child.nil? - node = OM::XML::DynamicNode.new(name, index, @document, child, self) - node.val = args - end - - # Only to be called by method_missing - def term_child_by_name(term, name) - if (term.kind_of? NamedTermProxy) - @document.class.terminology.retrieve_node(*(term.proxy_pointer.dup << name)) - else - term.retrieve_term(name) - end - end end end end