om 0.1.10 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,361 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "om"
3
+
4
+ describe "OM::XML::TermValueOperators" do
5
+
6
+ before(:each) do
7
+ @sample = OM::Samples::ModsArticle.from_xml( fixture( File.join("test_dummy_mods.xml") ) )
8
+ @article = OM::Samples::ModsArticle.from_xml( fixture( File.join("mods_articles","hydrangea_article1.xml") ) )
9
+ end
10
+
11
+ describe ".term_values" do
12
+
13
+ it "should return build an array of values from the nodeset corresponding to the given term" do
14
+ expected_values = ["Berners-Lee", "Jobs", "Wozniak", "Klimt"]
15
+ result = @sample.term_values(:person, :last_name)
16
+ result.length.should == expected_values.length
17
+ expected_values.each {|v| result.should include(v)}
18
+ end
19
+
20
+ end
21
+
22
+
23
+ describe ".update_values" do
24
+ it "should update the xml according to the find_by_terms_and_values in the given hash" do
25
+ terms_update_hash = {[{":person"=>"0"}, "affiliation"]=>{"0"=>"affiliation1", "1"=>"affiliation2", "2"=>"affiliation3"}, [{:person=>1}, :last_name]=>"Andronicus", [{"person"=>"1"},:first_name]=>["Titus"],[{:person=>1},:role]=>["otherrole1","otherrole2"] }
26
+ result = @article.update_values(terms_update_hash)
27
+ result.should == {"person_0_affiliation"=>{"0"=>"affiliation1", "1"=>"affiliation2", "2"=>"affiliation3"}, "person_1_last_name"=>{"0"=>"Andronicus"},"person_1_first_name"=>{"0"=>"Titus"}, "person_1_role"=>{"0"=>"otherrole1","1"=>"otherrole2"}}
28
+ person_0_affiliation = @article.find_by_terms({:person=>0}, :affiliation)
29
+ person_0_affiliation[0].text.should == "affiliation1"
30
+ person_0_affiliation[1].text.should == "affiliation2"
31
+ person_0_affiliation[2].text.should == "affiliation3"
32
+
33
+ person_1_last_names = @article.find_by_terms({:person=>1}, :last_name)
34
+ person_1_last_names.length.should == 1
35
+ person_1_last_names.first.text.should == "Andronicus"
36
+
37
+ person_1_first_names = @article.find_by_terms({:person=>1}, :first_name)
38
+ person_1_first_names.first.text.should == "Titus"
39
+
40
+ person_1_roles = @article.find_by_terms({:person=>1}, :role)
41
+ person_1_roles[0].text.should == "otherrole1"
42
+ person_1_roles[1].text.should == "otherrole2"
43
+ end
44
+ it "should call term_value_update if the corresponding node already exists" do
45
+ @article.expects(:term_value_update).with('//oxns:titleInfo/oxns:title', 0, "My New Title")
46
+ @article.update_values( {[:title_info, :main_title] => "My New Title"} )
47
+ end
48
+ it "should call term_values_append if the corresponding node does not already exist or if the requested index is -1" do
49
+ expected_args = {
50
+ :parent_select => OM::Samples::ModsArticle.terminology.xpath_with_indexes(*[{:person=>0}]) ,
51
+ :child_index => 0,
52
+ :template => [:person, :role],
53
+ :values => "My New Role"
54
+ }
55
+ @article.expects(:term_values_append).with(expected_args).times(2)
56
+ @article.update_values( {[{:person=>0}, :role] => {"4"=>"My New Role"}} )
57
+ @article.update_values( {[{:person=>0}, :role] => {"-1"=>"My New Role"}} )
58
+ end
59
+
60
+ it "should support updating attribute values" do
61
+ pointer = [:title_info, :language]
62
+ test_val = "language value"
63
+ @article.update_values( {pointer=>{"0"=>test_val}} )
64
+ @article.term_values(*pointer).first.should == test_val
65
+ end
66
+
67
+ it "should not get tripped up on root nodes" do
68
+ @article.update_values([:title_info]=>{"0"=>"york", "1"=>"mangle","2"=>"mork"})
69
+ @article.term_values(*[:title_info]).should == ["york", "mangle", "mork"]
70
+ end
71
+
72
+ it "should destringify the field key/find_by_terms_and_value pointer" do
73
+ OM::Samples::ModsArticle.terminology.expects(:xpath_with_indexes).with( *[{:person=>0}, :role]).times(7).returns("//oxns:name[@type=\"personal\"][1]/oxns:role")
74
+ OM::Samples::ModsArticle.terminology.stubs(:xpath_with_indexes).with( *[{:person=>0}]).returns("//oxns:name[@type=\"personal\"][1]")
75
+ @article.update_values( { [{":person"=>"0"}, "role"]=>"the role" } )
76
+ @article.update_values( { [{"person"=>"0"}, "role"]=>"the role" } )
77
+ @article.update_values( { [{:person=>0}, :role]=>"the role" } )
78
+ end
79
+
80
+ it "should traverse named term proxies transparently" do
81
+ @article.term_values( :journal, :issue, :start_page).should_not == ["108"]
82
+ @article.update_values( { ["journal", "issue", "start_page"]=>"108" } )
83
+ @article.term_values( :journal, :issue, :start_page).should == ["108"]
84
+ @article.term_values( :journal, :issue, :pages, :start).should == ["108"]
85
+ end
86
+
87
+ ### Examples copied over form nokogiri_datastream_spec
88
+
89
+ it "should apply submitted hash to corresponding datastream field values" do
90
+ result = @article.update_values( {[{":person"=>"0"}, "first_name"]=>{"0"=>"Billy", "1"=>"Bob", "2"=>"Joe"} })
91
+ result.should == {"person_0_first_name"=>{"0"=>"Billy", "1"=>"Bob", "2"=>"Joe"}}
92
+ # xpath = ds.class.xpath_with_indexes(*field_key)
93
+ # result = ds.term_values(xpath)
94
+ @article.term_values({:person=>0}, :first_name).should == ["Billy","Bob","Joe"]
95
+ @article.term_values('//oxns:name[@type="personal"][1]/oxns:namePart[@type="given"]').should == ["Billy","Bob","Joe"]
96
+ end
97
+ it "should support single-value arguments (as opposed to a hash of values with array indexes as keys)" do
98
+ # In other words, { [:journal, :title_info]=>"dork" } should have the same effect as { [:journal, :title_info]=>{"0"=>"dork"} }
99
+ result = @article.update_values( { [{":person"=>"0"}, "role"]=>"the role" } )
100
+ result.should == {"person_0_role"=>{"0"=>"the role"}}
101
+ @article.term_values({:person=>0}, :role).first.should == "the role"
102
+ @article.term_values('//oxns:name[@type="personal"][1]/oxns:role').first.should == "the role"
103
+ end
104
+ it "should do nothing if field key is a string (must be an array or symbol). Will not accept xpath queries!" do
105
+ xml_before = @article.to_xml
106
+ @article.update_values( { "fubar"=>"the role" } ).should == {}
107
+ @article.to_xml.should == xml_before
108
+ end
109
+ it "should do nothing if there is no term corresponding to the given field key" do
110
+ xml_before = @article.to_xml
111
+ @article.update_values( { [{"fubar"=>"0"}]=>"the role" } ).should == {}
112
+ @article.to_xml.should == xml_before
113
+ end
114
+
115
+ it "should work for text fields" do
116
+ att= {[{"person"=>"0"},"description"]=>{"-1"=>"mork", "1"=>"york"}}
117
+ result = @article.update_values(att)
118
+ result.should == {"person_0_description"=>{"0"=>"mork","1"=>"york"}}
119
+ @article.term_values({:person=>0},:description).should == ['mork', 'york']
120
+ att= {[{"person"=>"0"},"description"]=>{"-1"=>"dork"}}
121
+ result2 = @article.update_values(att)
122
+ result2.should == {"person_0_description"=>{"2"=>"dork"}}
123
+ @article.term_values({:person=>0},:description).should == ['mork', 'york', 'dork']
124
+ end
125
+
126
+ it "should return the new index of any added values" do
127
+ @article.term_values({:title_info=>0},:main_title).should == ["ARTICLE TITLE HYDRANGEA ARTICLE 1", "TITLE OF HOST JOURNAL"]
128
+ result = @article.update_values [{"title_info"=>"0"},"main_title"]=>{"-1"=>"mork"}
129
+ result.should == {"title_info_0_main_title"=>{"2"=>"mork"}}
130
+ end
131
+
132
+ it "should return accurate response when multiple values have been added in a single run" do
133
+ pending "THIS SHOULD BE FIXED"
134
+ att= {[:journal, :title_info]=>{"-1"=>"mork", "0"=>"york"}}
135
+ @article.update_values(att).should == {"journal_title_info"=>{"0"=>"york", "1"=>"mork"}}
136
+ @article.term_values(*att.keys.first).should == ["york", "mork"]
137
+ end
138
+
139
+ it "should append nodes at the specified index if possible" do
140
+ @article.update_values([:journal, :title_info]=>["all", "for", "the"])
141
+ att = {[:journal, :title_info]=>{"3"=>'glory'}}
142
+ result = @article.update_values(att)
143
+ result.should == {"journal_title_info"=>{"3"=>"glory"}}
144
+ @article.term_values(:journal, :title_info).should == ["all", "for", "the", "glory"]
145
+ end
146
+
147
+ it "should append values to the end of the array if the specified index is higher than the length of the values array" do
148
+ att = {[:journal, :issue, :pages, :end]=>{"3"=>'108'}}
149
+ @article.term_values(:journal, :issue, :pages, :end).should == []
150
+ result = @article.update_values(att)
151
+ result.should == {"journal_issue_pages_end"=>{"0"=>"108"}}
152
+ @article.term_values(:journal, :issue, :pages, :end).should == ["108"]
153
+ end
154
+
155
+ it "should allow deleting of values and should delete values so that to_xml does not return emtpy nodes" do
156
+ att= {[:journal, :title_info]=>{"0"=>"york", "1"=>"mangle","2"=>"mork"}}
157
+ @article.update_values(att)
158
+ @article.term_values(:journal, :title_info).should == ['york', 'mangle', 'mork']
159
+
160
+ @article.update_values({[:journal, :title_info]=>{"1"=>""}})
161
+ @article.term_values(:journal, :title_info).should == ['york', 'mork']
162
+
163
+ @article.update_values({[:journal, :title_info]=>{"0"=>:delete}})
164
+ @article.term_values(:journal, :title_info).should == ['mork']
165
+ end
166
+
167
+ end
168
+
169
+ describe ".term_values_append" do
170
+
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
172
+ @sample.term_values_append(
173
+ :parent_select => [:person, {:first_name=>"Tim", :last_name=>"Berners-Lee"}] ,
174
+ :child_index => :first,
175
+ :template => [:person, :affiliation],
176
+ :values => ["my new value", "another new value"]
177
+ )
178
+ end
179
+
180
+ it "should accept parent_select and template [term_reference, find_by_terms_and_value_opts] as argument arrays for generators/find_by_terms_and_values" do
181
+ # this appends two affiliation nodes into the first person node whose name is Tim Berners-Lee
182
+ expected_result = '<ns3:name type="personal">
183
+ <ns3:namePart type="family">Berners-Lee</ns3:namePart>
184
+ <ns3:namePart type="given">Tim</ns3:namePart>
185
+ <ns3:role>
186
+ <ns3:roleTerm type="text" authority="marcrelator">creator</ns3:roleTerm>
187
+ <ns3:roleTerm type="code" authority="marcrelator">cre</ns3:roleTerm>
188
+ </ns3:role>
189
+ <ns3:affiliation>my new value</ns3:affiliation><ns3:affiliation>another new value</ns3:affiliation></ns3:name>'
190
+
191
+ @sample.term_values_append(
192
+ :parent_select => [:person, {:first_name=>"Tim", :last_name=>"Berners-Lee"}] ,
193
+ :child_index => :first,
194
+ :template => [:person, :affiliation],
195
+ :values => ["my new value", "another new value"]
196
+ ).to_xml.should == expected_result
197
+
198
+ @sample.find_by_terms(:person, {:first_name=>"Tim", :last_name=>"Berners-Lee"}).first.to_xml.should == expected_result
199
+ end
200
+
201
+ it "should support adding attribute values" do
202
+ pending
203
+ pointer = [:title_info, :language]
204
+ test_val = "language value"
205
+ @article.term_values_append(
206
+ :parent_select => :title_info,
207
+ :child_index => :first,
208
+ :template => [:title_info, :language],
209
+ :values => test_val
210
+ )
211
+ @article.term_values(*pointer).first.should == test_val
212
+ end
213
+
214
+ it "should accept symbols as arguments for generators/find_by_terms_and_values" do
215
+ # this appends a role of "my role" into the third "person" node in the document
216
+ @sample.term_values_append(
217
+ :parent_select => :person ,
218
+ :child_index => 3,
219
+ :template => :role,
220
+ :values => "my role"
221
+ ).to_xml.should #== expected_result
222
+ @sample.find_by_terms(:person)[3].search("./ns3:role[3]").first.text.should == "my role"
223
+ end
224
+
225
+ it "should accept parent_select as an (xpath) string and template as a (template) string" do
226
+ # this uses the provided template to add a node into the first node resulting from the xpath '//oxns:name[@type="personal"]'
227
+ expected_result = "<ns3:name type=\"personal\">\n <ns3:namePart type=\"family\">Berners-Lee</ns3:namePart>\n <ns3:namePart type=\"given\">Tim</ns3:namePart>\n <ns3:role>\n <ns3:roleTerm type=\"text\" authority=\"marcrelator\">creator</ns3:roleTerm>\n <ns3:roleTerm type=\"code\" authority=\"marcrelator\">cre</ns3:roleTerm>\n </ns3:role>\n <ns3:role type=\"code\" authority=\"marcrelator\"><ns3:roleTerm>creator</ns3:roleTerm></ns3:role></ns3:name>"
228
+
229
+ @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role', @sample.ox_namespaces).length.should == 1
230
+
231
+ @sample.term_values_append(
232
+ :parent_select =>'//oxns:name[@type="personal"]',
233
+ :child_index => 0,
234
+ :template => 'xml.role { xml.roleTerm( \'#{builder_new_value}\', :type=>\'code\', :authority=>\'marcrelator\') }',
235
+ :values => "founder"
236
+ )
237
+
238
+ @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role', @sample.ox_namespaces).length.should == 2
239
+ @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=1]/oxns:role[last()]/oxns:roleTerm', @sample.ox_namespaces).first.text.should == "founder"
240
+
241
+ # @sample.find_by_terms_and_value(:person).first.to_xml.should == expected_result
242
+ end
243
+
244
+ it "should support more complex mixing & matching" do
245
+ pending "not working because builder_template is not returning the correct template (returns builder for role instead of roleTerm)"
246
+ @sample.ng_xml.xpath('//oxns:name[@type="personal"][2]/oxns:role[1]/oxns:roleTerm', @sample.ox_namespaces).length.should == 2
247
+ @sample.term_values_append(
248
+ :parent_select =>'//oxns:name[@type="personal"][2]/oxns:role',
249
+ :child_index => 0,
250
+ :template => [ :person, :role, :text, {:attributes=>{"authority"=>"marcrelator"}} ],
251
+ :values => "foo"
252
+ )
253
+
254
+ @sample.ng_xml.xpath('//oxns:name[@type="personal"][2]/oxns:role[1]/oxns:roleTerm', @sample.ox_namespaces).length.should == 3
255
+ @sample.find_by_terms({:person=>1},:role)[0].search("./oxns:roleTerm[@type=\"text\" and @authority=\"marcrelator\"]", @sample.ox_namespaces).first.text.should == "foo"
256
+ end
257
+
258
+ it "should raise exception if no node corresponds to the provided :parent_select and :child_index"
259
+
260
+ end
261
+
262
+ describe ".term_value_update" do
263
+
264
+ it "should accept an xpath as :parent_select" do
265
+ sample_xpath = '//oxns:name[@type="personal"][4]/oxns:role/oxns:roleTerm[@type="text"]'
266
+ @sample.term_value_update(sample_xpath,1,"artist")
267
+ @sample.ng_xml.xpath(sample_xpath, @sample.ox_namespaces)[1].text.should == "artist"
268
+ end
269
+
270
+ it "if :select is provided, should update the first node provided by that xpath statement" do
271
+ sample_xpath = '//oxns:name[@type="personal"][1]/oxns:namePart[@type="given"]'
272
+ @sample.term_value_update(sample_xpath,0,"Timmeh")
273
+ @sample.ng_xml.xpath(sample_xpath, @sample.ox_namespaces).first.text.should == "Timmeh"
274
+ end
275
+
276
+ it "should replace the existing node if you pass a template and values" do
277
+ pending
278
+ @sample.term_value_update(
279
+ :parent_select =>'//oxns:name[@type="personal"]',
280
+ :child_index => 1,
281
+ :template => [ :person, :role, {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ],
282
+ :value => "foo"
283
+ )
284
+ 1.should == 2
285
+ end
286
+ it "should delete nodes if value is :delete or empty string" do
287
+ @article.update_values([:title_info]=>{"0"=>"york", "1"=>"mangle","2"=>"mork"})
288
+ xpath = @article.class.terminology.xpath_for(:title_info)
289
+
290
+ @article.term_value_update([:title_info], 1, "")
291
+ @article.term_values(:title_info).should == ['york', 'mork']
292
+
293
+ @article.term_value_update([:title_info], 1, :delete)
294
+ @article.term_values(:title_info).should == ['york']
295
+ end
296
+ it "should create empty nodes if value is nil" do
297
+ @article.update_values([:title_info]=>{"0"=>"york", "1"=>nil,"2"=>"mork"})
298
+ @article.term_values(:title_info).should == ['york', "", "mork"]
299
+ end
300
+ end
301
+
302
+ describe ".term_value_delete" do
303
+ it "should accept an xpath query as :select option" do
304
+ generic_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role'
305
+ specific_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[oxns:roleTerm="visionary"]'
306
+ select_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[last()]'
307
+
308
+ # Check that we're starting with 2 roles
309
+ # Check that the specific node we want to delete exists
310
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 2
311
+ @sample.find_by_terms_and_value(specific_xpath).length.should == 1
312
+
313
+ @sample.term_value_delete(
314
+ :select =>select_xpath
315
+ )
316
+ # Check that we're finishing with 1 role
317
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 1
318
+ # Check that the specific node we want to delete no longer exists
319
+ @sample.find_by_terms_and_value(specific_xpath).length.should == 0
320
+ end
321
+ it "should accept :parent_select, :parent_index and :child_index options instead of a :select" do
322
+
323
+ generic_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role/oxns:roleTerm'
324
+ specific_xpath = '//oxns:name[@type="personal" and position()=4]/oxns:role[oxns:roleTerm="visionary"]'
325
+
326
+ # Check that we're starting with 2 roles
327
+ # Check that the specific node we want to delete exists
328
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 4
329
+ @sample.find_by_terms_and_value(specific_xpath).length.should == 1
330
+
331
+ # this is attempting to delete the last child (in this case roleTerm) from the 3rd role in the document.
332
+ @sample.term_value_delete(
333
+ :parent_select => [:person, :role],
334
+ :parent_index => 3,
335
+ :child_index => :last
336
+ )
337
+
338
+ # Check that we're finishing with 1 role
339
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 3
340
+ # Check that the specific node we want to delete no longer exists
341
+ @sample.find_by_terms_and_value(specific_xpath).length.should == 1
342
+ end
343
+ it "should work if only :parent_select and :child_index are provided" do
344
+ generic_xpath = '//oxns:name[@type="personal"]/oxns:role'
345
+ # specific_xpath = '//oxns:name[@type="personal"]/oxns:role'
346
+
347
+ # Check that we're starting with 2 roles
348
+ # Check that the specific node we want to delete exists
349
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 4
350
+ # @sample.find_by_terms_and_value(specific_xpath).length.should == 1
351
+
352
+ @sample.term_value_delete(
353
+ :parent_select => [:person, :role],
354
+ :child_index => 3
355
+ )
356
+ # Check that we're finishing with 1 role
357
+ @sample.find_by_terms_and_value(generic_xpath).length.should == 3
358
+ end
359
+ end
360
+
361
+ end
@@ -0,0 +1,111 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "om"
3
+
4
+ describe "OM::XML::TermXpathGeneratorSpec" do
5
+
6
+ before(:all) do
7
+ builder = OM::XML::Terminology::Builder.new do |t|
8
+ t.root(:path=>"mods")
9
+ t.name_ {
10
+ t.family_name(:path=>"namePart", :attributes=>{:type=>"family"})
11
+ t.first_name(:path=>"namePart", :attributes=>{:type=>"given"}, :label=>"first name")
12
+ }
13
+ # lookup :person, :first_name
14
+ t.person(:ref=>:name, :attributes=>{:type=>"personal"})
15
+ end
16
+ @sample_terminology = builder.build
17
+ @rootless_terminology = OM::XML::Terminology.new
18
+ end
19
+
20
+ before(:each) do
21
+ @test_term = OM::XML::Term.new(:terms_of_address, :path=>"namePart", :attributes=>{:type=>"termsOfAddress"})
22
+ @test_term_with_default_path = OM::XML::Term.new(:volume, :path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
23
+ @test_role_text = OM::XML::Term.new(:role_text, :path=>"roleTerm", :attributes=>{:type=>"text"})
24
+ @test_lang_attribute = OM::XML::Term.new(:language, :path=>{:attribute=>"lang"})
25
+ end
26
+
27
+ it "should support terms that are pointers to attribute values" do
28
+ OM::XML::TermXpathGenerator.generate_xpath(@test_lang_attribute, :absolute).should == "//@lang"
29
+ OM::XML::TermXpathGenerator.generate_xpath(@test_lang_attribute, :relative).should == "@lang"
30
+ OM::XML::TermXpathGenerator.generate_xpath(@test_lang_attribute, :constrained).should == '//@lang[contains(., "#{constraint_value}")]'.gsub('"', '\"')
31
+ end
32
+
33
+ describe "generate_xpath" do
34
+ it "should generate an xpath based on the given mapper and options" do
35
+ OM::XML::TermXpathGenerator.expects(:generate_absolute_xpath).with(@test_term)
36
+ OM::XML::TermXpathGenerator.generate_xpath(@test_term, :absolute)
37
+
38
+ OM::XML::TermXpathGenerator.expects(:generate_relative_xpath).with(@test_term)
39
+ OM::XML::TermXpathGenerator.generate_xpath(@test_term, :relative)
40
+
41
+ OM::XML::TermXpathGenerator.expects(:generate_constrained_xpath).with(@test_term)
42
+ OM::XML::TermXpathGenerator.generate_xpath(@test_term, :constrained)
43
+ end
44
+ end
45
+
46
+ describe "generate_relative_xpath" do
47
+ it "should generate a relative xpath based on the given mapper" do
48
+ OM::XML::TermXpathGenerator.generate_relative_xpath(@test_term).should == 'oxns:namePart[@type="termsOfAddress"]'
49
+ end
50
+ it "should support mappers without namespaces" do
51
+ @test_term.namespace_prefix = nil
52
+ OM::XML::TermXpathGenerator.generate_relative_xpath(@test_term).should == 'namePart[@type="termsOfAddress"]'
53
+ end
54
+ end
55
+
56
+ describe "generate_absolute_xpath" do
57
+ it "should generate an absolute xpath based on the given mapper" do
58
+ OM::XML::TermXpathGenerator.generate_absolute_xpath(@test_term).should == '//oxns:namePart[@type="termsOfAddress"]'
59
+ end
60
+ it "should prepend the xpath for any parent nodes" do
61
+ mock_parent_mapper = mock("Term", :xpath_absolute=>'//oxns:name[@type="conference"]/oxns:role')
62
+ @test_role_text.stubs(:parent).returns(mock_parent_mapper)
63
+ OM::XML::TermXpathGenerator.generate_absolute_xpath(@test_role_text).should == '//oxns:name[@type="conference"]/oxns:role/oxns:roleTerm[@type="text"]'
64
+ end
65
+ end
66
+
67
+ describe "generate_constrained_xpath" do
68
+ it "should generate a constrained xpath based on the given mapper" do
69
+ OM::XML::TermXpathGenerator.generate_constrained_xpath(@test_term).should == '//oxns:namePart[@type="termsOfAddress" and contains(., "#{constraint_value}")]'.gsub('"', '\"')
70
+ end
71
+ end
72
+
73
+ it "should support mappers without namespaces" do
74
+ @test_term.namespace_prefix = nil
75
+ OM::XML::TermXpathGenerator.generate_relative_xpath(@test_term).should == 'namePart[@type="termsOfAddress"]'
76
+ OM::XML::TermXpathGenerator.generate_absolute_xpath(@test_term).should == '//namePart[@type="termsOfAddress"]'
77
+ OM::XML::TermXpathGenerator.generate_constrained_xpath(@test_term).should == '//namePart[@type="termsOfAddress" and contains(., "#{constraint_value}")]'.gsub('"', '\"')
78
+ end
79
+
80
+ describe "generate_xpath_with_indexes" do
81
+ it "should accept multiple constraints" do
82
+ generated_xpath = OM::XML::TermXpathGenerator.generate_xpath_with_indexes( @sample_terminology, :person, {:first_name=>"Tim", :family_name=>"Berners-Lee"} )
83
+ # expect an xpath that looks like this: '//oxns:name[@type="personal" and contains(oxns:namePart[@type="family"], "Berners-Lee") and contains(oxns:namePart[@type="given"], "Tim")]'
84
+ # can't use string comparison because the contains functions can arrive in any order
85
+ generated_xpath.should match( /\/\/oxns:name\[@type=\"personal\".*and contains\(oxns:namePart\[@type=\"given\"\], \"Tim\"\).*\]/ )
86
+ generated_xpath.should match( /\/\/oxns:name\[@type=\"personal\".*and contains\(oxns:namePart\[@type=\"family\"\], \"Berners-Lee\"\).*\]/ )
87
+ end
88
+ it "should support xpath queries as argument" do
89
+ OM::XML::TermXpathGenerator.generate_xpath_with_indexes(@sample_terminology, '//oxns:name[@type="personal"][1]/oxns:namePart').should == '//oxns:name[@type="personal"][1]/oxns:namePart'
90
+ end
91
+ it "should return the xpath of the terminology's root node if term pointer is nil" do
92
+ OM::XML::TermXpathGenerator.generate_xpath_with_indexes( @sample_terminology, nil ).should == @sample_terminology.root_terms.first.xpath
93
+ end
94
+ it "should return / if term pointer is nil and the terminology does not have a root term defined" do
95
+ OM::XML::TermXpathGenerator.generate_xpath_with_indexes( @rootless_terminology, nil ).should == "/"
96
+ end
97
+ it "should destringify term pointers before using them" do
98
+ generated_xpath = OM::XML::TermXpathGenerator.generate_xpath_with_indexes( @sample_terminology, {"person"=>"1"}, "first_name" ).should == '//oxns:name[@type="personal"][2]/oxns:namePart[@type="given"]'
99
+ end
100
+ end
101
+
102
+ it "should support mappers with default_content_path" do
103
+ pending "need to implement mapper_set first"
104
+ #@test_term_with_default_path = OM::XML::Term.new(:volume, :path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
105
+
106
+ OM::XML::TermXpathGenerator.generate_relative_xpath(@test_term_with_default_path).should == 'oxns:detail[@type="volume"]'
107
+ OM::XML::TermXpathGenerator.generate_absolute_xpath(@test_term_with_default_path).should == '//oxns:detail[@type="volume"]'
108
+ OM::XML::TermXpathGenerator.generate_constrained_xpath(@test_term_with_default_path).should == '//oxns:detail[contains(oxns:number[@type="volume"], "#{constraint_value}")]'.gsub('"', '\"')
109
+ end
110
+
111
+ end
@@ -0,0 +1,178 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "om"
3
+
4
+ describe "OM::XML::Terminology::Builder" do
5
+
6
+ before(:each) do
7
+ @test_builder = OM::XML::Terminology::Builder.new
8
+
9
+ @builder_with_block = OM::XML::Terminology::Builder.new do |t|
10
+ t.root(:path=>"mods", :xmlns=>"http://www.loc.gov/mods/v3", :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd")
11
+
12
+ t.title_info(:path=>"titleInfo") {
13
+ t.main_title(:path=>"title", :label=>"title")
14
+ t.language(:path=>{:attribute=>"lang"})
15
+ }
16
+ # t.title(:path=>"titleInfo", :default_content_path=>"title") {
17
+ # t.@language(:path=>{:attribute=>"lang"})
18
+ # }
19
+ # This is a mods:name. The underscore is purely to avoid namespace conflicts.
20
+ t.name_ {
21
+ # this is a namepart
22
+ t.namePart(:index_as=>[:searchable, :displayable, :facetable, :sortable], :required=>:true, :type=>:string, :label=>"generic name")
23
+ # affiliations are great
24
+ t.affiliation
25
+ t.displayForm
26
+ t.role(:ref=>[:role])
27
+ t.description
28
+ t.date(:path=>"namePart", :attributes=>{:type=>"date"})
29
+ t.family_name(:path=>"namePart", :attributes=>{:type=>"family"})
30
+ t.given_name(:path=>"namePart", :attributes=>{:type=>"given"}, :label=>"first name")
31
+ t.terms_of_address(:path=>"namePart", :attributes=>{:type=>"termsOfAddress"})
32
+ }
33
+ # lookup :person, :first_name
34
+ t.person(:ref=>:name, :attributes=>{:type=>"personal"})
35
+
36
+ t.role {
37
+ t.text(:path=>"roleTerm",:attributes=>{:type=>"text"})
38
+ t.code(:path=>"roleTerm",:attributes=>{:type=>"code"})
39
+ }
40
+ t.journal(:path=>'relatedItem', :attributes=>{:type=>"host"}) {
41
+ t.title_info
42
+ t.origin_info(:path=>"originInfo")
43
+ t.issn(:path=>"identifier", :attributes=>{:type=>"issn"})
44
+ t.issue(:ref=>[:issue])
45
+ }
46
+ t.issue(:path=>"part") {
47
+ t.volume(:path=>"detail", :attributes=>{:type=>"volume"}, :default_content_path=>"number")
48
+ t.level(:path=>"detail", :attributes=>{:type=>"number"}, :default_content_path=>"number")
49
+ t.pages(:path=>"extent", :attributes=>{:unit=>"pages"}) {
50
+ t.start
51
+ t.end
52
+ }
53
+ # t.start_page(:path=>"pages", :attributes=>{:type=>"start"})
54
+ # t.end_page(:path=>"pages", :attributes=>{:type=>"end"})
55
+ # t.start_page(:path=>"extent", :attributes=>{:unit=>"pages"}, :default_content_path => "start")
56
+ # t.end_page(:path=>"extent", :attributes=>{:unit=>"pages"}, :default_content_path => "end")
57
+ t.publication_date(:path=>"date")
58
+ # t.my_absolute_proxy(:proxy_absolute=>[:name, :role]) # this should always point to [:name, :role]
59
+ t.start_page(:proxy=>[:pages, :start])
60
+ t.start_page(:proxy=>[:pages, :end])
61
+ }
62
+ end
63
+
64
+ # @test_full_terminology = @builder_with_block.build
65
+
66
+ end
67
+
68
+ describe '#new' do
69
+ it "should return an instance of OM::XML::Terminology::Builder" do
70
+ OM::XML::Terminology::Builder.new.should be_instance_of OM::XML::Terminology::Builder
71
+ end
72
+ it "should process the input block, creating a new Term Builder for each entry and its children" do
73
+ expected_root_terms = [:mods, :title_info, :issue, :person, :name, :journal, :role]
74
+ expected_root_terms.each do |name|
75
+ @builder_with_block.term_builders.should have_key(name)
76
+ end
77
+ @builder_with_block.term_builders.length.should == expected_root_terms.length
78
+
79
+ @builder_with_block.term_builders[:journal].should be_instance_of OM::XML::Term::Builder
80
+ @builder_with_block.term_builders[:journal].settings[:path].should == "relatedItem"
81
+ @builder_with_block.term_builders[:journal].settings[:attributes].should == {:type=>"host"}
82
+
83
+ @builder_with_block.term_builders[:journal].children[:issn].should be_instance_of OM::XML::Term::Builder
84
+ @builder_with_block.term_builders[:journal].children[:issn].settings[:path].should == "identifier"
85
+ @builder_with_block.term_builders[:journal].children[:issn].settings[:attributes].should == {:type=>"issn"}
86
+ end
87
+ it "should clip the underscore off the end of any Term names" do
88
+ @builder_with_block.term_builders[:name].should be_instance_of OM::XML::Term::Builder
89
+ @builder_with_block.term_builders[:name].name.should == :name
90
+
91
+ @builder_with_block.term_builders[:name].children[:date].should be_instance_of OM::XML::Term::Builder
92
+ @builder_with_block.term_builders[:name].children[:date].settings[:path].should == "namePart"
93
+ @builder_with_block.term_builders[:name].children[:date].settings[:attributes].should == {:type=>"date"}
94
+ end
95
+ end
96
+
97
+ describe '#from_xml' do
98
+ it "should let you load mappings from an xml file" do
99
+ pending
100
+ vocab = OM::XML::Terminology.from_xml( fixture("sample_mappings.xml") )
101
+ vocab.should be_instance_of OM::XML::Terminology
102
+ vocab.mappers.should == {}
103
+ end
104
+ end
105
+
106
+ describe ".retrieve_term_builder" do
107
+ it "should support looking up Term Builders by pointer" do
108
+ expected = @builder_with_block.term_builders[:name].children[:date]
109
+ @builder_with_block.retrieve_term_builder(:name, :date).should == expected
110
+ end
111
+ end
112
+
113
+ describe "build" do
114
+ it "should generate the new Terminology, calling .build on its Term builders"
115
+ it "should resolve :refs" do
116
+ @builder_with_block.retrieve_term_builder(:name, :role).settings[:ref].should == [:role]
117
+ @builder_with_block.retrieve_term_builder(:role).children[:text].should be_instance_of OM::XML::Term::Builder
118
+
119
+ built_terminology = @builder_with_block.build
120
+
121
+ built_terminology.retrieve_term(:name, :role, :text).should be_instance_of OM::XML::Term
122
+ built_terminology.retrieve_term(:name, :role, :text).path.should == "roleTerm"
123
+ built_terminology.retrieve_term(:name, :role, :text).attributes.should == {:type=>"text"}
124
+ end
125
+ it "should put copies of the entire terminology under any root terms" do
126
+ @builder_with_block.root_term_builders.should include(@builder_with_block.retrieve_term_builder(:mods))
127
+
128
+ built_terminology = @builder_with_block.build
129
+ expected_keys = [:title_info, :issue, :person, :name, :journal, :role]
130
+
131
+ built_terminology.retrieve_term(:mods).children.length.should == expected_keys.length
132
+ expected_keys.each do |key|
133
+ built_terminology.retrieve_term(:mods).children.keys.should include(key)
134
+ end
135
+ built_terminology.retrieve_term(:mods, :name, :role, :text).should be_instance_of OM::XML::Term
136
+ built_terminology.retrieve_term(:mods, :person, :role, :text).should be_instance_of OM::XML::Term
137
+
138
+ end
139
+ end
140
+
141
+ describe '.insert_term' do
142
+ it "should create a new OM::XML::Term::Builder and insert it into the class mappings hash" do
143
+ pending
144
+
145
+ result = @test_builder.insert_mapper(:name_, :namePart).index_as([:facetable, :searchable, :sortable, :displayable]).required(true).type(:text)
146
+ @test_builder.mapper_builders(:name_, :namePart).should == result
147
+ result.should be_instance_of OM::XML::Mapper::Builder
148
+ end
149
+ end
150
+
151
+ describe ".root" do
152
+ it "should accept options for the root node, such as namespace(s) and schema and those values should impact the resulting Terminology" do
153
+ root_term_builder = @test_builder.root(:path=>"mods", :xmlns => 'one:two', 'xmlns:foo' => 'bar', :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd")
154
+ root_term_builder.settings[:is_root_term].should == true
155
+
156
+ @test_builder.schema.should == "http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"
157
+ @test_builder.namespaces.should == { "oxns"=>"one:two", 'xmlns' => 'one:two', 'xmlns:foo' => 'bar' }
158
+ @test_builder.term_builders[:mods].should == root_term_builder
159
+
160
+ terminology = @test_builder.build
161
+ terminology.schema.should == "http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"
162
+ terminology.namespaces.should == { "oxns"=>"one:two", 'xmlns' => 'one:two', 'xmlns:foo' => 'bar' }
163
+ terminology.retrieve_term(:mods).should be_instance_of OM::XML::Term
164
+ terminology.retrieve_term(:mods).is_root_term?.should == true
165
+ end
166
+ it "should work within a builder block" do
167
+ @builder_with_block.term_builders[:mods].settings[:is_root_term].should == true
168
+ end
169
+ end
170
+
171
+ describe ".root_term_builders" do
172
+ it "should return the terms that have been marked root" do
173
+ @builder_with_block.root_term_builders.length.should == 1
174
+ @builder_with_block.root_term_builders.first.should == @builder_with_block.term_builders[:mods]
175
+ end
176
+ end
177
+
178
+ end