om 1.6.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/COMMON_OM_PATTERNS.textile +13 -1
  2. data/Gemfile +7 -4
  3. data/Gemfile.lock +19 -14
  4. data/History.textile +4 -0
  5. data/lib/om/version.rb +1 -1
  6. data/lib/om/xml/document.rb +16 -2
  7. data/lib/om/xml/terminology.rb +5 -0
  8. data/lib/tasks/om.rake +16 -10
  9. data/om.gemspec +1 -0
  10. data/spec/fixtures/mods_articles/hydrangea_article1.xml +7 -0
  11. data/spec/integration/differentiated_elements_spec.rb +39 -0
  12. data/spec/integration/element_value_spec.rb +91 -0
  13. data/spec/integration/proxies_and_ref_spec.rb +81 -0
  14. data/spec/integration/querying_documents_spec.rb +73 -0
  15. data/spec/integration/rights_metadata_integration_example_spec.rb +2 -4
  16. data/spec/integration/selective_querying_spec.rb +89 -0
  17. data/spec/integration/set_reentrant_terminology_spec.rb +134 -0
  18. data/spec/integration/xpathy_stuff_spec.rb +204 -0
  19. data/spec/spec_helper.rb +8 -0
  20. data/spec/unit/container_spec.rb +2 -4
  21. data/spec/unit/document_spec.rb +2 -3
  22. data/spec/unit/dynamic_node_spec.rb +1 -2
  23. data/spec/unit/named_term_proxy_spec.rb +1 -2
  24. data/spec/unit/node_generator_spec.rb +2 -3
  25. data/spec/unit/nokogiri_sanity_spec.rb +2 -3
  26. data/spec/unit/om_spec.rb +2 -4
  27. data/spec/unit/template_registry_spec.rb +1 -2
  28. data/spec/unit/term_builder_spec.rb +2 -3
  29. data/spec/unit/term_spec.rb +1 -2
  30. data/spec/unit/term_value_operators_spec.rb +2 -3
  31. data/spec/unit/term_xpath_generator_spec.rb +1 -2
  32. data/spec/unit/terminology_builder_spec.rb +1 -2
  33. data/spec/unit/terminology_spec.rb +1 -2
  34. data/spec/unit/validation_spec.rb +2 -4
  35. data/spec/unit/xml_serialization_spec.rb +2 -4
  36. data/spec/unit/xml_spec.rb +2 -4
  37. metadata +68 -13
@@ -347,4 +347,16 @@ oxns
347
347
  document namespaces & node namespaces
348
348
  _no namespace_ (suppressing oxns in xpath queries)
349
349
 
