om 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/om/xml/term.rb +4 -1
- data/lib/om/xml/term_value_operators.rb +78 -17
- data/lib/om/xml/term_xpath_generator.rb +4 -1
- data/lib/om/xml/terminology.rb +5 -0
- data/om.gemspec +2 -2
- data/spec/unit/term_value_operators_spec.rb +63 -15
- metadata +4 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.2
|
data/lib/om/xml/term.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
1
|
class OM::XML::Term
|
2
2
|
|
3
3
|
# Term::Builder Class Definition
|
4
|
-
|
4
|
+
#
|
5
|
+
# When coding against Builders, remember that they rely on MethodMissing,
|
6
|
+
# so any time you call a method on the Builder that it doesn't explicitly recognize,
|
7
|
+
# the Builder will add your method & arguments to the it's settings and return itself.
|
5
8
|
class Builder
|
6
9
|
attr_accessor :name, :settings, :children, :terminology_builder
|
7
10
|
|
@@ -34,7 +34,7 @@ module OM::XML::TermValueOperators
|
|
34
34
|
|
35
35
|
params.each_pair do |term_pointer,new_values|
|
36
36
|
pointer = OM.destringify(term_pointer)
|
37
|
-
|
37
|
+
template_pointer = OM.pointers_to_flat_array(pointer,false)
|
38
38
|
hn = OM::XML::Terminology.term_hierarchical_name(*pointer)
|
39
39
|
|
40
40
|
# Sanitize new_values to always be a hash with indexes
|
@@ -78,7 +78,8 @@ module OM::XML::TermValueOperators
|
|
78
78
|
new_values.each do |y,z|
|
79
79
|
if find_by_terms(*pointer)[y.to_i].nil? || y.to_i == -1
|
80
80
|
result[hn].delete(y)
|
81
|
-
term_values_append(:parent_select=>
|
81
|
+
term_values_append(:parent_select=>parent_pointer,:parent_index=>0,:template=>template_pointer,:values=>z)
|
82
|
+
# term_values_append(:parent_select=>parent_xpath,:parent_index=>0,:template=>template_pointer,:values=>z)
|
82
83
|
new_array_index = find_by_terms(*pointer).length - 1
|
83
84
|
result[hn][new_array_index.to_s] = z
|
84
85
|
else
|
@@ -91,10 +92,32 @@ module OM::XML::TermValueOperators
|
|
91
92
|
|
92
93
|
def term_values_append(opts={})
|
93
94
|
parent_select = Array( opts[:parent_select] )
|
94
|
-
|
95
|
+
parent_index = opts[:parent_index]
|
95
96
|
template = opts[:template]
|
96
|
-
new_values = Array( opts[:values] )
|
97
|
+
new_values = Array( opts[:values] )
|
98
|
+
|
99
|
+
parent_nodeset = find_by_terms(*parent_select)
|
100
|
+
parent_node = node_from_set(parent_nodeset, parent_index)
|
101
|
+
|
102
|
+
if parent_node.nil?
|
103
|
+
parent_node, parent_select = build_ancestors(parent_select, parent_index)
|
104
|
+
# parent_nodeset = find_by_terms(*parent_select)
|
105
|
+
# parent_node = node_from_set(parent_nodeset, :last)
|
106
|
+
# raise OM::XML::ParentNodeNotFoundError, "Failed to find a parent node to insert values into based on :parent_select #{parent_select.inspect} with :parent_index #{parent_index.inspect}"
|
107
|
+
end
|
108
|
+
|
109
|
+
insert_from_template(parent_node, new_values, template)
|
110
|
+
|
111
|
+
return parent_node
|
112
|
+
|
113
|
+
end
|
97
114
|
|
115
|
+
# Insert xml containing +new_values+ into +parent_node+. Generate the xml based on +template+
|
116
|
+
# @param [Nokogiri::XML::Node] parent_node to insert new xml into
|
117
|
+
# @param [Array] new_values to build the xml around
|
118
|
+
# @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.
|
119
|
+
# @return [Nokogiri::XML::Node] the parent_node with new chldren inserted into it
|
120
|
+
def insert_from_template(parent_node, new_values, template)
|
98
121
|
# If template is a string, use it as the template, otherwise use it as arguments to xml_builder_template
|
99
122
|
unless template.instance_of?(String)
|
100
123
|
template_args = Array(template)
|
@@ -102,14 +125,8 @@ module OM::XML::TermValueOperators
|
|
102
125
|
template_opts = template_args.delete_at(template_args.length - 1)
|
103
126
|
template_args << template_opts
|
104
127
|
end
|
128
|
+
template_args = OM.pointers_to_flat_array(template_args,false)
|
105
129
|
template = self.class.terminology.xml_builder_template( *template_args )
|
106
|
-
end
|
107
|
-
|
108
|
-
parent_nodeset = find_by_terms(*parent_select)
|
109
|
-
parent_node = node_from_set(parent_nodeset, child_index)
|
110
|
-
|
111
|
-
if parent_node.nil?
|
112
|
-
raise OM::XML::ParentNodeNotFoundError, "Failed to find a parent node to insert values into based on :parent_select #{parent_select.inspect} with :child_index #{child_index.inspect}"
|
113
130
|
end
|
114
131
|
|
115
132
|
builder = Nokogiri::XML::Builder.with(parent_node) do |xml|
|
@@ -118,17 +135,61 @@ module OM::XML::TermValueOperators
|
|
118
135
|
eval(builder_arg)
|
119
136
|
end
|
120
137
|
end
|
121
|
-
|
122
|
-
# Nokogiri::XML::Node.new(builder.to_xml, foo)
|
123
|
-
|
124
138
|
return parent_node
|
125
|
-
|
126
139
|
end
|
127
140
|
|
128
|
-
|
141
|
+
# Creates necesary ancestor nodes to support inserting a new term value where the ancestor node(s) don't exist yet.
|
142
|
+
# Corrects node indexes in the pointer array to correspond to the ancestors that it creates.
|
143
|
+
# Returns a two-value array with the 'parent' node and a corrected pointer array
|
144
|
+
# @return [Nokogiri::XML::Node] the 'parent' (the final node in the ancestor tree)
|
145
|
+
# @return [Array] corrected pointer array for retrieving this parent
|
146
|
+
def build_ancestors(parent_select, parent_index)
|
147
|
+
parent_select = Array(parent_select)
|
148
|
+
parent_nodeset = find_by_terms(*parent_select)
|
149
|
+
starting_point = node_from_set(parent_nodeset, parent_index)
|
150
|
+
if starting_point.nil?
|
151
|
+
starting_point = []
|
152
|
+
end
|
153
|
+
to_build = []
|
154
|
+
until !starting_point.empty?
|
155
|
+
to_build = [parent_select.pop] + to_build
|
156
|
+
starting_point = find_by_terms(*parent_select)
|
157
|
+
end
|
158
|
+
to_build.each do |term_pointer|
|
159
|
+
parent_select << term_pointer
|
160
|
+
|
161
|
+
# If pointers in parent_select don't match with the indexes of built ancestors, correct the hash
|
162
|
+
if find_by_terms(*parent_select+[{}]).length == 0
|
163
|
+
if parent_select.last.kind_of?(Hash)
|
164
|
+
suspect_pointer = parent_select.pop
|
165
|
+
term_key = suspect_pointer.keys.first
|
166
|
+
if parent_select.empty?
|
167
|
+
corrected_term_index = find_by_terms(term_key).length
|
168
|
+
else
|
169
|
+
corrected_term_index = find_by_terms(*parent_select+[{}]).length
|
170
|
+
end
|
171
|
+
parent_select << {term_key => corrected_term_index}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
template_pointer = OM.pointers_to_flat_array(parent_select,false)
|
175
|
+
new_values = [""]
|
176
|
+
insert_from_template(starting_point.first, new_values, template_pointer)
|
177
|
+
starting_point = find_by_terms(*parent_select+[{}])
|
178
|
+
# If pointers in parent_select don't match with the indexes of built ancestors, correct the hash
|
179
|
+
if starting_point.empty?
|
180
|
+
raise StandardError "Oops. Something went wrong adding #{term_pointer} to #{parent_select} while building ancestors"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
if parent_index > starting_point.length
|
184
|
+
parent_index = starting_point.length - 1
|
185
|
+
end
|
186
|
+
return node_from_set(starting_point, parent_index), parent_select
|
187
|
+
end
|
188
|
+
|
189
|
+
def term_value_update(node_select,node_index,new_value,opts={})
|
129
190
|
# template = opts.fetch(:template,nil)
|
130
191
|
|
131
|
-
node = find_by_terms_and_value(*node_select)[
|
192
|
+
node = find_by_terms_and_value(*node_select)[node_index]
|
132
193
|
if new_value == "" || new_value == :delete
|
133
194
|
node.remove
|
134
195
|
else
|
@@ -102,7 +102,10 @@ module OM::XML::TermXpathGenerator
|
|
102
102
|
query_constraints = nil
|
103
103
|
|
104
104
|
if pointers.length > 1 && pointers.last.kind_of?(Hash)
|
105
|
-
|
105
|
+
constraints = pointers.pop
|
106
|
+
unless constraints.empty?
|
107
|
+
query_constraints = constraints
|
108
|
+
end
|
106
109
|
end
|
107
110
|
|
108
111
|
if pointers.length == 1 && pointers.first.instance_of?(String)
|
data/lib/om/xml/terminology.rb
CHANGED
@@ -3,6 +3,11 @@ class OM::XML::Terminology
|
|
3
3
|
class BadPointerError < StandardError; end
|
4
4
|
class CircularReferenceError < StandardError; end
|
5
5
|
|
6
|
+
# Terminology::Builder Class Definition
|
7
|
+
#
|
8
|
+
# When coding against Builders, remember that they rely on MethodMissing,
|
9
|
+
# so any time you call a method on the Builder that it doesn't explicitly recognize,
|
10
|
+
# the Builder will add your method & arguments to the it's settings and return itself.
|
6
11
|
class Builder
|
7
12
|
|
8
13
|
attr_accessor :schema, :namespaces
|
data/om.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{om}
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matt Zumwalt"]
|
12
|
-
s.date = %q{2010-12-
|
12
|
+
s.date = %q{2010-12-15}
|
13
13
|
s.description = %q{OM (Opinionated Metadata): A library to help you tame sprawling XML schemas like MODS. Wraps Nokogiri documents in objects with miscellaneous helper methods for doing things like retrieve generated xpath queries or look up properties based on a simplified DSL}
|
14
14
|
s.email = %q{matt.zumwalt@yourmediashelf.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -45,15 +45,17 @@ describe "OM::XML::TermValueOperators" do
|
|
45
45
|
@article.expects(:term_value_update).with('//oxns:titleInfo/oxns:title', 0, "My New Title")
|
46
46
|
@article.update_values( {[:title_info, :main_title] => "My New Title"} )
|
47
47
|
end
|
48
|
+
|
48
49
|
it "should call term_values_append if the corresponding node does not already exist or if the requested index is -1" do
|
49
50
|
expected_args = {
|
50
|
-
:parent_select => OM::Samples::ModsArticle.terminology.xpath_with_indexes(*[{:person=>0}]) ,
|
51
|
-
:
|
51
|
+
# :parent_select => OM::Samples::ModsArticle.terminology.xpath_with_indexes(*[{:person=>0}]) ,
|
52
|
+
:parent_select => [{:person=>0}],
|
53
|
+
:parent_index => 0,
|
52
54
|
:template => [:person, :role],
|
53
55
|
:values => "My New Role"
|
54
56
|
}
|
55
57
|
@article.expects(:term_values_append).with(expected_args).times(2)
|
56
|
-
@article.update_values( {[{:person=>0}, :role] => {"
|
58
|
+
@article.update_values( {[{:person=>0}, :role] => {"6"=>"My New Role"}} )
|
57
59
|
@article.update_values( {[{:person=>0}, :role] => {"-1"=>"My New Role"}} )
|
58
60
|
end
|
59
61
|
|
@@ -84,6 +86,24 @@ describe "OM::XML::TermValueOperators" do
|
|
84
86
|
@article.term_values( :journal, :issue, :pages, :start).should == ["108"]
|
85
87
|
end
|
86
88
|
|
89
|
+
it "should create the necessary ancestor nodes when necessary" do
|
90
|
+
@sample.find_by_terms(:person).length.should == 4
|
91
|
+
@sample.update_values([{:person=>8}, :role, :text]=>"my role")
|
92
|
+
person_entries = @sample.find_by_terms(:person)
|
93
|
+
person_entries.length.should == 5
|
94
|
+
person_entries[4].search("./ns3:role").first.text.should == "my role"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should create deep trees of ancestor nodes" do
|
98
|
+
result = @article.update_values( {[{:journal=>0}, {:issue=>3}, :pages, :start]=>{"0"=>"434"} })
|
99
|
+
@article.find_by_terms({:journal=>0}, :issue).length.should == 2
|
100
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages).length.should == 1
|
101
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages, :start).length.should == 1
|
102
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages, :start).first.text.should == "434"
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
87
107
|
### Examples copied over form nokogiri_datastream_spec
|
88
108
|
|
89
109
|
it "should apply submitted hash to corresponding datastream field values" do
|
@@ -168,10 +188,10 @@ describe "OM::XML::TermValueOperators" do
|
|
168
188
|
|
169
189
|
describe ".term_values_append" do
|
170
190
|
|
171
|
-
it "looks up the parent using :parent_select, uses :
|
191
|
+
it "looks up the parent using :parent_select, uses :parent_index to choose the parent node from the result set, uses :template to build the node(s) to be inserted, inserts the :values(s) into the node(s) and adds the node(s) to the parent" do
|
172
192
|
@sample.term_values_append(
|
173
193
|
:parent_select => [:person, {:first_name=>"Tim", :last_name=>"Berners-Lee"}] ,
|
174
|
-
:
|
194
|
+
:parent_index => :first,
|
175
195
|
:template => [:person, :affiliation],
|
176
196
|
:values => ["my new value", "another new value"]
|
177
197
|
)
|
@@ -190,7 +210,7 @@ describe "OM::XML::TermValueOperators" do
|
|
190
210
|
|
191
211
|
@sample.term_values_append(
|
192
212
|
:parent_select => [:person, {:first_name=>"Tim", :last_name=>"Berners-Lee"}] ,
|
193
|
-
:
|
213
|
+
:parent_index => :first,
|
194
214
|
:template => [:person, :affiliation],
|
195
215
|
:values => ["my new value", "another new value"]
|
196
216
|
).to_xml.should == expected_result
|
@@ -204,7 +224,7 @@ describe "OM::XML::TermValueOperators" do
|
|
204
224
|
test_val = "language value"
|
205
225
|
@article.term_values_append(
|
206
226
|
:parent_select => :title_info,
|
207
|
-
:
|
227
|
+
:parent_index => :first,
|
208
228
|
:template => [:title_info, :language],
|
209
229
|
:values => test_val
|
210
230
|
)
|
@@ -215,7 +235,7 @@ describe "OM::XML::TermValueOperators" do
|
|
215
235
|
# this appends a role of "my role" into the third "person" node in the document
|
216
236
|
@sample.term_values_append(
|
217
237
|
:parent_select => :person ,
|
218
|
-
:
|
238
|
+
:parent_index => 3,
|
219
239
|
:template => :role,
|
220
240
|
:values => "my role"
|
221
241
|
).to_xml.should #== expected_result
|
@@ -230,7 +250,7 @@ describe "OM::XML::TermValueOperators" do
|
|
230
250
|
|
231
251
|
@sample.term_values_append(
|
232
252
|
:parent_select =>'//oxns:name[@type="personal"]',
|
233
|
-
:
|
253
|
+
:parent_index => 0,
|
234
254
|
:template => 'xml.role { xml.roleTerm( \'#{builder_new_value}\', :type=>\'code\', :authority=>\'marcrelator\') }',
|
235
255
|
:values => "founder"
|
236
256
|
)
|
@@ -246,7 +266,7 @@ describe "OM::XML::TermValueOperators" do
|
|
246
266
|
@sample.ng_xml.xpath('//oxns:name[@type="personal"][2]/oxns:role[1]/oxns:roleTerm', @sample.ox_namespaces).length.should == 2
|
247
267
|
@sample.term_values_append(
|
248
268
|
:parent_select =>'//oxns:name[@type="personal"][2]/oxns:role',
|
249
|
-
:
|
269
|
+
:parent_index => 0,
|
250
270
|
:template => [ :person, :role, :text, {:attributes=>{"authority"=>"marcrelator"}} ],
|
251
271
|
:values => "foo"
|
252
272
|
)
|
@@ -255,8 +275,36 @@ describe "OM::XML::TermValueOperators" do
|
|
255
275
|
@sample.find_by_terms({:person=>1},:role)[0].search("./oxns:roleTerm[@type=\"text\" and @authority=\"marcrelator\"]", @sample.ox_namespaces).first.text.should == "foo"
|
256
276
|
end
|
257
277
|
|
258
|
-
|
259
|
-
|
278
|
+
it "should create the necessary ancestor nodes when you insert a new term value" do
|
279
|
+
@sample.find_by_terms(:person).length.should == 4
|
280
|
+
@sample.term_values_append(
|
281
|
+
:parent_select => :person ,
|
282
|
+
:parent_index => 8,
|
283
|
+
:template => :role,
|
284
|
+
:values => "my role"
|
285
|
+
)
|
286
|
+
person_entries = @sample.find_by_terms(:person)
|
287
|
+
person_entries.length.should == 5
|
288
|
+
person_entries[4].search("./ns3:role").first.text.should == "my role"
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should create the necessary ancestor nodes for deep trees of ancestors" do
|
292
|
+
deep_pointer = [{:journal=>0}, {:issue=>3}, :pages, :start]
|
293
|
+
@article.find_by_terms({:journal=>0}).length.should == 1
|
294
|
+
@article.find_by_terms({:journal=>0}, :issue).length.should == 1
|
295
|
+
@article.term_values_append(
|
296
|
+
:parent_select => deep_pointer[0..deep_pointer.length-2] ,
|
297
|
+
:parent_index => 0,
|
298
|
+
:template => deep_pointer,
|
299
|
+
:values => "451"
|
300
|
+
)
|
301
|
+
@article.find_by_terms({:journal=>0}, :issue).length.should == 2
|
302
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages).length.should == 1
|
303
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages, :start).length.should == 1
|
304
|
+
@article.find_by_terms({:journal=>0}, {:issue=>1}, :pages, :start).first.text.should == "451"
|
305
|
+
|
306
|
+
end
|
307
|
+
|
260
308
|
end
|
261
309
|
|
262
310
|
describe ".term_value_update" do
|
@@ -277,7 +325,7 @@ describe "OM::XML::TermValueOperators" do
|
|
277
325
|
pending
|
278
326
|
@sample.term_value_update(
|
279
327
|
:parent_select =>'//oxns:name[@type="personal"]',
|
280
|
-
:
|
328
|
+
:parent_index => 1,
|
281
329
|
:template => [ :person, :role, {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ],
|
282
330
|
:value => "foo"
|
283
331
|
)
|
@@ -318,7 +366,7 @@ describe "OM::XML::TermValueOperators" do
|
|
318
366
|
# Check that the specific node we want to delete no longer exists
|
319
367
|
@sample.find_by_terms_and_value(specific_xpath).length.should == 0
|
320
368
|
end
|
321
|
-
it "should accept :parent_select, :parent_index and :
|
369
|
+
it "should accept :parent_select, :parent_index and :parent_index options instead of a :select" do
|
322
370
|
|
323
371
|
generic_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role/oxns:roleTerm'
|
324
372
|
specific_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[oxns:roleTerm="visionary"]'
|
@@ -340,7 +388,7 @@ describe "OM::XML::TermValueOperators" do
|
|
340
388
|
# Check that the specific node we want to delete no longer exists
|
341
389
|
@sample.find_by_terms_and_value(specific_xpath).length.should == 1
|
342
390
|
end
|
343
|
-
it "should work if only :parent_select and :
|
391
|
+
it "should work if only :parent_select and :parent_index are provided" do
|
344
392
|
generic_xpath = '//oxns:name[@type="personal"]/oxns:role'
|
345
393
|
# specific_xpath = '//oxns:name[@type="personal"]/oxns:role'
|
346
394
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: om
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 1.0.
|
9
|
+
- 2
|
10
|
+
version: 1.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matt Zumwalt
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-12-
|
18
|
+
date: 2010-12-15 00:00:00 -06:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|