om 1.8.0 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +382 -0
- data/.travis.yml +10 -0
- data/Rakefile +1 -1
- data/container_spec.rb +14 -14
- data/gemfiles/gemfile.rails3 +11 -0
- data/gemfiles/gemfile.rails4 +10 -0
- data/lib/om.rb +9 -12
- data/lib/om/samples/mods_article.rb +9 -9
- data/lib/om/tree_node.rb +6 -6
- data/lib/om/version.rb +1 -1
- data/lib/om/xml.rb +18 -20
- data/lib/om/xml/container.rb +12 -12
- data/lib/om/xml/document.rb +3 -7
- data/lib/om/xml/dynamic_node.rb +45 -50
- data/lib/om/xml/named_term_proxy.rb +13 -13
- data/lib/om/xml/node_generator.rb +3 -3
- data/lib/om/xml/template_registry.rb +18 -26
- data/lib/om/xml/term.rb +30 -46
- data/lib/om/xml/term_value_operators.rb +52 -56
- data/lib/om/xml/term_xpath_generator.rb +51 -57
- data/lib/om/xml/terminology.rb +8 -10
- data/lib/om/xml/validation.rb +19 -19
- data/lib/om/xml/vocabulary.rb +4 -4
- data/lib/tasks/om.rake +4 -6
- data/om.gemspec +1 -2
- data/spec/integration/differentiated_elements_spec.rb +2 -2
- data/spec/integration/element_value_spec.rb +13 -13
- data/spec/integration/proxies_and_ref_spec.rb +15 -15
- data/spec/integration/querying_documents_spec.rb +24 -18
- data/spec/integration/rights_metadata_integration_example_spec.rb +18 -18
- data/spec/integration/selective_querying_spec.rb +1 -1
- data/spec/integration/serialization_spec.rb +13 -13
- data/spec/integration/set_reentrant_terminology_spec.rb +7 -7
- data/spec/integration/xpathy_stuff_spec.rb +16 -16
- data/spec/spec_helper.rb +2 -3
- data/spec/unit/container_spec.rb +28 -29
- data/spec/unit/document_spec.rb +49 -50
- data/spec/unit/dynamic_node_spec.rb +55 -47
- data/spec/unit/named_term_proxy_spec.rb +16 -16
- data/spec/unit/node_generator_spec.rb +7 -7
- data/spec/unit/nokogiri_sanity_spec.rb +30 -30
- data/spec/unit/om_spec.rb +5 -5
- data/spec/unit/template_registry_spec.rb +69 -69
- data/spec/unit/term_builder_spec.rb +77 -77
- data/spec/unit/term_spec.rb +78 -72
- data/spec/unit/term_value_operators_spec.rb +186 -191
- data/spec/unit/term_xpath_generator_spec.rb +37 -43
- data/spec/unit/terminology_builder_spec.rb +85 -85
- data/spec/unit/terminology_spec.rb +98 -98
- data/spec/unit/validation_spec.rb +22 -22
- data/spec/unit/xml_serialization_spec.rb +21 -22
- data/spec/unit/xml_spec.rb +7 -7
- metadata +143 -147
@@ -1,9 +1,9 @@
|
|
1
1
|
class OM::XML::NamedTermProxy
|
2
|
-
|
2
|
+
|
3
3
|
attr_accessor :proxy_pointer, :name, :terminology
|
4
|
-
|
4
|
+
|
5
5
|
include OM::TreeNode
|
6
|
-
|
6
|
+
|
7
7
|
# Creates a Named Proxy that points to another term in the Terminology.
|
8
8
|
# Unlike regular terms, NamedTermProxy requires you to provide a reference to the containing Terminology.
|
9
9
|
# This is to ensure that it will always be able to look up the term that it's referencing.
|
@@ -14,13 +14,13 @@ class OM::XML::NamedTermProxy
|
|
14
14
|
def initialize(name, proxy_pointer, terminology, opts={})
|
15
15
|
opts = {:namespace_prefix=>"oxns", :ancestors=>[], :children=>{}}.merge(opts)
|
16
16
|
[:children, :ancestors, :index_as].each do |accessor_name|
|
17
|
-
instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
|
17
|
+
instance_variable_set("@#{accessor_name}", opts.fetch(accessor_name, nil) )
|
18
18
|
end
|
19
19
|
@terminology = terminology
|
20
20
|
@name = name
|
21
21
|
@proxy_pointer = proxy_pointer
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def proxied_term
|
25
25
|
if self.parent.nil?
|
26
26
|
pt = self.terminology.retrieve_term(*self.proxy_pointer)
|
@@ -33,18 +33,18 @@ class OM::XML::NamedTermProxy
|
|
33
33
|
return pt
|
34
34
|
end
|
35
35
|
end
|
36
|
-
|
36
|
+
|
37
37
|
# do nothing -- this is to prevent errors when the parent term calls generate_xpath_queries! on its children
|
38
38
|
def generate_xpath_queries!
|
39
39
|
# do nothing
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
# A proxy term can never serve as the root term of a Terminology.
|
43
43
|
# Explicitly setting is_root_term? to return false to support proxies that are _at_ the root of the Terminology but aren't _the_ root term.
|
44
44
|
def is_root_term?
|
45
|
-
|
45
|
+
false
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
##
|
49
49
|
# Always co-erce :index_as attributes into an Array
|
50
50
|
def index_as
|
@@ -54,10 +54,10 @@ class OM::XML::NamedTermProxy
|
|
54
54
|
self.proxied_term.index_as
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
# Any unknown method calls will be proxied to the proxied term
|
59
|
-
def method_missing method, *args, &block
|
60
|
-
|
59
|
+
def method_missing method, *args, &block
|
60
|
+
self.proxied_term.send(method, *args)
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module OM::XML::NodeGenerator
|
2
|
-
|
2
|
+
|
3
3
|
# Module Methods -- These methods can be called directly on the Module itself
|
4
4
|
# @param OM::XML::Term term The term to generate a node based on
|
5
5
|
# @param String builder_new_value The new value to insert into the generated node
|
@@ -14,8 +14,8 @@ module OM::XML::NodeGenerator
|
|
14
14
|
builder = Nokogiri::XML::Builder.new do |xml|
|
15
15
|
eval( builder_call_body )
|
16
16
|
end
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
builder.doc
|
19
19
|
end
|
20
20
|
|
21
21
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Extend an OM::XML::Document with reusable templates, then use them to add content to
|
1
|
+
# Extend an OM::XML::Document with reusable templates, then use them to add content to
|
2
2
|
# instance documents.
|
3
3
|
#
|
4
4
|
# Example:
|
5
5
|
#
|
6
6
|
# require 'om/samples/mods_article'
|
7
|
-
#
|
7
|
+
#
|
8
8
|
# class OM::Samples::ModsArticle
|
9
9
|
# define_template :personalName do |xml, family, given, address|
|
10
10
|
# xml.name(:type => 'personal') do
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# xml.namePart(:type => 'termsOfAddress') { xml.text(address) }
|
14
14
|
# end
|
15
15
|
# end
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# define_template :role do |xml, text, attrs|
|
18
18
|
# xml.role do
|
19
19
|
# attrs = { :type => 'text' }.merge(attrs)
|
@@ -21,9 +21,9 @@
|
|
21
21
|
# end
|
22
22
|
# end
|
23
23
|
# end
|
24
|
-
#
|
24
|
+
#
|
25
25
|
# mods = OM::Samples::ModsArticle.from_xml(File.read('./spec/fixtures/CBF_MODS/ARS0025_016.xml'))
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# mods.add_previous_sibling_node([:person => 0], :personalName, 'Shmoe', 'Joseph', 'Dr.') { |person|
|
28
28
|
# person.add_child(mods.template(:role, 'author', :authority => 'marcrelator'))
|
29
29
|
# person.add_child(mods.template(:role, 'sub', :authority => 'local', :type => 'code'))
|
@@ -44,7 +44,7 @@ class OM::XML::TemplateRegistry
|
|
44
44
|
unless node_type.is_a?(Symbol)
|
45
45
|
raise TypeError, "Registered node type must be a Symbol (e.g., :person)"
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
@templates[node_type] = block
|
49
49
|
node_type
|
50
50
|
end
|
@@ -77,11 +77,9 @@ class OM::XML::TemplateRegistry
|
|
77
77
|
result = create_detached_node(nil, node_type, *args)
|
78
78
|
# Strip namespaces from text and CDATA nodes. Stupid Nokogiri.
|
79
79
|
result.traverse { |node|
|
80
|
-
if node.is_a?(Nokogiri::XML::CharacterData)
|
81
|
-
node.namespace = nil
|
82
|
-
end
|
80
|
+
node.namespace = nil if node.is_a?(Nokogiri::XML::CharacterData)
|
83
81
|
}
|
84
|
-
|
82
|
+
result
|
85
83
|
end
|
86
84
|
|
87
85
|
# +instantiate+ a node and add it as a child of the [Nokogiri::XML::Node] specified by +target_node+
|
@@ -125,7 +123,7 @@ class OM::XML::TemplateRegistry
|
|
125
123
|
def swap(target_node, node_type, *args, &block)
|
126
124
|
attach_node(:swap, target_node, :parent, node_type, *args, &block)
|
127
125
|
end
|
128
|
-
|
126
|
+
|
129
127
|
def methods
|
130
128
|
super + @templates.keys.collect { |k| k.to_s }
|
131
129
|
end
|
@@ -140,7 +138,7 @@ class OM::XML::TemplateRegistry
|
|
140
138
|
end
|
141
139
|
|
142
140
|
private
|
143
|
-
|
141
|
+
|
144
142
|
# Create a new Nokogiri::XML::Node based on the template for +node_type+
|
145
143
|
#
|
146
144
|
# @param [Nokogiri::XML::Node] builder_node The node to use as starting point for building the node using Nokogiri::XML::Builder.with(builder_node). This provides namespace info, etc for constructing the new Node object. If nil, defaults to {OM::XML::TemplateRegistry#empty_root_node}. This is just used to create the new node and will not be included in the response.
|
@@ -148,13 +146,9 @@ class OM::XML::TemplateRegistry
|
|
148
146
|
# @param [Array] args any additional args
|
149
147
|
def create_detached_node(builder_node, node_type, *args)
|
150
148
|
proc = @templates[node_type]
|
151
|
-
if proc.nil?
|
152
|
-
|
153
|
-
|
154
|
-
if builder_node.nil?
|
155
|
-
builder_node = empty_root_node
|
156
|
-
end
|
157
|
-
|
149
|
+
raise NameError, "Unknown node type: #{node_type.to_s}" if proc.nil?
|
150
|
+
builder_node = empty_root_node if builder_node.nil?
|
151
|
+
|
158
152
|
builder = Nokogiri::XML::Builder.with(builder_node) do |xml|
|
159
153
|
proc.call(xml,*args)
|
160
154
|
end
|
@@ -165,11 +159,11 @@ class OM::XML::TemplateRegistry
|
|
165
159
|
#
|
166
160
|
# @param [Symbol] method name that should be called on +target_node+, usually a Nokogiri::XML::Node instance method
|
167
161
|
# @param [Nokogiri::XML::Node or Nokogiri::XML::NodeSet with only one Node in it] target_node
|
168
|
-
# @param [Symbol] builder_node_offset Indicates node to use as the starting point for _constructing_ the new node using {OM::XML::TemplateRegistry#create_detached_node}. If this is set to :parent, target_node.parent will be used. Otherwise, target_node will be used.
|
162
|
+
# @param [Symbol] builder_node_offset Indicates node to use as the starting point for _constructing_ the new node using {OM::XML::TemplateRegistry#create_detached_node}. If this is set to :parent, target_node.parent will be used. Otherwise, target_node will be used.
|
169
163
|
# @param node_type
|
170
164
|
# @param [Array] args any additional arguments for creating the node
|
171
165
|
def attach_node(method, target_node, builder_node_offset, node_type, *args, &block)
|
172
|
-
if target_node.is_a?(Nokogiri::XML::NodeSet)
|
166
|
+
if target_node.is_a?(Nokogiri::XML::NodeSet) && target_node.length == 1
|
173
167
|
target_node = target_node.first
|
174
168
|
end
|
175
169
|
builder_node = builder_node_offset == :parent ? target_node.parent : target_node
|
@@ -177,9 +171,7 @@ class OM::XML::TemplateRegistry
|
|
177
171
|
result = target_node.send(method, new_node)
|
178
172
|
# Strip namespaces from text and CDATA nodes. Stupid Nokogiri.
|
179
173
|
new_node.traverse { |node|
|
180
|
-
if node.is_a?(Nokogiri::XML::CharacterData)
|
181
|
-
node.namespace = nil
|
182
|
-
end
|
174
|
+
node.namespace = nil if node.is_a?(Nokogiri::XML::CharacterData)
|
183
175
|
}
|
184
176
|
if block_given?
|
185
177
|
yield result
|
@@ -187,9 +179,9 @@ class OM::XML::TemplateRegistry
|
|
187
179
|
return result
|
188
180
|
end
|
189
181
|
end
|
190
|
-
|
182
|
+
|
191
183
|
def empty_root_node
|
192
184
|
Nokogiri::XML('<root/>').root
|
193
185
|
end
|
194
|
-
|
186
|
+
|
195
187
|
end
|
data/lib/om/xml/term.rb
CHANGED
@@ -56,16 +56,14 @@ class OM::XML::Term
|
|
56
56
|
trail = ""
|
57
57
|
nodes_visited.each_with_index do |node, z|
|
58
58
|
trail << node.name.inspect
|
59
|
-
unless z == nodes_visited.length-1
|
60
|
-
trail << " => "
|
61
|
-
end
|
59
|
+
trail << " => " unless z == nodes_visited.length-1
|
62
60
|
end
|
63
61
|
raise OM::XML::Terminology::CircularReferenceError, "Circular reference in Terminology: #{trail}"
|
64
62
|
end
|
65
63
|
result << target
|
66
64
|
result.concat( target.lookup_refs(nodes_visited << self) )
|
67
65
|
end
|
68
|
-
|
66
|
+
result
|
69
67
|
end
|
70
68
|
|
71
69
|
# If a :ref value has been set, looks up the target of that ref and merges the target's settings & children with the current builder's settings & children
|
@@ -81,7 +79,7 @@ class OM::XML::Term
|
|
81
79
|
@settings[:path] = name_of_last_ref.to_s
|
82
80
|
end
|
83
81
|
@settings.delete :ref
|
84
|
-
|
82
|
+
self
|
85
83
|
end
|
86
84
|
|
87
85
|
# Returns a new Hash that merges +downstream_hash+ with +upstream_hash+
|
@@ -96,7 +94,7 @@ class OM::XML::Term
|
|
96
94
|
up.delete(setting_name)
|
97
95
|
end
|
98
96
|
end
|
99
|
-
|
97
|
+
up.merge(dn)
|
100
98
|
end
|
101
99
|
|
102
100
|
# Builds a new OM::XML::Term based on the Builder object's current settings
|
@@ -121,31 +119,29 @@ class OM::XML::Term
|
|
121
119
|
term.generate_xpath_queries!
|
122
120
|
end
|
123
121
|
|
124
|
-
|
122
|
+
term
|
125
123
|
end
|
126
124
|
|
127
125
|
# :data_type accessor has been deprecated in favor of :type
|
128
126
|
# Any value set for :data_type will get set for :type instead
|
129
127
|
def data_type value
|
130
128
|
@settings[:type] = value
|
131
|
-
|
129
|
+
self
|
132
130
|
end
|
133
131
|
deprecation_deprecate :data_type
|
134
132
|
|
135
133
|
# We have to add this method so it will play nice with ruby 1.8.7
|
136
134
|
def type value
|
137
135
|
@settings[:type] = value
|
138
|
-
|
136
|
+
self
|
139
137
|
end
|
140
138
|
|
141
139
|
|
142
140
|
# Any unknown method calls will add an entry to the settings hash and return the current object
|
143
141
|
def method_missing method, *args, &block
|
144
|
-
if args.length == 1
|
145
|
-
args = args.first
|
146
|
-
end
|
142
|
+
args = args.first if args.length == 1
|
147
143
|
@settings[method] = args
|
148
|
-
|
144
|
+
self
|
149
145
|
end
|
150
146
|
end
|
151
147
|
|
@@ -197,9 +193,9 @@ class OM::XML::Term
|
|
197
193
|
# @param name [Symbol] the name to refer to this term by
|
198
194
|
# @param opts [Hash]
|
199
195
|
# @options opts [Array] :index_as a list of indexing hints provided to to_solr
|
200
|
-
# @options opts [String] :path partial xpath that points to the node.
|
201
|
-
# @options opts [Hash] :attributes xml attributes to match in the selector
|
202
|
-
# @options opts [String] :namespace_prefix xml namespace for this node
|
196
|
+
# @options opts [String] :path partial xpath that points to the node.
|
197
|
+
# @options opts [Hash] :attributes xml attributes to match in the selector
|
198
|
+
# @options opts [String] :namespace_prefix xml namespace for this node
|
203
199
|
# @options opts [Symbol] :type one of :string, :date, :integer. Defaults to :string
|
204
200
|
def initialize(name, opts={}, terminology=nil)
|
205
201
|
opts = {:ancestors=>[], :children=>{}}.merge(opts)
|
@@ -211,15 +207,11 @@ class OM::XML::Term
|
|
211
207
|
|
212
208
|
unless terminology.nil?
|
213
209
|
if opts[:namespace_prefix].nil?
|
214
|
-
unless terminology.namespaces["xmlns"].nil?
|
215
|
-
@namespace_prefix = "oxns"
|
216
|
-
end
|
210
|
+
@namespace_prefix = "oxns" unless terminology.namespaces["xmlns"].nil?
|
217
211
|
end
|
218
212
|
end
|
219
213
|
@name = name
|
220
|
-
if @path.nil? || @path.empty?
|
221
|
-
@path = name.to_s
|
222
|
-
end
|
214
|
+
@path = name.to_s if @path.nil? || @path.empty?
|
223
215
|
end
|
224
216
|
|
225
217
|
|
@@ -237,7 +229,7 @@ class OM::XML::Term
|
|
237
229
|
end
|
238
230
|
new_values
|
239
231
|
end
|
240
|
-
|
232
|
+
|
241
233
|
# @param val [String,Date,Integer]
|
242
234
|
def serialize (val)
|
243
235
|
case type
|
@@ -245,7 +237,7 @@ class OM::XML::Term
|
|
245
237
|
val.to_s
|
246
238
|
when :boolean
|
247
239
|
val.to_s
|
248
|
-
else
|
240
|
+
else
|
249
241
|
val
|
250
242
|
end
|
251
243
|
end
|
@@ -255,14 +247,14 @@ class OM::XML::Term
|
|
255
247
|
def deserialize(val)
|
256
248
|
case type
|
257
249
|
when :date
|
258
|
-
#TODO use present?
|
250
|
+
# TODO use present?
|
259
251
|
val.map { |v| !v.empty? ? Date.parse(v) : nil}
|
260
252
|
when :integer
|
261
|
-
#TODO use blank?
|
253
|
+
# TODO use blank?
|
262
254
|
val.map { |v| v.empty? ? nil : v.to_i}
|
263
255
|
when :boolean
|
264
256
|
val.map { |v| v == 'true' }
|
265
|
-
else
|
257
|
+
else
|
266
258
|
val
|
267
259
|
end
|
268
260
|
end
|
@@ -287,7 +279,7 @@ class OM::XML::Term
|
|
287
279
|
new_mapper.add_child(child)
|
288
280
|
end
|
289
281
|
|
290
|
-
|
282
|
+
new_mapper
|
291
283
|
end
|
292
284
|
|
293
285
|
##
|
@@ -312,7 +304,7 @@ class OM::XML::Term
|
|
312
304
|
return nil
|
313
305
|
end
|
314
306
|
end
|
315
|
-
|
307
|
+
target
|
316
308
|
end
|
317
309
|
|
318
310
|
def is_root_term?
|
@@ -336,7 +328,7 @@ class OM::XML::Term
|
|
336
328
|
else
|
337
329
|
node_options = ["\':::builder_new_value:::\'"]
|
338
330
|
end
|
339
|
-
|
331
|
+
unless self.attributes.nil?
|
340
332
|
self.attributes.merge(extra_attributes).each_pair do |k,v|
|
341
333
|
node_options << "\'#{k}\'=>\'#{v}\'" unless v == :none
|
342
334
|
end
|
@@ -346,7 +338,7 @@ class OM::XML::Term
|
|
346
338
|
if builder_method.include?(":")
|
347
339
|
builder_ref = "xml['#{self.path[0..path.index(":")-1]}']"
|
348
340
|
builder_method = self.path[path.index(":")+1..-1]
|
349
|
-
elsif !self.namespace_prefix.nil?
|
341
|
+
elsif !self.namespace_prefix.nil? && self.namespace_prefix != 'oxns'
|
350
342
|
builder_ref = "xml['#{self.namespace_prefix}']"
|
351
343
|
elsif self.path.kind_of?(Hash) && self.path[:attribute]
|
352
344
|
builder_method = "@#{self.path[:attribute]}"
|
@@ -355,7 +347,7 @@ class OM::XML::Term
|
|
355
347
|
builder_method += "_"
|
356
348
|
end
|
357
349
|
template = "#{builder_ref}.#{builder_method}( #{OM::XML.delimited_list(node_options)} )" + node_child_template
|
358
|
-
|
350
|
+
template.gsub( /:::(.*?):::/ ) { '#{'+$1+'}' }
|
359
351
|
end
|
360
352
|
|
361
353
|
# Generates absolute, relative, and constrained xpaths for the term, setting xpath, xpath_relative, and xpath_constrained accordingly.
|
@@ -365,7 +357,7 @@ class OM::XML::Term
|
|
365
357
|
self.xpath_constrained = OM::XML::TermXpathGenerator.generate_constrained_xpath(self)
|
366
358
|
self.xpath_relative = OM::XML::TermXpathGenerator.generate_relative_xpath(self)
|
367
359
|
self.children.each_value {|child| child.generate_xpath_queries! }
|
368
|
-
|
360
|
+
self
|
369
361
|
end
|
370
362
|
|
371
363
|
# Return an XML representation of the Term
|
@@ -380,9 +372,7 @@ class OM::XML::Term
|
|
380
372
|
def to_xml(options={}, document=Nokogiri::XML::Document.new)
|
381
373
|
builder = Nokogiri::XML::Builder.with(document) do |xml|
|
382
374
|
xml.term(:name=>name) {
|
383
|
-
if is_root_term?
|
384
|
-
xml.is_root_term("true")
|
385
|
-
end
|
375
|
+
xml.is_root_term("true") if is_root_term?
|
386
376
|
xml.path path
|
387
377
|
xml.namespace_prefix namespace_prefix
|
388
378
|
unless attributes.nil? || attributes.empty?
|
@@ -393,15 +383,11 @@ class OM::XML::Term
|
|
393
383
|
}
|
394
384
|
end
|
395
385
|
xml.index_as {
|
396
|
-
unless index_as.nil?
|
397
|
-
index_as.each { |index_type| xml.index_type }
|
398
|
-
end
|
386
|
+
index_as.each { |index_type| xml.index_type } unless index_as.nil?
|
399
387
|
}
|
400
388
|
xml.required required
|
401
389
|
xml.data_type type
|
402
|
-
unless variant_of.nil?
|
403
|
-
xml.variant_of variant_of
|
404
|
-
end
|
390
|
+
xml.variant_of variant_of unless variant_of.nil?
|
405
391
|
unless default_content_path.nil?
|
406
392
|
xml.default_content_path default_content_path
|
407
393
|
end
|
@@ -410,16 +396,14 @@ class OM::XML::Term
|
|
410
396
|
xml.absolute xpath
|
411
397
|
xml.constrained xpath_constrained
|
412
398
|
}
|
413
|
-
if options.fetch(:children, true)
|
414
|
-
xml.children
|
415
|
-
end
|
399
|
+
xml.children if options.fetch(:children, true)
|
416
400
|
}
|
417
401
|
end
|
418
402
|
doc = builder.doc
|
419
403
|
if options.fetch(:children, true)
|
420
404
|
children.values.each {|child| child.to_xml(options, doc.xpath("//term[@name=\"#{name}\"]/children").first)}
|
421
405
|
end
|
422
|
-
|
406
|
+
doc
|
423
407
|
end
|
424
408
|
|
425
409
|
# private :update_xpath_values
|
@@ -3,12 +3,12 @@ require "logger"
|
|
3
3
|
|
4
4
|
class OM::XML::ParentNodeNotFoundError < RuntimeError; end
|
5
5
|
module OM::XML::TermValueOperators
|
6
|
-
|
6
|
+
|
7
7
|
# Retrieves all of the nodes from the current document that match +term_pointer+ and returns an array of their values
|
8
8
|
def term_values(*term_pointer)
|
9
9
|
result = []
|
10
10
|
xpath = self.class.terminology.xpath_with_indexes(*term_pointer)
|
11
|
-
#if value is on line by itself sometimes does not trim leading and trailing whitespace for a text node so will detect and fix it
|
11
|
+
# if value is on line by itself sometimes does not trim leading and trailing whitespace for a text node so will detect and fix it
|
12
12
|
trim_text = !xpath.nil? && !xpath.index("text()").nil?
|
13
13
|
find_by_terms(*term_pointer).each {|node| result << (trim_text ? node.text.strip : node.text) }
|
14
14
|
|
@@ -20,61 +20,59 @@ module OM::XML::TermValueOperators
|
|
20
20
|
term.deserialize(result)
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
# alias for term_values
|
25
25
|
def property_values(*lookup_args)
|
26
26
|
term_values(*lookup_args)
|
27
27
|
end
|
28
|
-
|
29
|
-
#
|
28
|
+
|
29
|
+
#
|
30
30
|
# example term values hash: {[{":person"=>"0"}, "role", "text"]=>{"0"=>"role1", "1"=>"role2", "2"=>"role3"}, [{:person=>1}, :family_name]=>"Andronicus", [{"person"=>"1"},:given_name]=>["Titus"],[{:person=>1},:role,:text]=>["otherrole1","otherrole2"] }
|
31
31
|
def update_values(params={})
|
32
|
-
# remove any terms from params that this datastream doesn't recognize
|
33
|
-
|
34
|
-
params.delete_if do |term_pointer,new_values|
|
32
|
+
# remove any terms from params that this datastream doesn't recognize
|
33
|
+
|
34
|
+
params.delete_if do |term_pointer,new_values|
|
35
35
|
if term_pointer.kind_of?(String)
|
36
36
|
true
|
37
37
|
else
|
38
38
|
!self.class.terminology.has_term?(*OM.destringify(term_pointer))
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
result = params.dup
|
43
|
-
|
43
|
+
|
44
44
|
params.each_pair do |term_pointer,new_values|
|
45
45
|
pointer = OM.destringify(term_pointer)
|
46
46
|
template_pointer = OM.pointers_to_flat_array(pointer,false)
|
47
47
|
hn = OM::XML::Terminology.term_hierarchical_name(*pointer)
|
48
|
-
|
48
|
+
|
49
49
|
term = self.class.terminology.retrieve_term( *OM.pointers_to_flat_array(pointer,false) )
|
50
50
|
|
51
51
|
# Sanitize new_values to always be a hash with indexes
|
52
52
|
new_values = term.sanitize_new_values(new_values)
|
53
|
-
|
53
|
+
|
54
54
|
# Populate the response hash appropriately, using hierarchical names for terms as keys rather than the given pointers.
|
55
55
|
result.delete(term_pointer)
|
56
56
|
result[hn] = new_values.dup
|
57
|
-
|
57
|
+
|
58
58
|
# Skip any submitted values if the new value matches the current values
|
59
59
|
current_values = term_values(*pointer)
|
60
60
|
new_values.keys.sort { |a,b| a.to_i <=> b.to_i }.each do |y|
|
61
61
|
z = new_values[y]
|
62
|
-
if current_values[y.to_i]==z
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
|
62
|
+
new_values.delete(y) if current_values[y.to_i]==z && y.to_i > -1
|
63
|
+
end
|
64
|
+
|
67
65
|
# Fill out the pointer completely if the final term is a NamedTermProxy
|
68
66
|
if term.kind_of? OM::XML::NamedTermProxy
|
69
67
|
pointer.pop
|
70
68
|
pointer = pointer.concat(term.proxy_pointer)
|
71
69
|
end
|
72
|
-
|
70
|
+
|
73
71
|
xpath = self.class.terminology.xpath_with_indexes(*pointer)
|
74
72
|
parent_pointer = pointer.dup
|
75
73
|
parent_pointer.pop
|
76
74
|
parent_xpath = self.class.terminology.xpath_with_indexes(*parent_pointer)
|
77
|
-
|
75
|
+
|
78
76
|
# If the value doesn't exist yet, append it. Otherwise, update the existing value.
|
79
77
|
new_values.keys.sort { |a,b| a.to_i <=> b.to_i }.each do |y|
|
80
78
|
z = new_values[y]
|
@@ -89,18 +87,18 @@ module OM::XML::TermValueOperators
|
|
89
87
|
end
|
90
88
|
end
|
91
89
|
end
|
92
|
-
|
90
|
+
result
|
93
91
|
end
|
94
|
-
|
92
|
+
|
95
93
|
def term_values_append(opts={})
|
96
94
|
parent_select = Array( opts[:parent_select] )
|
97
95
|
parent_index = opts[:parent_index]
|
98
96
|
template = opts[:template]
|
99
|
-
new_values = Array( opts[:values] )
|
100
|
-
|
97
|
+
new_values = Array( opts[:values] )
|
98
|
+
|
101
99
|
parent_nodeset = find_by_terms(*parent_select)
|
102
100
|
parent_node = node_from_set(parent_nodeset, parent_index)
|
103
|
-
|
101
|
+
|
104
102
|
if parent_node.nil?
|
105
103
|
if parent_select.empty?
|
106
104
|
parent_node = ng_xml.root
|
@@ -110,12 +108,12 @@ module OM::XML::TermValueOperators
|
|
110
108
|
end
|
111
109
|
|
112
110
|
insert_from_template(parent_node, new_values, template)
|
113
|
-
|
114
|
-
|
115
|
-
|
111
|
+
|
112
|
+
parent_node
|
113
|
+
|
116
114
|
end
|
117
115
|
|
118
|
-
# Insert xml containing +new_values+ into +parent_node+. Generate the xml based on +template+
|
116
|
+
# Insert xml containing +new_values+ into +parent_node+. Generate the xml based on +template+
|
119
117
|
# @param [Nokogiri::XML::Node] parent_node to insert new xml into
|
120
118
|
# @param [Array] new_values to build the xml around
|
121
119
|
# @param [Array -- (OM term pointer array) OR String -- (like what you would pass into Nokogiri::XML::Builder.new)] template for building the new xml. Use the syntax that Nokogiri::XML::Builder uses.
|
@@ -133,23 +131,23 @@ module OM::XML::TermValueOperators
|
|
133
131
|
template = self.class.terminology.xml_builder_template( *template_args )
|
134
132
|
end
|
135
133
|
|
136
|
-
#if there is an xpath element pointing to text() need to change to just 'text' so it references the text method for the parent node
|
134
|
+
# if there is an xpath element pointing to text() need to change to just 'text' so it references the text method for the parent node
|
137
135
|
template.gsub!(/text\(\)/, 'text')
|
138
|
-
|
136
|
+
|
139
137
|
builder = Nokogiri::XML::Builder.with(parent_node) do |xml|
|
140
138
|
new_values.each do |builder_new_value|
|
141
139
|
builder_new_value = builder_new_value.gsub(/'/, "\\\\'") # escape any apostrophes in the new value
|
142
140
|
if matchdata = /xml\.@(\w+)/.match(template)
|
143
141
|
parent_node.set_attribute(matchdata[1], builder_new_value)
|
144
|
-
else
|
142
|
+
else
|
145
143
|
builder_arg = eval('"'+ template + '"') # this inserts builder_new_value into the builder template
|
146
144
|
eval(builder_arg)
|
147
145
|
end
|
148
146
|
end
|
149
147
|
end
|
150
|
-
|
148
|
+
parent_node
|
151
149
|
end
|
152
|
-
|
150
|
+
|
153
151
|
# Creates necesary ancestor nodes to support inserting a new term value where the ancestor node(s) don't exist yet.
|
154
152
|
# Corrects node indexes in the pointer array to correspond to the ancestors that it creates.
|
155
153
|
# Returns a two-value array with the 'parent' node and a corrected pointer array
|
@@ -159,20 +157,18 @@ module OM::XML::TermValueOperators
|
|
159
157
|
parent_select = Array(parent_select)
|
160
158
|
parent_nodeset = find_by_terms(*parent_select)
|
161
159
|
starting_point = node_from_set(parent_nodeset, parent_index)
|
162
|
-
if starting_point.nil?
|
163
|
-
starting_point = []
|
164
|
-
end
|
160
|
+
starting_point = [] if starting_point.nil?
|
165
161
|
to_build = []
|
166
|
-
|
162
|
+
while starting_point.empty?
|
167
163
|
to_build = [parent_select.pop] + to_build
|
168
164
|
starting_point = find_by_terms(*parent_select)
|
169
|
-
if starting_point.empty? && parent_select.empty?
|
165
|
+
if starting_point.empty? && parent_select.empty?
|
170
166
|
raise OM::XML::TemplateMissingException, "Cannot insert nodes into the document because it is empty. Try defining self.xml_template on the #{self.class} class."
|
171
167
|
end
|
172
168
|
end
|
173
|
-
to_build.each do |term_pointer|
|
169
|
+
to_build.each do |term_pointer|
|
174
170
|
parent_select << term_pointer
|
175
|
-
|
171
|
+
|
176
172
|
# If pointers in parent_select don't match with the indexes of built ancestors, correct the hash
|
177
173
|
if find_by_terms(*parent_select+[{}]).length == 0
|
178
174
|
if parent_select.last.kind_of?(Hash)
|
@@ -198,13 +194,13 @@ module OM::XML::TermValueOperators
|
|
198
194
|
if parent_index > starting_point.length
|
199
195
|
parent_index = starting_point.length - 1
|
200
196
|
end
|
201
|
-
|
197
|
+
node_from_set(starting_point, parent_index)
|
202
198
|
end
|
203
|
-
|
199
|
+
|
204
200
|
def term_value_update(node_select,node_index,new_value,opts={})
|
205
201
|
ng_xml_will_change!
|
206
202
|
# template = opts.fetch(:template,nil)
|
207
|
-
|
203
|
+
|
208
204
|
node = find_by_terms_and_value(*node_select)[node_index]
|
209
205
|
if delete_on_update?(node, new_value)
|
210
206
|
node.remove
|
@@ -216,23 +212,23 @@ module OM::XML::TermValueOperators
|
|
216
212
|
def delete_on_update?(node, new_value)
|
217
213
|
new_value == "" || new_value == :delete
|
218
214
|
end
|
219
|
-
|
215
|
+
|
220
216
|
# def term_value_set(term_ref, query_opts, node_index, new_value)
|
221
217
|
# end
|
222
|
-
|
218
|
+
|
223
219
|
def term_value_delete(opts={})
|
224
220
|
ng_xml_will_change!
|
225
221
|
parent_select = Array( opts[:parent_select] )
|
226
222
|
parent_index = opts[:parent_index]
|
227
223
|
child_index = opts[:child_index]
|
228
224
|
xpath_select = opts[:select]
|
229
|
-
|
225
|
+
|
230
226
|
if !xpath_select.nil?
|
231
227
|
node = find_by_terms_and_value(xpath_select).first
|
232
228
|
else
|
233
229
|
# parent_nodeset = find_by_terms_and_value(parent_select, parent_select)
|
234
230
|
parent_nodeset = find_by_terms_and_value(*parent_select)
|
235
|
-
|
231
|
+
|
236
232
|
if parent_index.nil?
|
237
233
|
node = node_from_set(parent_nodeset, child_index)
|
238
234
|
else
|
@@ -241,24 +237,24 @@ module OM::XML::TermValueOperators
|
|
241
237
|
node = node_from_set(parent.xpath("*"), child_index)
|
242
238
|
end
|
243
239
|
end
|
244
|
-
|
240
|
+
|
245
241
|
node.remove
|
246
242
|
end
|
247
|
-
|
248
|
-
|
243
|
+
|
244
|
+
|
249
245
|
# Allows you to provide an array index _or_ a symbol representing the function to call on the nodeset in order to retrieve the node.
|
250
246
|
def node_from_set(nodeset, index)
|
251
247
|
if index.kind_of?(Integer)
|
252
248
|
node = nodeset[index]
|
253
|
-
elsif index.kind_of?(Symbol) && nodeset.respond_to?(index)
|
249
|
+
elsif index.kind_of?(Symbol) && nodeset.respond_to?(index)
|
254
250
|
node = nodeset.send(index)
|
255
251
|
else
|
256
252
|
raise "Could not retrieve node using index #{index}."
|
257
253
|
end
|
258
|
-
|
259
|
-
|
254
|
+
|
255
|
+
node
|
260
256
|
end
|
261
|
-
|
257
|
+
|
262
258
|
private :node_from_set
|
263
|
-
|
259
|
+
|
264
260
|
end
|