350
- h3. :ref and :proxy Terms
350
+ h3. :ref and :proxy Terms
351
+
352
+ If needed (as a differentiator) you can use the root element as a member of the proxy address:
353
+ <pre>
354
+ t.root(:path=>"mods")
355
+ t.titleInfo {
356
+ t.title
357
+ }
358
+ This produces a relative xpath: (e.g. //titleInfo/title)
359
+ t.title(:proxy=>[:titleInfo, :title])
360
+ This produces an absolute query (e.g. /mods/titleInfo/title)
361
+ t.title(:proxy=>[:mods, :titleInfo, :title])
362
+ </pre>
data/Gemfile CHANGED
@@ -2,11 +2,14 @@ source "http://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'ruby-debug', :platform => :ruby_18
6
- gem 'ruby-debug19', :platform => :ruby_19
7
-
8
5
  group :development, :test do
9
- gem "rcov"
6
+ gem 'ruby-debug', :platform => :ruby_18
7
+ gem "debugger", :platform => :ruby_19
8
+
9
+ gem 'rcov', :platform => :mri_18
10
+ gem 'simplecov', :platform => :mri_19
11
+ gem 'simplecov-rcov', :platform => :mri_19
12
+
10
13
  gem "yard"
11
14
  gem "RedCloth", "~> 4.2.9"
12
15
  end
@@ -9,20 +9,26 @@ GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
11
  RedCloth (4.2.9)
12
- archive-tar-minitar (0.5.2)
13
12
  columnize (0.3.5)
13
+ debugger (1.1.3)
14
+ columnize (>= 0.3.1)
15
+ debugger-linecache (~> 1.1.1)
16
+ debugger-ruby_core_source (~> 1.1.2)
17
+ debugger-linecache (1.1.1)
18
+ debugger-ruby_core_source (>= 1.1.1)
19
+ debugger-ruby_core_source (1.1.3)
14
20
  diff-lcs (1.1.3)
15
21
  equivalent-xml (0.2.8)
16
22
  nokogiri (>= 1.4.3)
17
23
  linecache (0.46)
18
24
  rbx-require-relative (> 0.0.4)
19
- linecache19 (0.5.12)
20
- ruby_core_source (>= 0.1.4)
21
25
  mediashelf-loggable (0.4.9)
22
26
  metaclass (0.0.1)
23
27
  mocha (0.10.0)
24
28
  metaclass (~> 0.0.1)
29
+ multi_json (1.3.6)
25
30
  nokogiri (1.5.0)
31
+ rake (0.9.2.2)
26
32
  rbx-require-relative (0.0.5)
27
33
  rcov (0.9.11)
28
34
  rspec (2.7.0)
@@ -38,16 +44,12 @@ GEM
38
44
  ruby-debug-base (~> 0.10.4.0)
39
45
  ruby-debug-base (0.10.4)
40
46
  linecache (>= 0.3)
41
- ruby-debug-base19 (0.11.25)
42
- columnize (>= 0.3.1)
43
- linecache19 (>= 0.5.11)
44
- ruby_core_source (>= 0.1.4)
45
- ruby-debug19 (0.11.6)
46
- columnize (>= 0.3.1)
47
- linecache19 (>= 0.5.11)
48
- ruby-debug-base19 (>= 0.11.19)
49
- ruby_core_source (0.1.5)
50
- archive-tar-minitar (>= 0.5.2)
47
+ simplecov (0.6.4)
48
+ multi_json (~> 1.0)
49
+ simplecov-html (~> 0.5.3)
50
+ simplecov-html (0.5.3)
51
+ simplecov-rcov (0.2.3)
52
+ simplecov (>= 0.4.1)
51
53
  yard (0.7.4)
52
54
 
53
55
  PLATFORMS
@@ -55,11 +57,14 @@ PLATFORMS
55
57
 
56
58
  DEPENDENCIES
57
59
  RedCloth (~> 4.2.9)
60
+ debugger
58
61
  equivalent-xml (>= 0.2.4)
59
62
  mocha (>= 0.9.8)
60
63
  om!
64
+ rake
61
65
  rcov
62
66
  rspec (~> 2.0)
63
67
  ruby-debug
64
- ruby-debug19
68
+ simplecov
69
+ simplecov-rcov
65
70
  yard
@@ -1,3 +1,7 @@
1
+ h3. 1.6.1
2
+ Integration spec to illustrate selective querying.
3
+ Add #use_terminology and #extend_terminology methods to extend existing OM terminologies
4
+
1
5
  h3. 1.6.0
2
6
  Delegate all methods on the dynamic node to the found values
3
7
  Allow arrays to be set on dynamic nodes
@@ -1,3 +1,3 @@
1
1
  module Om
2
- VERSION = "1.6.0"
2
+ VERSION = "1.6.1"
3
3
  end
@@ -5,12 +5,26 @@ module OM::XML::Document
5
5
 
6
6
  module ClassMethods
7
7
 
8
- attr_accessor :terminology, :template_registry
8
+ attr_accessor :terminology, :terminology_builder, :template_registry
9
9
 
10
10
  # Sets the OM::XML::Terminology for the Document
11
11
  # Expects +&block+ that will be passed into OM::XML::Terminology::Builder.new
12
12
  def set_terminology &block
13
- @terminology = OM::XML::Terminology::Builder.new( &block ).build
13
+ @terminology_builder = OM::XML::Terminology::Builder.new( &block )
14
+
15
+ @terminology = @terminology_builder.build
16
+ end
17
+
18
+ # Update the OM::XML::Terminology with additional terms
19
+ def extend_terminology &block
20
+ @terminology_builder.extend_terminology(&block)
21
+ @terminology = @terminology_builder.build
22
+ end
23
+
24
+ # (Explicitly) inherit terminology from upstream classes
25
+ def use_terminology klass
26
+ @terminology_builder = klass.terminology_builder.dup
27
+ @terminology = @terminology_builder.build
14
28
  end
15
29
 
16
30
  # Define a new node template with the OM::XML::TemplateRegistry.
@@ -102,6 +102,11 @@ class OM::XML::Terminology
102
102
  return current_term
103
103
  end
104
104
 
105
+ # Add additional terms into this terminology
106
+ def extend_terminology &block
107
+ yield self if block_given?
108
+ end
109
+
105
110
  def build
106
111
  terminology = OM::XML::Terminology.new(:schema=>@schema, :namespaces=>@namespaces)
107
112
  root_term_builders.each do |root_term_builder|
@@ -1,22 +1,28 @@
1
1
  desc "Task to execute builds on a Hudson Continuous Integration Server."
2
2
  task :hudson do
3
3
  Rake::Task["om:doc"].invoke
4
- Rake::Task["om:rcov"].invoke
5
- Rake::Task["om:rspec"].invoke
4
+ Rake::Task["coverage"].invoke
5
+ end
6
+
7
+
8
+ desc "Execute specs with coverage"
9
+ task :coverage do
10
+ # Put spec opts in a file named .rspec in root
11
+ ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
12
+ ENV['COVERAGE'] = 'true' unless ruby_engine == 'jruby'
13
+
14
+
15
+ Rake::Task['om:rspec'].invoke
6
16
  end
7
17
 
8
18
  namespace :om do
9
19
 
10
20
  require 'rspec/core/rake_task'
11
21
  RSpec::Core::RakeTask.new(:rspec) do |spec|
12
- spec.pattern = FileList['spec/**/*_spec.rb']
13
- spec.pattern += FileList['spec/*_spec.rb']
14
- end
15
-
16
- RSpec::Core::RakeTask.new(:rcov) do |spec|
17
- spec.pattern = FileList['spec/**/*_spec.rb']
18
- spec.pattern += FileList['spec/*_spec.rb']
19
- spec.rcov = true
22
+ if ENV['COVERAGE'] and RUBY_VERSION =~ /^1.8/
23
+ spec.rcov = true
24
+ spec.rcov_opts = %w{-I../../app -I../../lib --exclude spec\/*,gems\/*,ruby\/* --aggregate coverage.data}
25
+ end
20
26
  end
21
27
 
22
28
  # Use yard to build docs
data/om.gemspec CHANGED
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.add_dependency('nokogiri', ">= 1.4.2")
16
16
  s.add_dependency('mediashelf-loggable')
17
17
  s.add_development_dependency "rspec", "~> 2.0"
18
+ s.add_development_dependency "rake"
18
19
  s.add_development_dependency "mocha", ">= 0.9.8"
19
20
  s.add_development_dependency "equivalent-xml", ">= 0.2.4"
20
21
 
@@ -37,6 +37,13 @@
37
37
  </role>
38
38
  </name>
39
39
 
40
+ <name type="corporate">
41
+ <namePart>NSF</namePart>
42
+ <role>
43
+ <roleTerm authority="marcrelator" type="text">funder</roleTerm>
44
+ </role>
45
+ </name>
46
+
40
47
  <typeOfResource>text</typeOfResource>
41
48
  <genre authority="local">journal article</genre>
42
49
 
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe "use the root element as a member of the proxy address" do
4
+ before(:all) do
5
+ class BlahTerminology
6
+ include OM::XML::Document
7
+
8
+ set_terminology do |t|
9
+ t.root :path => 'root_element', :xmlns => "asdf"
10
+ t.relevant_container do
11
+ t.the_thing_we_want
12
+ end
13
+
14
+ t.the_thing_we_want :proxy => [:root_element, :relevant_container, :the_thing_we_want]
15
+ end
16
+ end
17
+ end
18
+
19
+ subject do
20
+ BlahTerminology.from_xml('<root_element xmlns="asdf">
21
+ <arbitrary_container_element>
22
+ <relevant_container>
23
+ <the_thing_we_want but="not really">1</the_thing_we_want>
24
+ </relevant_container>
25
+ </arbitrary_container_element>
26
+ <relevant_container>
27
+ <the_thing_we_want>2</the_thing_we_want>
28
+ </relevant_container>
29
+ </root_element>')
30
+ end
31
+
32
+ it "should pull out all occurences of the_thing_we_want in the relevant_container" do
33
+ subject.relevant_container.the_thing_we_want.should == ["1", "2"]
34
+ end
35
+
36
+ it "should only pull out the_thing_we_want at the root level" do
37
+ subject.the_thing_we_want.should == ["2"]
38
+ end
39
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe "element values" do
4
+ before(:all) do
5
+ class ElementValueTerminology
6
+ include OM::XML::Document
7
+
8
+ set_terminology do |t|
9
+ t.root(:path => "outer", :xmlns => nil)
10
+ t.elementA
11
+ t.elB(:path => "elementB")
12
+ t.elC(:path => "elementC")
13
+
14
+ t.elementC(:attributes=>{:animal=>"seagull"}, :namespace_prefix => nil)
15
+ t.here(:path=>"resource", :attributes=>{:type=>"ead"}, :namespace_prefix => nil)
16
+ t.there(:path=>"resource", :attributes=>{:type=>"nowhere"}, :namespace_prefix => nil)
17
+ t.elementD(:attributes=>{:animal=>:none}, :namespace_prefix => nil)
18
+ t.no_attrib(:path => "elementB", :attributes=>{:animal=>:none}, :namespace_prefix => nil)
19
+
20
+ t.elementB {
21
+ t.my_attr(:path => {:attribute=>"animal"}, :namespace_prefix => nil)
22
+ }
23
+ t.alternate(:path => "elementB/@animal", :namespace_prefix => nil)
24
+ t.another(:proxy=>[:elementB, :my_attr])
25
+ t.animal_attrib(:path => {:attribute=>"animal"}, :namespace_prefix => nil)
26
+ end
27
+ end
28
+ end
29
+
30
+ subject do
31
+ ElementValueTerminology.from_xml <<-EOF
32
+ <outer outerId="hypatia:outer" type="outer type">
33
+ <elementA>valA</elementA>
34
+ <elementB>valB1</elementB>
35
+ <elementB animal="vole">valB2</elementB>
36
+ <elementC type="c type" animal="seagull">valC</elementC>
37
+ <elementD >valD1</elementC>
38
+ <elementD animal="seagull">valD2</elementC>
39
+ <resource type="ead" id="coll.ead" objectId="hypatia:ead_file_asset_fixture">
40
+ <file id="my_ead.xml" format="XML" mimetype="text/xml" size="47570">
41
+ <checksum type="md5">123</checksum>
42
+ <checksum type="sha1">456</checksum>
43
+ </file>
44
+ </resource>
45
+ <resource type="image" id="image" objectId="hypatia:coll_img_file_asset_fixture">
46
+ <file id="my_image.jpg" format="JPG" mimetype="image/jpeg" size="302080">
47
+ <checksum type="md5">789</checksum>
48
+ <checksum type="sha1">666</checksum>
49
+ </file>
50
+ </resource>
51
+ </outer>
52
+ EOF
53
+ end
54
+
55
+ it "should handle single-element terms correctly" do
56
+ subject.elementA.should == ["valA"]
57
+ end
58
+
59
+ it "should handle term paths" do
60
+ subject.elC.should == ["valC"]
61
+ end
62
+
63
+ it "should handle multiple-element, terms with paths correctly" do
64
+ subject.elB.should == ["valB1", "valB2"]
65
+ end
66
+
67
+ it "should handle terms that require specific attributes" do
68
+ subject.elementC.should == ["valC"]
69
+ end
70
+
71
+ it "should handle" do
72
+ subject.here.length.should == 1
73
+ subject.here.first.split(/\W/).should include('123', '456')
74
+ end
75
+
76
+ it "should handle missing terms" do
77
+ subject.there.should be_empty
78
+ end
79
+
80
+ it "should handle element value given the absence of a specific attribute" do
81
+ subject.elementD.should == ["valD1"]
82
+ subject.no_attrib.should == ["valB1"]
83
+ end
84
+
85
+ it "should handle OM terms for an attribute value" do
86
+ subject.elementB.my_attr.should == ["vole"]
87
+ subject.alternate.should == ["vole"]
88
+ subject.another.should == ["vole"]
89
+ subject.animal_attrib.should include("vole", "seagull")
90
+ end
91
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe "an example with :proxy and :ref" do
4
+ before(:all) do
5
+ class ExampleProxyAndRefTerminology
6
+ include OM::XML::Document
7
+
8
+ set_terminology do |t|
9
+ t.root(:path => "outer", :xmlns => '', :namespace_prefix => nil)
10
+
11
+ t.resource(:namespace_prefix => nil) {
12
+ t.fedora_pid(:path=>{:attribute=>"objectId"}, :namespace_prefix => nil)
13
+ t.file(:ref=>[:file], :namespace_prefix => nil, :namespace_prefix => nil)
14
+ }
15
+ t.file(:namespace_prefix => nil) {
16
+ t.ds_label(:path=>{:attribute=>"id"}, :namespace_prefix => nil)
17
+ t.size(:path=>{:attribute=>"size"}, :namespace_prefix => nil)
18
+ t.md5(:path=>"checksum", :attributes=>{:type=>"md5"}, :namespace_prefix => nil)
19
+ t.sha1(:path=>"checksum", :attributes=>{:type=>"sha1"}, :namespace_prefix => nil)
20
+ }
21
+ # really want ead where the type is ead and the file format is XML and the file mimetype is text/xml (and the file id is (?coll_ead.xml ... can be whatever the label of the DS is in the FileAsset object))
22
+ t.ead(:ref=>:resource, :attributes=>{:type=>"ead"})
23
+ t.image(:ref=>:resource, :attributes=>{:type=>"image"})
24
+
25
+ t.ead_fedora_pid(:proxy=>[:ead, :fedora_pid])
26
+ t.ead_ds_label(:proxy=>[:ead, :file, :ds_label])
27
+ t.ead_size(:proxy=>[:ead, :file, :size])
28
+ t.ead_md5(:proxy=>[:ead, :file, :md5])
29
+ t.ead_sha1(:proxy=>[:ead, :file, :sha1])
30
+
31
+ t.image_fedora_pid(:proxy=>[:image, :fedora_pid])
32
+ t.image_ds_label(:proxy=>[:image, :file, :ds_label])
33
+ t.image_size(:proxy=>[:image, :file, :size])
34
+ t.image_md5(:proxy=>[:image, :file, :md5])
35
+ t.image_sha1(:proxy=>[:image, :file, :sha1])
36
+ end
37
+ end
38
+ end
39
+
40
+ subject do
41
+ ExampleProxyAndRefTerminology.from_xml <<-EOF
42
+ <outer outerId="hypatia:outer" type="outer type">
43
+ <resource type="ead" id="coll.ead" objectId="hypatia:ead_file_asset_fixture">
44
+ <file id="my_ead.xml" format="XML" mimetype="text/xml" size="47570">
45
+ <checksum type="md5">123</checksum>
46
+ <checksum type="sha1">456</checksum>
47
+ </file>
48
+ </resource>
49
+ <resource type="image" id="image" objectId="hypatia:coll_img_file_asset_fixture">
50
+ <file id="my_image.jpg" format="JPG" mimetype="image/jpeg" size="302080">
51
+ <checksum type="md5">789</checksum>
52
+ <checksum type="sha1">666</checksum>
53
+ </file>
54
+ </resource>
55
+ </outer>
56
+
57
+ EOF
58
+ end
59
+
60
+ describe "image" do
61
+ it "should have the right proxy terms" do
62
+ subject.ead_fedora_pid.should include "hypatia:ead_file_asset_fixture"
63
+ subject.ead_ds_label.should include "my_ead.xml"
64
+ subject.ead_size.should include "47570"
65
+ subject.ead_md5.should include "123"
66
+ subject.ead_sha1.should include "456"
67
+ end
68
+ end
69
+
70
+ describe "ead" do
71
+ it "should have the right proxy terms" do
72
+ subject.image_fedora_pid.should include "hypatia:coll_img_file_asset_fixture"
73
+ subject.image_ds_label.should include "my_image.jpg"
74
+ subject.image_size.should include "302080"
75
+ subject.image_md5.should include "789"
76
+ subject.image_sha1.should include "666"
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require "om/samples"
3
+
4
+ describe "Rspec tests for QUERYING_DOCUMENTS.textile" do
5
+
6
+ before(:all) do
7
+ @xml_file = "mods_articles/hydrangea_article1.xml"
8
+ @doc = OM::Samples::ModsArticle.from_xml(fixture @xml_file) { |conf|
9
+ conf.default_xml.noblanks
10
+ }
11
+ @term = OM::Samples::ModsArticle.terminology
12
+ end
13
+
14
+ it "xpath_for()" do
15
+ @term.xpath_for(:name).should ==
16
+ '//oxns:name'
17
+ @term.xpath_for(:person).should ==
18
+ '//oxns:name[@type="personal"]'
19
+ @term.xpath_for(:organization).should ==
20
+ '//oxns:name[@type="corporate"]'
21
+ @term.xpath_for(:person, :first_name).should ==
22
+ '//oxns:name[@type="personal"]/oxns:namePart[@type="given"]'
23
+ @term.xpath_for(:journal, :issue, :pages, :start).should ==
24
+ '//oxns:relatedItem[@type="host"]/oxns:part/oxns:extent[@unit="pages"]/oxns:start'
25
+ end
26
+
27
+ it "term_values()" do
28
+ @doc.term_values(:person, :first_name).should == ["GIVEN NAMES", "Siddartha"]
29
+ @doc.term_values(:person, :last_name).should == ["FAMILY NAME", "Gautama"]
30
+ @doc.term_values(:organization, :namePart).should == ['NSF']
31
+ @doc.term_values(:journal, :issue, :pages, :start).should == ['195']
32
+ @doc.term_values(:journal, :title_info, :main_title).should == ["TITLE OF HOST JOURNAL"]
33
+ end
34
+
35
+ it "xpath_for(): relative vs absolute" do
36
+ xp_rel = '//oxns:titleInfo/oxns:title'
37
+ xp_abs = '//oxns:mods/oxns:titleInfo/oxns:title'
38
+ @term.xpath_for( :title_info, :main_title).should == xp_rel
39
+ @term.xpath_for(:mods, :title_info, :main_title).should == xp_abs
40
+ end
41
+
42
+ it "term_values(): relative vs absolute" do
43
+ exp = [
44
+ "ARTICLE TITLE HYDRANGEA ARTICLE 1",
45
+ "Artikkelin otsikko Hydrangea artiklan 1",
46
+ "TITLE OF HOST JOURNAL",
47
+ ]
48
+ xp_abs = '//oxns:mods/oxns:titleInfo/oxns:title'
49
+ @doc.term_values( :title_info, :main_title).should == exp
50
+ @doc.term_values(:mods, :title_info, :main_title).should == exp[0..1]
51
+ end
52
+
53
+ it "find_by_terms()" do
54
+ exp_xml_role = '<role><roleTerm authority="marcrelator" type="text">funder</roleTerm></role>'
55
+ exp_xml_start = '<start>195</start>'
56
+ @doc.find_by_terms(:organization, :role).class.should == Nokogiri::XML::NodeSet
57
+ @doc.find_by_terms(:organization, :role).to_xml.should be_equivalent_to exp_xml_role
58
+ @doc.find_by_terms(:journal, :issue, :pages, :start).to_xml.should == exp_xml_start
59
+ end
60
+
61
+ it "find_by_terms() error" do
62
+ exp_err = OM::XML::Terminology::BadPointerError
63
+ expect { @doc.find_by_terms :journal, :issue, :BLAH, :start }.to raise_error exp_err
64
+ end
65
+
66
+ it "proxies" do
67
+ @term.xpath_for(:title).should ==
68
+ '//oxns:titleInfo/oxns:title'
69
+ @term.xpath_for(:journal_title)
70
+ '//oxns:relatedItem[@type="host"]/oxns:titleInfo/oxns:title'
71
+ end
72
+
73
+ end