Class: OM::XML::Term

Inherits:
Object
  • Object
show all
Includes:
TreeNode, TermBuilder
Defined in:
lib/om/xml/term.rb

Instance Attribute Summary collapse

Attributes included from TreeNode

#ancestors

Class Method Summary collapse

Instance Method Summary collapse

Methods included from TreeNode

#add_child, #parent, #retrieve_child, #set_parent

Constructor Details

#initialize(name, opts = {}, terminology = nil) ⇒ Term

h2. Namespaces By default, OM assumes you have no namespace defined unless it is explicitly defined at the root of your document.

Parameters:

  • name (Symbol)

    the name to refer to this term by

  • opts (Hash) (defaults to: {})

Options Hash (opts):

  • :index_as (Array)

    a list of indexing hints provided to to_solr

  • :path (String)

    partial xpath that points to the node.

  • :attributes (Hash)

    xml attributes to match in the selector

  • :namespace_prefix (String)

    xml namespace for this node. If not provided, the default namespace set in the terminology will be used.

  • :type (Symbol)

    one of :string, :date, :time :integer. Defaults to :string


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/om/xml/term.rb', line 56

def initialize(name, opts={}, terminology=nil)
  opts = {:ancestors=>[], :children=>{}}.merge(opts)
  [:children, :ancestors,:path, :index_as, :required, :variant_of, :path, :attributes, :default_content_path, :namespace_prefix].each do |accessor_name|
    instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
  end

  self.type = opts[:type] || :string

  unless terminology.nil?
    if opts[:namespace_prefix].nil?
      unless terminology.namespaces["xmlns"].nil?
        @namespace_prefix = "oxns"
      end
    end
  end
  @name = name
  if @path.nil? || @path.empty?
    @path = name.to_s
  end
end

Instance Attribute Details

#attributesObject

Any XML attributes that qualify the Term.

Examples:

