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/.document +5 -0
- data/.gitignore +21 -0
- data/History.textile +1 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/om/xml/accessors.rb +130 -0
- data/lib/om/xml/container.rb +32 -0
- data/lib/om/xml/properties.rb +380 -0
- data/lib/om/xml/property_value_operators.rb +111 -0
- data/lib/om/xml/validation.rb +63 -0
- data/lib/om/xml.rb +23 -0
- data/lib/om.rb +9 -0
- data/om.gemspec +89 -0
- data/spec/fixtures/CBF_MODS/ARS0025_016.xml +94 -0
- data/spec/fixtures/RUBRIC_mods_article_template.xml +89 -0
- data/spec/fixtures/mods-3-2.xsd +1 -24
- data/spec/fixtures/mods_articles/hydrangea_article1.xml +90 -0
- data/spec/fixtures/test_dummy_mods.xml +36 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/unit/accessors_spec.rb +156 -0
- data/spec/unit/container_spec.rb +60 -0
- data/spec/unit/properties_spec.rb +247 -0
- data/spec/unit/property_value_operators_spec.rb +245 -0
- data/spec/unit/validation_spec.rb +78 -0
- data/spec/unit/xml_spec.rb +21 -0
- metadata +174 -0
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -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
|