om 0.1.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.
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,14 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'om'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+ require 'ruby-debug'
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.mock_with :mocha
10
+ end
11
+
12
+ def fixture(file)
13
+ File.new(File.join(File.dirname(__FILE__), 'fixtures', file))
14
+ end
@@ -0,0 +1,156 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "nokogiri"
3
+ require "om"
4
+
5
+ describe "OM::XML::Accessors" do
6
+
7
+ before(:all) do
8
+ class AccessorTest
9
+
10
+ include OM::XML::Container
11
+ include OM::XML::Accessors
12
+ #accessor :title, :relative_xpath=>[:titleInfo, :title]}}
13
+
14
+ accessor :title_info, :relative_xpath=>'oxns:titleInfo', :children=>[
15
+ {:main_title=>{:relative_xpath=>'oxns:title'}},
16
+ {:language =>{:relative_xpath=>{:attribute=>"lang"} }}
17
+ ] # this allows you to access the language attribute as if it was a regular child accessor
18
+ accessor :abstract
19
+ accessor :topic_tag, :relative_xpath=>'oxns:subject/oxns:topic'
20
+ accessor :person, :relative_xpath=>'oxns:name[@type="personal"]', :children=>[
21
+ {:last_name=>{:relative_xpath=>'oxns:namePart[@type="family"]'}},
22
+ {:first_name=>{:relative_xpath=>'oxns:namePart[@type="given"]'}},
23
+ {:institution=>{:relative_xpath=>'oxns:affiliation'}},
24
+ {:role=>{:children=>[
25
+ {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
26
+ {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
27
+ ]}}
28
+ ]
29
+ accessor :organization, :relative_xpath=>'oxns:name[@type="institutional"]', :children=>[
30
+ {:role=>{:children=>[
31
+ {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
32
+ {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
33
+ ]}}
34
+ ]
35
+ accessor :conference, :relative_xpath=>'oxns:name[@type="conference"]', :children=>[
36
+ {:role=>{:children=>[
37
+ {:text=>{:relative_xpath=>'oxns:roleTerm[@type="text"]'}},
38
+ {:code=>{:relative_xpath=>'oxns:roleTerm[@type="code"]'}}
39
+ ]}}
40
+ ]
41
+ accessor :journal, :relative_xpath=>'oxns:relatedItem[@type="host"]', :children=>[
42
+ # allows for children that are hashes...
43
+ # this allows for more robust handling of nested values (in generating views and when generating solr field names)
44
+ {:title=>{:relative_xpath=>'oxns:titleInfo/oxns:title'}},
45
+ {:publisher=>{:relative_xpath=>'oxns:originInfo/oxns:publisher'}},
46
+ {:issn=>{:relative_xpath=>'oxns:identifier[@type="issn"]'}},
47
+ {:date_issued=>{:relative_xpath=>'oxns:originInfo/oxns:dateIssued'}},
48
+ {:issue => {:relative_xpath=>"oxns:part", :children=>[
49
+ {:volume=>{:relative_xpath=>'oxns:detail[@type="volume"]'}},
50
+ {:level=>{:relative_xpath=>'oxns:detail[@type="level"]'}},
51
+ {:start_page=>{:relative_xpath=>'oxns:extent[@unit="pages"]/oxns:start'}},
52
+ {:end_page=>{:relative_xpath=>'oxns:extent[@unit="pages"]/oxns:end'}},
53
+ {:publication_date=>{:relative_xpath=>'oxns:date'}}
54
+ ]}}
55
+ ]
56
+ end
57
+
58
+ end
59
+
60
+ before(:each) do
61
+ article_xml = fixture( File.join("mods_articles", "hydrangea_article1.xml") )
62
+ @sample = AccessorTest.from_xml(article_xml)
63
+ end
64
+
65
+ describe '#accessor' do
66
+ it "should populate the .accessors hash" do
67
+ AccessorTest.accessors[:abstract][:relative_xpath].should == "oxns:abstract"
68
+ AccessorTest.accessors[:journal][:relative_xpath].should == 'oxns:relatedItem[@type="host"]'
69
+ AccessorTest.accessors[:journal][:children][:issue][:relative_xpath].should == "oxns:part"
70
+ AccessorTest.accessors[:journal][:children][:issue][:children][:end_page][:relative_xpath].should == 'oxns:extent[@unit="pages"]/oxns:end'
71
+
72
+ AccessorTest.accessors[:person][:children][:role][:children][:text][:relative_xpath].should == 'oxns:roleTerm[@type="text"]'
73
+ end
74
+ end
75
+
76
+ describe ".retrieve" do
77
+ it "should use Nokogiri to retrieve a NodeSet corresponding to the combination of accessor keys and array/nodeset indexes" do
78
+ @sample.retrieve( :person ).length.should == 2
79
+
80
+ @sample.retrieve( :person, 1 ).first.should == @sample.ng_xml.xpath('//oxns:name[@type="personal" and position()=2]', "oxns"=>"http://www.loc.gov/mods/v3").first
81
+ @sample.retrieve( :person, 1, :first_name ).class.should == Nokogiri::XML::NodeSet
82
+ @sample.retrieve( :person, 1, :first_name ).first.text.should == "Siddartha"
83
+ end
84
+
85
+ it "should support accessors whose relative_xpath is a lookup array instead of an xpath string" do
86
+ # pending "this only impacts scenarios where we want to display & edit"
87
+ AccessorTest.accessors[:title_info][:children][:language][:relative_xpath].should == {:attribute=>"lang"}
88
+ # @sample.retrieve( :title, 1 ).first.text.should == "Artikkelin otsikko Hydrangea artiklan 1"
89
+ @sample.retrieve( :title_info, 1, :language ).first.text.should == "finnish"
90
+ end
91
+
92
+ end
93
+
94
+ describe ".retrieve_at" do
95
+ it "should return the first node in the resulting set (uses Nokogiri xpath_at)" do
96
+ pending "might be able to make this implicit in the last value of call to .retrieve"
97
+ @sample.retrieve_at(:person, 1, :first_name).text.should == "Siddartha"
98
+ @sample.retrieve_at(:person, 1, :first_name).should == @sample.retrieve( :person, 1, :first_name).first
99
+ end
100
+ end
101
+
102
+ describe "generated accessor methods" do
103
+ it "should mix accessor methods into nodesets so you can use regular array syntax to access stuff" do
104
+ pending "This is tempting, but somewhat difficult to implement and potentially slow at runtime. Might never be worth it?"
105
+ @sample.persons.length.should == 2
106
+ @sample.persons[1].first_name.text.should == "Siddartha"
107
+ @sample.persons.last.roles.length.should == 1
108
+ @sample.persons.last.roles[0].text.should == "teacher"
109
+ end
110
+ end
111
+
112
+ describe "#accessor_info" do
113
+ it "should return the xpath given in the call to #accessor" do
114
+ AccessorTest.accessor_info( :abstract ).should == AccessorTest.accessors[:abstract]
115
+ end
116
+ it "should return the xpath given in the call to #accessor" do
117
+ AccessorTest.accessor_info( :abstract ).should == AccessorTest.accessors[:abstract]
118
+ end
119
+ it "should dig into the accessors hash as far as you want, ignoring index values" do
120
+ AccessorTest.accessor_info( :conference, 0, :role, 1, :text ).should == AccessorTest.accessors[:conference][:children][:role][:children][:text]
121
+ AccessorTest.accessor_info( :conference, :role, :text ).should == AccessorTest.accessors[:conference][:children][:role][:children][:text]
122
+ end
123
+ end
124
+
125
+ describe "#accessor_xpath" do
126
+ it "should return the xpath given in the call to #accessor" do
127
+ AccessorTest.accessor_xpath( :abstract ).should == '//oxns:abstract'
128
+ end
129
+ # Note: Ruby array indexes begin from 0. In xpath queries (which start from 1 instead of 0), this will be translated accordingly.
130
+ it "should prepend the xpath for any parent nodes, inserting calls to xpath:position() function where necessary" do
131
+ AccessorTest.accessor_xpath( :conference, 0, :role, 1, :text ).should == '//oxns:name[@type="conference" and position()=1]/oxns:role[position()=2]/oxns:roleTerm[@type="text"]'
132
+ end
133
+ end
134
+ # describe ".accessor_xpath (instance method)" do
135
+ # it "should delegate to the class method" do
136
+ # AccessorTest.expects(:accessor_xpath).with( [:conference, conference_index, :text_role] )
137
+ # @sample.accessor_xpath( [:conference, conference_index, :role] )
138
+ # end
139
+ # end
140
+ #
141
+ # describe "generated catchall xpaths" do
142
+ # it "should return an xpath query that will catch all nodes corresponding to the specified accessor" do
143
+ # AccessorTest.journal_issue_end_page_xpath.should == 'oxns:relatedItem[@type="host"]/oxns:part/oxns:extent[@unit="pages"]/oxns:end'
144
+ # end
145
+ # it "should rely on #catchall_xpath" do
146
+ # AccessorTest.expects(:catchall_xpath).with(:journal, :issue, :end_page)
147
+ # AccessorTest.journal_issue_end_page_xpath
148
+ # end
149
+ # end
150
+ #
151
+ # describe ".catchall_xpath" do
152
+ # it "should return an xpath query that will catch all nodes corresponding to the specified accessor" do
153
+ # AccessorTest.catchall_xpath(:journal, :issue, :end_page).should == 'oxns:relatedItem[@type="host"]/oxns:part/oxns:extent[@unit="pages"]/oxns:end'
154
+ # end
155
+ # end
156
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "nokogiri"
3
+ require "om"
4
+
5
+ describe "OM::XML::Container" do
6
+
7
+ before(:all) do
8
+ class ContainerTest
9
+ include OM::XML::Container
10
+ end
11
+ end
12
+
13
+ before(:each) do
14
+ @container = ContainerTest.from_xml("<foo><bar>1</bar></foo>")
15
+ end
16
+
17
+ it "should automatically include the other modules" do
18
+ pending
19
+ ContainerTest.included_modules.should include(OM::XML::Accessor)
20
+ ContainerTest.included_modules.should include(OM::XML::Schema)
21
+ end
22
+
23
+ it "should add .ng_xml accessor" do
24
+ @container.should respond_to(:ng_xml)
25
+ @container.should respond_to(:ng_xml=)
26
+ end
27
+
28
+ describe "new" do
29
+ it "should populate ng_xml with an instance of Nokogiri::XML::Document" do
30
+ @container.ng_xml.class.should == Nokogiri::XML::Document
31
+ end
32
+ end
33
+
34
+ describe "#from_xml" do
35
+ it "should accept a String, parse it and store it in .ng_xml" do
36
+ Nokogiri::XML::Document.expects(:parse).returns("parsed xml")
37
+ container1 = ContainerTest.from_xml("<foo><bar>1</bar></foo>")
38
+ container1.ng_xml.should == "parsed xml"
39
+ end
40
+ it "should accept a File, parse it and store it in .ng_xml" do
41
+ file = fixture(File.join("mods_articles", "hydrangea_article1.xml"))
42
+ Nokogiri::XML::Document.expects(:parse).returns("parsed xml")
43
+ container1 = ContainerTest.from_xml(file)
44
+ container1.ng_xml.should == "parsed xml"
45
+ end
46
+ it "should accept Nokogiri nodes as input and leave them as-is" do
47
+ parsed_xml = Nokogiri::XML::Document.parse("<foo><bar>1</bar></foo>")
48
+ container1 = ContainerTest.from_xml(parsed_xml)
49
+ container1.ng_xml.should == parsed_xml
50
+ end
51
+ end
52
+
53
+ describe ".to_xml" do
54
+ it "should call .ng_xml.to_xml" do
55
+ @container.ng_xml.expects(:to_xml).returns("ng xml")
56
+ @container.to_xml.should == "ng xml"
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,247 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require "om"
3
+
4
+ describe "OM::XML::Properties" do
5
+
6
+ before(:all) do
7
+ #ModsHelpers.name_("Beethoven, Ludwig van", :date=>"1770-1827", :role=>"creator")
8
+ class FakeOxMods
9
+
10
+ include OM::XML::Container
11
+ include OM::XML::Properties
12
+
13
+ # Could add support for multiple root declarations.
14
+ # For now, assume that any modsCollections have already been broken up and fed in as individual mods documents
15
+ # root :mods_collection, :path=>"modsCollection",
16
+ # :attributes=>[],
17
+ # :subelements => :mods
18
+
19
+ root_property :mods, "mods", "http://www.loc.gov/mods/v3", :attributes=>["id", "version"], :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"
20
+
21
+
22
+ property :name_, :path=>"name",
23
+ :attributes=>[:xlink, :lang, "xml:lang", :script, :transliteration, {:type=>["personal", "enumerated", "corporate"]} ],
24
+ :subelements=>["namePart", "displayForm", "affiliation", :role, "description"],
25
+ :default_content_path => "namePart",
26
+ :convenience_methods => {
27
+ :date => {:path=>"namePart", :attributes=>{:type=>"date"}},
28
+ :family_name => {:path=>"namePart", :attributes=>{:type=>"family"}},
29
+ :given_name => {:path=>"namePart", :attributes=>{:type=>"given"}},
30
+ :terms_of_address => {:path=>"namePart", :attributes=>{:type=>"termsOfAddress"}}
31
+ }
32
+
33
+ property :person, :variant_of=>:name_, :attributes=>{:type=>"personal"}
34
+
35
+ property :role, :path=>"role",
36
+ :parents=>[:name_],
37
+ :attributes=>[ { "type"=>["text", "code"] } , "authority"],
38
+ :default_content_path => "roleTerm"
39
+
40
+
41
+ end
42
+
43
+ class FakeOtherOx < Nokogiri::XML::Document
44
+
45
+ include OM::XML::Properties
46
+ # extend OX::ClassMethods
47
+
48
+ root_property :other, "other", "http://www.foo.com"
49
+
50
+ end
51
+
52
+ end
53
+
54
+ before(:each) do
55
+ @fixturemods = FakeOxMods.from_xml( fixture( File.join("CBF_MODS", "ARS0025_016.xml") ) )
56
+ end
57
+
58
+ after(:all) do
59
+ Object.send(:remove_const, :FakeOxMods)
60
+ end
61
+
62
+ describe "#new" do
63
+ it "should set up namespaces" do
64
+ @fixturemods.ox_namespaces.should == {"oxns"=>"http://www.loc.gov/mods/v3", "xmlns:ns2"=>"http://www.w3.org/1999/xlink", "xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance", "xmlns:ns3"=>"http://www.loc.gov/mods/v3"}
65
+ end
66
+ end
67
+
68
+ describe "#root_property" do
69
+ it "should initialize root_property class attributes without attributes bleeding over to other OX classes" do
70
+ FakeOxMods.root_property_ref.should == :mods
71
+ FakeOxMods.root_config.should == {:ref=>:mods, :path=>"mods", :namespace=>"http://www.loc.gov/mods/v3", :attributes=>["id", "version"], :schema=>"http://www.loc.gov/standards/mods/v3/mods-3-2.xsd"}
72
+ FakeOxMods.ox_namespaces.should == {"oxns"=>"http://www.loc.gov/mods/v3"}
73
+
74
+ FakeOtherOx.root_property_ref.should == :other
75
+ FakeOtherOx.root_config.should == {:namespace=>"http://www.foo.com", :path=>"other", :ref=>:other}
76
+ end
77
+ end
78
+
79
+ describe "#property" do
80
+
81
+ it "fails gracefully if you try to look up nodes for an undefined property" do
82
+ @fixturemods.lookup(:nobody_home).should == []
83
+ end
84
+
85
+ it "constructs xpath queries for finding properties" do
86
+ FakeOxMods.properties[:name_][:xpath].should == '//oxns:name'
87
+ FakeOxMods.properties[:name_][:xpath_relative].should == 'oxns:name'
88
+
89
+ FakeOxMods.properties[:person][:xpath].should == '//oxns:name[@type="personal"]'
90
+ FakeOxMods.properties[:person][:xpath_relative].should == 'oxns:name[@type="personal"]'
91
+ end
92
+
93
+ it "constructs templates for value-driven searches" do
94
+ FakeOxMods.properties[:name_][:xpath_constrained].should == '//oxns:name[contains(oxns:namePart, "#{constraint_value}")]'.gsub('"', '\"')
95
+ FakeOxMods.properties[:person][:xpath_constrained].should == '//oxns:name[@type="personal" and contains(oxns:namePart, "#{constraint_value}")]'.gsub('"', '\"')
96
+
97
+ # Example of how you could use these templates:
98
+ constraint_value = "SAMPLE CONSTRAINT VALUE"
99
+ constrained_query = eval( '"' + FakeOxMods.properties[:person][:xpath_constrained] + '"' )
100
+ constrained_query.should == '//oxns:name[@type="personal" and contains(oxns:namePart, "SAMPLE CONSTRAINT VALUE")]'
101
+ end
102
+
103
+ it "constructs xpath queries & templates for convenience methods" do
104
+ FakeOxMods.properties[:name_][:convenience_methods][:date][:xpath].should == '//oxns:name/oxns:namePart[@type="date"]'
105
+ FakeOxMods.properties[:name_][:convenience_methods][:date][:xpath_relative].should == 'oxns:namePart[@type="date"]'
106
+ FakeOxMods.properties[:name_][:convenience_methods][:date][:xpath_constrained].should == '//oxns:name[contains(oxns:namePart[@type="date"], "#{constraint_value}")]'.gsub('"', '\"')
107
+
108
+ FakeOxMods.properties[:person][:convenience_methods][:date][:xpath].should == '//oxns:name[@type="personal"]/oxns:namePart[@type="date"]'
109
+ FakeOxMods.properties[:person][:convenience_methods][:date][:xpath_relative].should == 'oxns:namePart[@type="date"]'
110
+ FakeOxMods.properties[:person][:convenience_methods][:date][:xpath_constrained].should == '//oxns:name[@type="personal" and contains(oxns:namePart[@type="date"], "#{constraint_value}")]'.gsub('"', '\"')
111
+
112
+ end
113
+
114
+ it "constructs xpath queries & templates for subelements too" do
115
+ FakeOxMods.properties[:person][:convenience_methods][:displayForm][:xpath].should == '//oxns:name[@type="personal"]/oxns:displayForm'
116
+ FakeOxMods.properties[:person][:convenience_methods][:displayForm][:xpath_relative].should == 'oxns:displayForm'
117
+ FakeOxMods.properties[:person][:convenience_methods][:displayForm][:xpath_constrained].should == '//oxns:name[@type="personal" and contains(oxns:displayForm, "#{constraint_value}")]'.gsub('"', '\"')
118
+ end
119
+
120
+ it "supports subelements that are specified as separate properties" do
121
+ FakeOxMods.properties[:name_][:convenience_methods][:role][:xpath].should == '//oxns:name/oxns:role'
122
+ FakeOxMods.properties[:name_][:convenience_methods][:role][:xpath_relative].should == 'oxns:role'
123
+ FakeOxMods.properties[:name_][:convenience_methods][:role][:xpath_constrained].should == '//oxns:name[contains(oxns:role/oxns:roleTerm, "#{constraint_value}")]'.gsub('"', '\"')
124
+ end
125
+
126
+ it "should not overwrite default property info when adding a variant property" do
127
+ FakeOxMods.properties[:name_].should_not equal(FakeOxMods.properties[:person])
128
+ FakeOxMods.properties[:name_][:convenience_methods].should_not equal(FakeOxMods.properties[:person][:convenience_methods])
129
+
130
+ FakeOxMods.properties[:name_][:xpath].should_not == FakeOxMods.properties[:person][:xpath]
131
+ FakeOxMods.properties[:name_][:convenience_methods][:date][:xpath_constrained].should_not == FakeOxMods.properties[:person][:convenience_methods][:date][:xpath_constrained]
132
+ end
133
+
134
+ end
135
+
136
+ describe ".lookup" do
137
+
138
+ it "uses the generated xpath queries" do
139
+ @fixturemods.ng_xml.expects(:xpath).with('//oxns:name[@type="personal"]', @fixturemods.ox_namespaces)
140
+ @fixturemods.lookup(:person)
141
+
142
+ @fixturemods.ng_xml.expects(:xpath).with('//oxns:name[@type="personal" and contains(oxns:namePart, "Beethoven, Ludwig van")]', @fixturemods.ox_namespaces)
143
+ @fixturemods.lookup(:person, "Beethoven, Ludwig van")
144
+
145
+ @fixturemods.ng_xml.expects(:xpath).with('//oxns:name[@type="personal" and contains(oxns:namePart[@type="date"], "2010")]', @fixturemods.ox_namespaces)
146
+ @fixturemods.lookup(:person, :date=>"2010")
147
+
148
+ @fixturemods.ng_xml.expects(:xpath).with('//oxns:name[@type="personal" and contains(oxns:role/oxns:roleTerm, "donor")]', @fixturemods.ox_namespaces)
149
+ @fixturemods.lookup(:person, :role=>"donor")
150
+
151
+ # @fixturemods.expects(:xpath).with('//oxns:name[@type="personal"]/oxns:namePart[@type="date"]', @fixturemods.ox_namespaces)
152
+ # @fixturemods.lookup([:person,:date])
153
+
154
+ # @fixturemods.expects(:xpath).with('//oxns:name[contains(oxns:namePart[@type="date"], "2010")]', @fixturemods.ox_namespaces)
155
+ # @fixturemods.lookup([:person,:date], "2010")
156
+ end
157
+
158
+ end
159
+
160
+ describe ".xpath_query_for" do
161
+
162
+ it "retrieves the generated xpath query to match your desires" do
163
+ @fixturemods.xpath_query_for(:person).should == '//oxns:name[@type="personal"]'
164
+
165
+ @fixturemods.xpath_query_for(:person, "Beethoven, Ludwig van").should == '//oxns:name[@type="personal" and contains(oxns:namePart, "Beethoven, Ludwig van")]'
166
+
167
+ @fixturemods.xpath_query_for(:person, :date=>"2010").should == '//oxns:name[@type="personal" and contains(oxns:namePart[@type="date"], "2010")]'
168
+
169
+ @fixturemods.xpath_query_for(:person, :role=>"donor").should == '//oxns:name[@type="personal" and contains(oxns:role/oxns:roleTerm, "donor")]'
170
+
171
+ @fixturemods.xpath_query_for([:person,:date]).should == '//oxns:name[@type="personal"]/oxns:namePart[@type="date"]'
172
+
173
+ @fixturemods.xpath_query_for([:person,:date], "2010").should == '//oxns:name[@type="personal" and contains(oxns:namePart[@type="date"], "2010")]'
174
+ end
175
+
176
+ it "parrots any strings back to you (in case you already have an xpath query)" do
177
+ @fixturemods.xpath_query_for('//oxns:name[@type="personal"]/oxns:namePart[@type="date"]').should == '//oxns:name[@type="personal"]/oxns:namePart[@type="date"]'
178
+ end
179
+
180
+ end
181
+
182
+ describe "#generate_xpath" do
183
+ it "should generate an xpath query from the options in the provided hash and should support generating xpaths with constraint values" do
184
+ opts1 = {:path=>"name", :default_content_path=>"namePart"}
185
+ opts2 = {:path=>"originInfo"}
186
+ opts3 = {:path=>["name", "namePart"]}
187
+ FakeOxMods.generate_xpath( opts1 ).should == '//oxns:name'
188
+ FakeOxMods.generate_xpath( opts1, :constraints=>:default ).should == '//oxns:name[contains(oxns:namePart, "#{constraint_value}")]'
189
+ FakeOxMods.generate_xpath( opts2, :constraints=>:default ).should == '//oxns:originInfo[contains("#{constraint_value}")]'
190
+
191
+ FakeOxMods.generate_xpath( opts1, :variations=>{:attributes=>{:type=>"personal"}} ).should == '//oxns:name[@type="personal"]'
192
+ FakeOxMods.generate_xpath( opts1, :variations=>{:attributes=>{:type=>"personal"}}, :constraints=>:default ).should == '//oxns:name[@type="personal" and contains(oxns:namePart, "#{constraint_value}")]'
193
+
194
+ FakeOxMods.generate_xpath( opts1, :constraints=>{:path=>"namePart", :attributes=>{:type=>"date"}} ).should == '//oxns:name[contains(oxns:namePart[@type="date"], "#{constraint_value}")]'
195
+ FakeOxMods.generate_xpath( opts1, :constraints=>{:path=>"namePart", :attributes=>{:type=>"date"}}, :variations=>{:attributes=>{:type=>"personal"}} ).should == '//oxns:name[@type="personal" and contains(oxns:namePart[@type="date"], "#{constraint_value}")]'
196
+ FakeOxMods.generate_xpath(FakeOxMods.properties[:person], :variations=>{:attributes=>{:type=>"personal"}}, :constraints=>{:path=>"role", :default_content_path=>"roleTerm"}, :subelement_of=>":person").should == '//oxns:name[@type="personal" and contains(oxns:role/oxns:roleTerm, "#{constraint_value}")]'
197
+
198
+ FakeOxMods.generate_xpath(opts1, :variations=>{:attributes=>{:type=>"personal"}, :subelement_path=>"displayForm" } ).should == '//oxns:name[@type="personal"]/oxns:displayForm'
199
+ FakeOxMods.generate_xpath(opts1, :variations=>{:attributes=>{:type=>"personal"}}, :constraints=>{:path=>"displayForm"} ).should == '//oxns:name[@type="personal" and contains(oxns:displayForm, "#{constraint_value}")]'
200
+ FakeOxMods.generate_xpath(opts1, :variations=>{:attributes=>{:type=>"personal"}, :subelement_path=>["role", "roleTerm"] } ).should == '//oxns:name[@type="personal"]/oxns:role/oxns:roleTerm'
201
+
202
+ FakeOxMods.generate_xpath( opts3, :variations=>{:attributes=>{:type=>"date"}} ).should == '//oxns:name/oxns:namePart[@type="date"]'
203
+ end
204
+
205
+ it "should support relative paths" do
206
+ relative_opts = {:path=>"namePart"}
207
+ FakeOxMods.generate_xpath( relative_opts, :variations=>{:attributes=>{:type=>"date"}}, :relative=>true).should == 'oxns:namePart[@type="date"]'
208
+ end
209
+
210
+ it "should work with real properties hashes" do
211
+ FakeOxMods.generate_xpath(FakeOxMods.properties[:person], :variations=>FakeOxMods.properties[:person]).should == "//oxns:name[@type=\"personal\"]"
212
+ FakeOxMods.generate_xpath(FakeOxMods.properties[:person], :variations=>FakeOxMods.properties[:person], :constraints=>{:path=>"role", :default_content_path=>"roleTerm"}, :subelement_of=>":person").should == '//oxns:name[@type="personal" and contains(oxns:role/oxns:roleTerm, "#{constraint_value}")]'
213
+ date_hash = FakeOxMods.properties[:person][:convenience_methods][:date]
214
+ FakeOxMods.generate_xpath( date_hash, :variations=>date_hash, :relative=>true ).should == 'oxns:namePart[@type="date"]'
215
+ end
216
+
217
+ it "should support custom templates" do
218
+ opts = {:path=>"name", :default_content_path=>"namePart"}
219
+ FakeOxMods.generate_xpath( opts, :template=>'/#{prefix}:sampleNode/#{prefix}:#{path}[contains(#{default_content_path}, \":::constraint_value:::\")]' ).should == '/oxns:sampleNode/oxns:name[contains(namePart, "#{constraint_value}")]'
220
+ end
221
+ end
222
+
223
+ describe "#builder_template" do
224
+
225
+ it "should generate a template call for passing into the builder block (assumes 'xml' as the argument for the block)" do
226
+ FakeOxMods.builder_template([:person,:date]).should == 'xml.namePart( \'#{builder_new_value}\', :type=>\'date\' )'
227
+ FakeOxMods.builder_template([:name_,:affiliation]).should == 'xml.affiliation( \'#{builder_new_value}\' )'
228
+
229
+ simple_role_builder_template = 'xml.role( :type=>\'text\' ) { xml.roleTerm( \'#{builder_new_value}\' ) }'
230
+ FakeOxMods.builder_template([:role]).should == simple_role_builder_template
231
+ FakeOxMods.builder_template([:person,:role]).should == simple_role_builder_template
232
+
233
+ marcrelator_role_builder_template = 'xml.role( :type=>\'code\', :authority=>\'marcrelator\' ) { xml.roleTerm( \'#{builder_new_value}\' ) }'
234
+ FakeOxMods.builder_template([:role], {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ).should == marcrelator_role_builder_template
235
+ FakeOxMods.builder_template([:person,:role], {:attributes=>{"type"=>"code", "authority"=>"marcrelator"}} ).should == marcrelator_role_builder_template
236
+ end
237
+
238
+ end
239
+
240
+ describe "#applicable_attributes" do
241
+ it "returns a Hash where all of the values are strings" do
242
+ FakeOxMods.send(:applicable_attributes, {:type=>"date"} ).should == {:type=>"date"}
243
+ FakeOxMods.send(:applicable_attributes, ["authority", {:type=>["text","code"]}] ).should == {:type=>"text"}
244
+ end
245
+ end
246
+
247
+ end