Declare a Term that has a given attribute (ie. //title)

t.english_title(:path=>"title", :attributes=>{"xml:lang"=>"eng"}

Use nil to point to nodes that do not have a given attribute (ie. //title)

t.title_without_lang_attribute(:path=>"title", :attributes=>{"xml:lang"=>nil})

22
23
24
# File 'lib/om/xml/term.rb', line 22

def attributes
  @attributes
end

#childrenObject

Returns the value of attribute children


14
15
16
# File 'lib/om/xml/term.rb', line 14

def children
  @children
end

#default_content_pathObject

Returns the value of attribute default_content_path


13
14
15
# File 'lib/om/xml/term.rb', line 13

def default_content_path
  @default_content_path
end

#index_asObject

Always co-erce :index_as attributes into an Array


157
158
159
# File 'lib/om/xml/term.rb', line 157

def index_as
  @index_as
end

#internal_xmlObject

Returns the value of attribute internal_xml


14
15
16
# File 'lib/om/xml/term.rb', line 14

def internal_xml
  @internal_xml
end

#is_root_termObject

Returns the value of attribute is_root_term


13
14
15
# File 'lib/om/xml/term.rb', line 13

def is_root_term
  @is_root_term
end

#nameObject

Returns the value of attribute name


13
14
15
# File 'lib/om/xml/term.rb', line 13

def name
  @name
end

#namespace_prefixObject

Namespace Prefix (xmlns) for the Term.

By default, OM assumes that all terms in a Terminology have the namespace set in the root of the document. If you want to set a different namespace for a Term, pass :namespace_prefix into its initializer (or call .namespace_prefix= on its builder) If a node has no namespace, you must explicitly set namespace_prefix to nil. Currently you have to do this on each term, you can't set namespace_prefix to nil for an entire Terminology.

Examples:

# For xml like this
<foo xmlns="http://foo.com/schemas/fooschema" xmlns:bar="http://bar.com/schemas/barschema">
  <address>1400 Pennsylvania Avenue</address>
  <bar:latitude>56</bar:latitude>
</foo>

# The Terminology would look like this
OM::XML::Terminology::Builder.new do |t|
  t.root(:name=>:foo, :path=>"foo", :xmlns=>"http://foo.com/schemas/fooschema", "xmlns:bar"=>"http://bar.com/schemas/barschema")
  t.address
  t.latitude(:namespace_prefix=>"bar")
end

43
44
45
# File 'lib/om/xml/term.rb', line 43

def namespace_prefix
  @namespace_prefix
end

#pathObject

Returns the value of attribute path


13
14
15
# File 'lib/om/xml/term.rb', line 13

def path
  @path
end

#requiredObject

Returns the value of attribute required


13
14
15
# File 'lib/om/xml/term.rb', line 13

def required
  @required
end

#terminologyObject

Returns the value of attribute terminology


14
15
16
# File 'lib/om/xml/term.rb', line 14

def terminology
  @terminology
end

#typeObject

Returns the value of attribute type


13
14
15
# File 'lib/om/xml/term.rb', line 13

def type
  @type
end

#variant_ofObject

Returns the value of attribute variant_of


13
14
15
# File 'lib/om/xml/term.rb', line 13

def variant_of
  @variant_of
end

#xpathObject

Returns the value of attribute xpath


13
14
15
# File 'lib/om/xml/term.rb', line 13

def xpath
  @xpath
end

#xpath_constrainedObject

Returns the value of attribute xpath_constrained


13
14
15
# File 'lib/om/xml/term.rb', line 13

def xpath_constrained
  @xpath_constrained
end

#xpath_relativeObject

Returns the value of attribute xpath_relative


13
14
15
# File 'lib/om/xml/term.rb', line 13

def xpath_relative
  @xpath_relative
end

Class Method Details

.from_node(mapper_xml) ⇒ Object


132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/om/xml/term.rb', line 132

def self.from_node(mapper_xml)
  name = mapper_xml.attribute("name").text.to_sym
  attributes = {}
  mapper_xml.xpath("./attribute").each do |a|
    attributes[a.attribute("name").text.to_sym] = a.attribute("value").text
  end
  new_mapper = self.new(name, :attributes=>attributes)
  [:index_as, :required, :type, :variant_of, :path, :default_content_path, :namespace_prefix].each do |accessor_name|
    attribute =  mapper_xml.attribute(accessor_name.to_s)
    unless attribute.nil?
      new_mapper.instance_variable_set("@#{accessor_name}", attribute.text )
    end
  end
  new_mapper.internal_xml = mapper_xml

  mapper_xml.xpath("./mapper").each do |child_node|
    child = self.from_node(child_node)
    new_mapper.add_child(child)
  end

  return new_mapper
end

Instance Method Details

#deserialize(val) ⇒ String, ...

Parameters:

  • val (String)

    the value (from xml) to deserialize into the correct object type.

Returns:

  • (String, Date, Integer)

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/om/xml/term.rb', line 114

def deserialize(val)
  case type
  when :date
    #TODO use present?
    val.map { |v| !v.empty? ? Date.parse(v) : nil}
  when :time
    #TODO use present?
    val.map { |v| !v.empty? ? DateTime.parse(v) : nil}
  when :integer
    #TODO use blank?
    val.map { |v| v.empty? ? nil : v.to_i}
  when :boolean
    val.map { |v| v == 'true' }
  else 
    val
  end
end

#generate_xpath_queries!Object

Generates absolute, relative, and constrained xpaths for the term, setting xpath, xpath_relative, and xpath_constrained accordingly. Also triggers update_xpath_values! on all child nodes, as their absolute paths rely on those of their parent nodes.


235
236
237
238
239
240
241
# File 'lib/om/xml/term.rb', line 235

def generate_xpath_queries!
  self.xpath = OM::XML::TermXpathGenerator.generate_absolute_xpath(self)
  self.xpath_constrained = OM::XML::TermXpathGenerator.generate_constrained_xpath(self)
  self.xpath_relative = OM::XML::TermXpathGenerator.generate_relative_xpath(self)
  self.children.each_value {|child| child.generate_xpath_queries! }
  return self
end

#is_root_term?Boolean

Returns:

  • (Boolean)

180
181
182
# File 'lib/om/xml/term.rb', line 180

def is_root_term?
  @is_root_term == true
end

#retrieve_term(*pointers) ⇒ Object

crawl down into mapper's children hash to find the desired mapper ie. @test_mapper.retrieve_mapper(:conference, :role, :text)


163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/om/xml/term.rb', line 163

def retrieve_term(*pointers)
  children_hash = self.children
  pointers.each do |p|
    if children_hash.has_key?(p)
      target = children_hash[p]
      if pointers.index(p) == pointers.length-1
        return target
      else
        children_hash = target.children
      end
    else
      return nil
    end
  end
  return target
end

#sanitize_new_values(new_values) ⇒ Object


78
79
80
81
82
83
84
85
86
87
88
# File 'lib/om/xml/term.rb', line 78

def sanitize_new_values(new_values)
    # Sanitize new_values to always be a hash with indexes
    case new_values
    when Hash
      sanitize_new_values(new_values.values)
    when Array
      new_values.map {|v| serialize(v)}
    else
      [serialize(new_values)]
    end
end

#serialize(val) ⇒ Object

Parameters:

  • val (String, Date, Integer)

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/om/xml/term.rb', line 91

def serialize (val)
  return if val.nil?
  case type
  when :date, :integer
    val.to_s
  when :time
    begin
      time = val.to_time
    rescue ArgumentError
      # In Rails 3, an invalid time raises ArgumentError, in Rails 4 it just returns nil
      raise TypeMismatch, "Can't convert `#{val}` to time"
    end
    raise TypeMismatch, "Can't convert `#{val}` to time" if time.nil?
    time.utc.iso8601
  when :boolean
    val.to_s
  else
    val
  end
end

#to_xml(options = {}, document = Nokogiri::XML::Document.new) ⇒ Nokogiri::XML::Document

Return an XML representation of the Term

Examples:

If :children=>false, skips rendering child Terms

term.to_xml(:children=>false)

You can provide your own Nokogiri document to insert the xml into

doc = Nokogiri::XML::Document.new
term.to_xml({}, document=doc)

Parameters:

  • options (Hash) (defaults to: {})

    the term will be added to it. If :children=>false, skips rendering child Terms

  • document (Nokogiri::XML::Document) (defaults to: Nokogiri::XML::Document.new)

    (optional) document to insert the term xml into

Returns:

  • (Nokogiri::XML::Document)

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/om/xml/term.rb', line 252

def to_xml(options={}, document=Nokogiri::XML::Document.new)
  builder = Nokogiri::XML::Builder.with(document) do |xml|
    xml.term(:name=>name) {
      if is_root_term?
        xml.is_root_term("true")
      end
      xml.path path
      xml.namespace_prefix namespace_prefix
      unless attributes.nil? || attributes.empty?
        xml.attributes {
          attributes.each_pair do |attribute_name, attribute_value|
            xml.send("#{attribute_name}_".to_sym, attribute_value)
          end
        }
      end
      xml.index_as {
        unless index_as.nil?
          index_as.each  { |index_type| xml.index_type }
        end
      }
      xml.required required
      xml.data_type type
      unless variant_of.nil?
        xml.variant_of variant_of
      end
      unless default_content_path.nil?
        xml.default_content_path default_content_path
      end
      xml.xpath {
        xml.relative xpath_relative
        xml.absolute xpath
        xml.constrained xpath_constrained
      }
      if options.fetch(:children, true)
        xml.children
      end
    }
  end
  doc = builder.doc
  if options.fetch(:children, true)
    children.values.each {|child| child.to_xml(options, doc.xpath("//term[@name=\"#{name}\"]/children").first)}
  end
  return doc
end

#xml_builder_template(extra_opts = {}) ⇒ Object

term_pointers reference to the property you want to generate a builder template for

Parameters:

  • extra_opts (Hash) (defaults to: {})

Options Hash (extra_opts):

  • :attributes (Hash)

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/om/xml/term.rb', line 191

def xml_builder_template(extra_opts = {})
  extra_attributes = extra_opts.fetch(:attributes, {})

  node_options = []
  node_child_template = ""
  if !self.default_content_path.nil?
    node_child_options = ["\':::builder_new_value:::\'"]
    node_child_template = " { xml.#{self.default_content_path}( #{OM::XML.delimited_list(node_child_options)} ) }"
  else
    node_options = ["\':::builder_new_value:::\'"]
  end
  if !self.attributes.nil?
    self.attributes.merge(extra_attributes).each_pair do |k,v|
      node_options << "\'#{k}\'=>\'#{v}\'" unless v == :none
    end
  end

  builder_ref = if self.path.include?(":")
    "xml['#{self.path[0..path.index(":")-1]}']"
  elsif !self.namespace_prefix.nil? and self.namespace_prefix != 'oxns'
    "xml['#{self.namespace_prefix}']"
  else
    "xml"
  end

  attribute = OM::XML.delimited_list(node_options)

  builder_method = if self.path.include?(":")
    "#{self.path[path.index(":")+1..-1]}( #{attribute} )"
  elsif self.path.include?(".")
    "send(:\\\"#{self.path}\\\",  #{attribute} )"
  elsif self.path.kind_of?(Hash) && self.path[:attribute]
    "@#{self.path[:attribute]}( #{OM::XML.delimited_list(node_options)} )"
  elsif Nokogiri::XML::Builder.method_defined? self.path.to_sym
    "#{self.path}_( #{OM::XML.delimited_list(node_options)} )"
  else
    "#{self.path}( #{OM::XML.delimited_list(node_options)} )"
  end
  template = "#{builder_ref}.#{builder_method}#{node_child_template}"
  return template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
end

#xpath_absoluteObject


184
185
186
# File 'lib/om/xml/term.rb', line 184

def xpath_absolute
  @xpath
end