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 CHANGED
@@ -1 +1 @@
1
- 1.0.1
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
- template = OM.pointers_to_flat_array(pointer,false)
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=>parent_xpath,:child_index=>0,:template=>template,:values=>z)
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
- child_index = opts[:child_index]
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
- def term_value_update(node_select,child_index,new_value,opts={})
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)[child_index]
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
- query_constraints = pointers.pop
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)
@@ -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.1"
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-06}
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
- :child_index => 0,
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] => {"4"=>"My New 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 :child_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
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
- :child_index => :first,
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
- :child_index => :first,
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
- :child_index => :first,
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
- :child_index => 3,
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
- :child_index => 0,
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
- :child_index => 0,
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
- it "should raise exception if no node corresponds to the provided :parent_select and :child_index"
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
- :child_index => 1,
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 :child_index options instead of a :select" do
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 :child_index are provided" do
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: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
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-06 00:00:00 -06:00
18
+ date: 2010-12-15 00:00:00 -06:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency