Class: OM::XML::DynamicNode

Inherits:
Object
  • Object
show all
Defined in:
lib/om/xml/dynamic_node.rb

Overview

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.

Examples:

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)
@article.ng_xml.xpath("//oxns:relatedItem[@type=\"host\"]/oxns:part[2]/extent[@unit="pages"]", {"oxns"=>"http://www.loc.gov/mods/v3"})

Return an NodeSet of the _first titles_ of all journal nodes

# Using DynamicNode syntax:
@article.journal.title(1)
# Other ways to perform this query:
@article.find_by_terms(:journal, {:title => 1})
@article.ng_xml.xpath("//oxns:relatedItem[@type=\"host\"]/oxns:titleInfo/oxns:title[1]", {"oxns"=>"http://www.loc.gov/mods/v3"})

Find all of the titles from all journals & return the first title Node from that NodeSet

# Using DynamicNode syntax:
@article.journal.title[1]
# Other ways to perform this query:
@article.find_by_terms(:journal, :title)[1]
@article.ng_xml.xpath("//oxns:relatedItem[@type=\"host\"]/oxns:titleInfo/oxns:title", {"oxns"=>"http://www.loc.gov/mods/v3"})[1]

Defined Under Namespace

Classes: AddressedNode

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, index, document, term, parent = nil) ⇒ DynamicNode

TODO a real term object in here would make it easier to lookup



33
34
35
36
37
38
39
# File 'lib/om/xml/dynamic_node.rb', line 33

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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



52
53
54
55
56
57
58
# File 'lib/om/xml/dynamic_node.rb', line 52

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)
end

Instance Attribute Details

#addressed_nodeObject

Returns the value of attribute addressed_node.



32
33
34
# File 'lib/om/xml/dynamic_node.rb', line 32

def addressed_node
  @addressed_node
end

#indexObject

Returns the value of attribute index.



32
33
34
# File 'lib/om/xml/dynamic_node.rb', line 32

def index
  @index
end

#keyObject

Returns the value of attribute key.



32
33
34
# File 'lib/om/xml/dynamic_node.rb', line 32

def key
  @key
end

#parentObject

Returns the value of attribute parent.



32
33
34
# File 'lib/om/xml/dynamic_node.rb', line 32

def parent
  @parent
end

#termObject

Returns the value of attribute term.



32
33
34
# File 'lib/om/xml/dynamic_node.rb', line 32

def term
  @term
end

Instance Method Details

#!=(other) ⇒ Object



112
113
114
# File 'lib/om/xml/dynamic_node.rb', line 112

def !=(other)
  val != other
end

#==(other) ⇒ Object



108
109
110
# File 'lib/om/xml/dynamic_node.rb', line 108

def ==(other)
  other == val
end

#deleteObject



100
101
102
# File 'lib/om/xml/dynamic_node.rb', line 100

def delete
  nodeset.delete
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


116
117
118
# File 'lib/om/xml/dynamic_node.rb', line 116

def eql?(other)
  self == other
end

#inspectObject



104
105
106
# File 'lib/om/xml/dynamic_node.rb', line 104

def inspect
  val.inspect
end

#nodesetObject



94
95
96
97
98
# File 'lib/om/xml/dynamic_node.rb', line 94

def nodeset
  query = xpath
  trim_text = !query.index("text()").nil?
  return @document.find_by_xpath(query)
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

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.

Returns:

  • (Boolean)


48
49
50
# File 'lib/om/xml/dynamic_node.rb', line 48

def respond_to_missing?(name, include_private = false)
  /=$/.match(name.to_s) || val.respond_to?(name, include_private) || super
end

#retrieve_addressed_nodeObject

This is very similar to Terminology#retrieve_term, however it expands proxy paths out into their cannonical paths



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/om/xml/dynamic_node.rb', line 148

def retrieve_addressed_node()
   chain = []
   chain += parent.retrieve_addressed_node() if parent
   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)
      while !proxy.empty?
        first = proxy.shift
        p = p.retrieve_term(first)
        chain << AddressedNode.new(p, p.xpath_relative, self)
      end
   else
     chain << AddressedNode.new(key, term.xpath_relative, self)
   end
   chain
end

#to_pointerObject



120
121
122
123
124
125
126
# File 'lib/om/xml/dynamic_node.rb', line 120

def to_pointer
  if self.index
    parent.nil? ?  [{key => index}] : parent.to_pointer << {key => index}
  else ### A pointer
    parent.nil? ? [key] : parent.to_pointer << key
  end
end

#valArray

This resolves the target of this dynamic node into a reified Array

Returns:

  • (Array)


87
88
89
90
91
92
# File 'lib/om/xml/dynamic_node.rb', line 87

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

#val=(args) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/om/xml/dynamic_node.rb', line 60

def val=(args)
  @document.ng_xml_will_change!
  new_values = term.sanitize_new_values(args.first)
  existing_nodes = @document.find_by_xpath(xpath)
  if existing_nodes.length > new_values.length
    starting_index = new_values.length + 1
    starting_index.upto(existing_nodes.size).each do |index|
      @document.term_value_delete select: xpath, child_index: index
    end
  end
  new_values.each_with_index do |z, y|
## If we pass something that already has an index on it, we should be able to add it.
    if existing_nodes[y.to_i].nil?
      parent_pointer = if parent
        parent.to_pointer
      elsif term.is_a? NamedTermProxy
        term.proxy_pointer[0..-2]
      end
      @document.term_values_append(:parent_select=> parent_pointer,:parent_index=>0,:template=>to_pointer,:values=>z)
    else
      @document.term_value_update(xpath, y.to_i, z)
    end
  end
end

#xpathObject



128
129
130
131
132
133
134
135
# File 'lib/om/xml/dynamic_node.rb', line 128

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