om 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|