mods 2.4.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +24 -0
  3. data/.gitignore +1 -0
  4. data/README.md +0 -1
  5. data/lib/mods/date.rb +54 -17
  6. data/lib/mods/marc_country_codes.rb +12 -10
  7. data/lib/mods/nom_terminology.rb +109 -845
  8. data/lib/mods/reader.rb +9 -39
  9. data/lib/mods/record.rb +13 -28
  10. data/lib/mods/version.rb +1 -1
  11. data/mods.gemspec +2 -2
  12. data/spec/fixture_data/hp566jq8781.xml +334 -0
  13. data/spec/integration/parker_spec.rb +217 -0
  14. data/spec/{date_spec.rb → lib/date_spec.rb} +9 -1
  15. data/spec/lib/language_spec.rb +123 -0
  16. data/spec/lib/location_spec.rb +175 -0
  17. data/spec/lib/name_spec.rb +368 -0
  18. data/spec/lib/origin_info_spec.rb +134 -0
  19. data/spec/lib/part_spec.rb +162 -0
  20. data/spec/lib/physical_description_spec.rb +72 -0
  21. data/spec/{reader_spec.rb → lib/reader_spec.rb} +1 -41
  22. data/spec/lib/record_info_spec.rb +114 -0
  23. data/spec/lib/record_spec.rb +287 -0
  24. data/spec/lib/related_item_spec.rb +124 -0
  25. data/spec/lib/subject_spec.rb +427 -0
  26. data/spec/lib/title_spec.rb +108 -0
  27. data/spec/lib/top_level_elmnts_simple_spec.rb +169 -0
  28. data/spec/spec_helper.rb +86 -5
  29. data/spec/support/fixtures.rb +9 -0
  30. metadata +49 -44
  31. data/.travis.yml +0 -16
  32. data/spec/language_spec.rb +0 -118
  33. data/spec/location_spec.rb +0 -295
  34. data/spec/name_spec.rb +0 -759
  35. data/spec/origin_info_spec.rb +0 -447
  36. data/spec/part_spec.rb +0 -471
  37. data/spec/physical_description_spec.rb +0 -144
  38. data/spec/record_info_spec.rb +0 -493
  39. data/spec/record_spec.rb +0 -356
  40. data/spec/related_item_spec.rb +0 -305
  41. data/spec/subject_spec.rb +0 -809
  42. data/spec/title_spec.rb +0 -226
  43. data/spec/top_level_elmnts_simple_spec.rb +0 -369
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Mods <physicalDescription> Element" do
4
+ it "extent child element" do
5
+ record = mods_record("<physicalDescription><extent>extent</extent></physicalDescription>")
6
+ expect(record.physical_description.extent.map(&:text)).to eq(["extent"])
7
+ end
8
+
9
+ context "note child element" do
10
+ subject(:note) do
11
+ mods_record(<<-XML).physical_description.note
12
+ <physicalDescription>
13
+ <form authority='aat'>Graphics</form>
14
+ <form>plain form</form>
15
+ <note displayLabel='Dimensions'>dimension text</note>
16
+ <note displayLabel='Condition'>condition text</note>
17
+ </physicalDescription>
18
+ XML
19
+ end
20
+ it "should understand note element" do
21
+ expect(note.map(&:text)).to eq(["dimension text", "condition text"])
22
+ end
23
+ it "should understand displayLabel attribute on note element" do
24
+ expect(note.displayLabel).to eq(["Dimensions", "Condition"])
25
+ end
26
+ end
27
+
28
+ context "form child element" do
29
+ subject(:physical_description) do
30
+ mods_record(<<-XML).physical_description
31
+ <physicalDescription>
32
+ <form authority='smd'>map</form>
33
+ <form type='material'>foo</form>
34
+ <extent>1 map ; 22 x 18 cm.</extent>
35
+ </physicalDescription>
36
+ XML
37
+ end
38
+
39
+ it "should understand form element" do
40
+ expect(physical_description.form.map(&:text)).to eq(["map", "foo"])
41
+ end
42
+ it "should understand authority attribute on form element" do
43
+ expect(physical_description.form.authority).to eq(["smd"])
44
+ end
45
+ it "should understand type attribute on form element" do
46
+ expect(physical_description.form.type_at).to eq(["material"])
47
+ end
48
+ end
49
+
50
+ context "digital materials" do
51
+ subject(:physical_description) do
52
+ mods_record(<<-XML).physical_description
53
+ <physicalDescription>
54
+ <reformattingQuality>preservation</reformattingQuality>
55
+ <internetMediaType>image/jp2</internetMediaType>
56
+ <digitalOrigin>reformatted digital</digitalOrigin>
57
+ </physicalDescription>
58
+ XML
59
+ end
60
+
61
+ it "should understand reformattingQuality child element" do
62
+ expect(physical_description.reformattingQuality.map(&:text)).to eq(["preservation"])
63
+ end
64
+ it "should understand digitalOrigin child element" do
65
+ expect(physical_description.digitalOrigin.map(&:text)).to eq(["reformatted digital"])
66
+ end
67
+ it "should understand internetMediaType child element" do
68
+ expect(physical_description.internetMediaType.map(&:text)).to eq(["image/jp2"])
69
+ end
70
+ end
71
+
72
+ end
@@ -33,7 +33,7 @@ describe "Mods::Reader" do
33
33
 
34
34
  context "from_file" do
35
35
  before(:all) do
36
- @fixture_dir = File.join(File.dirname(__FILE__), 'fixture_data')
36
+ @fixture_dir = File.join(File.dirname(__FILE__), '../fixture_data')
37
37
  @fixture_mods_file = File.join(@fixture_dir, 'shpc1.mods.xml')
38
38
  @from_file = Mods::Reader.new.from_file(@fixture_mods_file)
39
39
  end
@@ -48,7 +48,6 @@ describe "Mods::Reader" do
48
48
  context "namespace awareness" do
49
49
  it "should care about namespace by default" do
50
50
  r = Mods::Reader.new
51
- expect(r.namespace_aware).to be_truthy
52
51
  expect(@doc_from_str_default_ns.root.namespace.href).to eq(Mods::MODS_NS)
53
52
  expect(@doc_from_str_default_ns.xpath('/m:mods/m:note', @ns_hash).text).to eq("ns")
54
53
  expect(@doc_from_str_default_ns.xpath('/mods/note').size).to eq(0)
@@ -61,24 +60,6 @@ describe "Mods::Reader" do
61
60
  expect(@doc_from_str_wrong_ns.xpath('/m:mods/m:note', @ns_hash).size).to eq(0)
62
61
  expect(@doc_from_str_wrong_ns.xpath('/mods/note').size).to eq(0)
63
62
  end
64
-
65
- it "should be allowed not to care about namespaces" do
66
- r = Mods::Reader.new(false)
67
- expect(r.namespace_aware).to be_falsey
68
- my_from_str_ns = r.from_str(@example_ns_str)
69
- expect(my_from_str_ns.xpath('/m:mods/m:note', @ns_hash).size).to eq(0)
70
- expect(my_from_str_ns.xpath('/mods/note').text).to eq("ns")
71
- my_from_str_no_ns = r.from_str(@example_no_ns_str)
72
- expect(my_from_str_no_ns.xpath('/m:mods/m:note', @ns_hash).size).to eq(0)
73
- expect(my_from_str_no_ns.xpath('/mods/note').text).to eq("no ns")
74
- my_from_str_wrong_ns = r.from_str(@example_wrong_ns_str)
75
- expect(my_from_str_wrong_ns.xpath('/m:mods/m:note', @ns_hash).size).to eq(0)
76
- expect(my_from_str_wrong_ns.xpath('/mods/note').text).to eq("wrong ns")
77
- end
78
- end
79
-
80
- it "should do something useful when it gets unparseable XML" do
81
- skip "need to implement error handling for bad xml"
82
63
  end
83
64
 
84
65
  context "normalizing mods" do
@@ -94,19 +75,6 @@ describe "Mods::Reader" do
94
75
  reader = Mods::Reader.new.from_str(utf_mods)
95
76
  expect(reader.encoding).to eql("UTF-8")
96
77
  end
97
- it "should remove xsi:schemaLocation attribute from mods element if removing namespaces" do
98
- str = '<ns3:mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://www.loc.gov/mods/v3" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
99
- <ns3:note>be very frightened</ns3:note></ns3:mods>'
100
- ng_xml = Nokogiri::XML(str)
101
- # Nokogiri treats namespaced attributes differently in jruby than in ruby
102
- expect(ng_xml.root.has_attribute?('schemaLocation') || ng_xml.root.has_attribute?('xsi:schemaLocation')).to be_truthy
103
- r = Mods::Reader.new
104
- r.namespace_aware = false
105
- r.from_nk_node(ng_xml)
106
- # the below are different depending on jruby or ruby ... oy
107
- expect(r.mods_ng_xml.root.attributes.keys).not_to include('schemaLocation')
108
- expect(r.mods_ng_xml.root.attributes.keys).not_to include('xsi:schemaLocation')
109
- end
110
78
  end
111
79
 
112
80
  context "from_nk_node" do
@@ -142,14 +110,6 @@ describe "Mods::Reader" do
142
110
  it "should care about namespace by default" do
143
111
  expect(@mods_ng_doc.xpath('/m:mods/m:titleInfo/m:title', @ns_hash).text).to eq("boo")
144
112
  end
145
- it "should be able not to care about namespaces" do
146
- @r.namespace_aware = false
147
- mods_ng_doc = @r.from_nk_node(@mods_node)
148
- expect(mods_ng_doc).to be_kind_of(Nokogiri::XML::Document)
149
- expect(mods_ng_doc.xpath('/m:mods/m:titleInfo/m:title', @ns_hash).size).to eq(0)
150
- expect(mods_ng_doc.xpath('/mods/titleInfo/title').text).to eq("boo")
151
- @r.namespace_aware = true
152
- end
153
113
  end # context from_nk_node
154
114
 
155
115
  end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Mods <recordInfo> Element" do
4
+ context 'with some basic record info' do
5
+ subject(:record_info) do
6
+ mods_record("<recordInfo>
7
+ <recordContentSource authority='marcorg'>RQE</recordContentSource>
8
+ <recordCreationDate encoding='marc'>890517</recordCreationDate>
9
+ <recordIdentifier source='SIRSI'>a9079953</recordIdentifier>
10
+ </recordInfo>").record_info
11
+ end
12
+
13
+ it 'has the expected attributes' do
14
+ expect(record_info.first).to have_attributes(
15
+ recordContentSource: match_array(have_attributes(
16
+ authority: 'marcorg',
17
+ text: 'RQE'
18
+ )),
19
+ recordCreationDate: match_array(have_attributes(
20
+ encoding: 'marc',
21
+ text: '890517'
22
+ )),
23
+ recordIdentifier: match_array(have_attributes(
24
+ source: 'SIRSI',
25
+ text: 'a9079953'
26
+ ))
27
+ )
28
+ end
29
+ end
30
+
31
+ context 'with some more expansive record info' do
32
+ subject(:record_info) do
33
+ mods_record("<recordInfo>
34
+ <descriptionStandard>aacr2</descriptionStandard>
35
+ <recordContentSource authority='marcorg'>AU@</recordContentSource>
36
+ <recordCreationDate encoding='marc'>050921</recordCreationDate>
37
+ <recordIdentifier source='SIRSI'>a8837534</recordIdentifier>
38
+ <languageOfCataloging>
39
+ <languageTerm authority='iso639-2b' type='code'>eng</languageTerm>
40
+ </languageOfCataloging>
41
+ </recordInfo>").record_info
42
+ end
43
+
44
+ it 'has the expected attributes' do
45
+ expect(record_info.first).to have_attributes(
46
+ descriptionStandard: match_array(have_attributes(text: 'aacr2')),
47
+ recordContentSource: match_array(have_attributes(
48
+ authority: 'marcorg',
49
+ text: 'AU@'
50
+ )),
51
+ recordCreationDate: match_array(have_attributes(
52
+ encoding: 'marc',
53
+ text: '050921'
54
+ )),
55
+ recordIdentifier: match_array(have_attributes(
56
+ source: 'SIRSI',
57
+ text: 'a8837534'
58
+ )),
59
+ languageOfCataloging: match_array(have_attributes(
60
+ languageTerm: match_array(have_attributes(
61
+ authority: 'iso639-2b',
62
+ type_at: 'code',
63
+ text: 'eng'
64
+ ))
65
+ ))
66
+ )
67
+ end
68
+ end
69
+
70
+ context 'with an rlin record info' do
71
+ subject(:record_info) do
72
+ mods_record("<recordInfo>
73
+ <descriptionStandard>appm</descriptionStandard>
74
+ <recordContentSource authority='marcorg'>CSt</recordContentSource>
75
+ <recordCreationDate encoding='marc'>850416</recordCreationDate>
76
+ <recordChangeDate encoding='iso8601'>19991012150824.0</recordChangeDate>
77
+ <recordIdentifier source='CStRLIN'>a4083219</recordIdentifier>
78
+ </recordInfo>").record_info
79
+ end
80
+
81
+ it 'has the expected attributes' do
82
+ expect(record_info.first).to have_attributes(
83
+ recordChangeDate: match_array(have_attributes(
84
+ encoding: 'iso8601',
85
+ text: '19991012150824.0'
86
+ ))
87
+ )
88
+ end
89
+ end
90
+
91
+ context 'with a displayLabel' do
92
+ subject(:record) do
93
+ mods_record("<recordInfo displayLabel='val'><recordOrigin>nowhere</recordOrigin></recordInfo></mods>")
94
+ end
95
+
96
+ it 'has the expected attributes' do
97
+ expect(record.record_info.first).to have_attributes(
98
+ displayLabel: 'val'
99
+ )
100
+ end
101
+ end
102
+
103
+ context 'with a recordOrigin' do
104
+ subject(:record) do
105
+ mods_record("<recordInfo><recordOrigin>human prepared</recordOrigin></recordInfo></mods>")
106
+ end
107
+
108
+ it 'has the expected attributes' do
109
+ expect(record.record_info.first).to have_attributes(
110
+ recordOrigin: have_attributes(text: 'human prepared')
111
+ )
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,287 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Mods::Record" do
4
+ context "from_str" do
5
+ before(:all) do
6
+ @mods_ng_doc_w_ns = mods_record('<note>default ns</note>')
7
+ end
8
+ it "should return a mods record" do
9
+ expect(@mods_ng_doc_w_ns).to be_a_kind_of(Mods::Record)
10
+ end
11
+ it "should have namespace aware parsing turned on by default" do
12
+ expect(@mods_ng_doc_w_ns.namespaces.size).to be > 0
13
+ end
14
+ it "terminology should work with Record object defaults when mods string has namespaces" do
15
+ expect(@mods_ng_doc_w_ns.note.map { |e| e.text }).to eq(['default ns'])
16
+ end
17
+ it "terminology should not work with Record object defaults when mods string has NO namespaces" do
18
+ mods_ng_doc = Mods::Record.new.from_str('<mods><note>no ns</note></mods>')
19
+ expect(mods_ng_doc.note.size).to eq(0)
20
+ end
21
+ end
22
+
23
+ # Be able to create a new Mods::Record from a url
24
+ context "from_url" do
25
+ before(:all) do
26
+ @mods_doc = Mods::Record.new.from_url('http://www.loc.gov/standards/mods/modsrdf/examples/0001.xml')
27
+ end
28
+ it "should return a mods record" do
29
+ expect(@mods_doc).to be_a_kind_of(Mods::Record)
30
+ end
31
+ it "should raise an error on a bad url" do
32
+ expect{Mods::Record.new.from_url("http://example.org/fake.xml")}.to raise_error
33
+ end
34
+ end
35
+
36
+ # Be able to create a new Mods::Record from a file
37
+ context "from_file" do
38
+ before(:all) do
39
+ @fixture_dir = File.join(File.dirname(__FILE__), '../fixture_data')
40
+ @mods_doc = Mods::Record.new.from_file(File.join(@fixture_dir, 'shpc1.mods.xml'))
41
+ end
42
+ it "should return a mods record" do
43
+ expect(@mods_doc).to be_a_kind_of(Mods::Record)
44
+ end
45
+ end
46
+
47
+ context "from_nk_node" do
48
+ before(:all) do
49
+ oai_resp = '<?xml version="1.0" encoding="UTF-8"?>
50
+ <OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/">
51
+ <responseDate>2012-11-13T22:11:35Z</responseDate>
52
+ <request>http://sul-lyberservices-prod.stanford.edu/sw-oai-provider/oai</request>
53
+ <GetRecord>
54
+ <record>
55
+ <header>
56
+ <identifier>oai:searchworks.stanford.edu/druid:mm848sz7984</identifier>
57
+ <datestamp>2012-10-28T01:06:31Z</datestamp>
58
+ </header>
59
+ <metadata>
60
+ <ns3:mods xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="http://www.loc.gov/mods/v3" xsi:schemaLocation="http://www.loc.gov/mods/v3 http://www.loc.gov/standards/mods/v3/mods-3-2.xsd">
61
+ <ns3:titleInfo>
62
+ <ns3:title>boo</ns3:title>
63
+ </ns3:titleInfo>
64
+ </ns3:mods>
65
+ </metadata>
66
+ </record>
67
+ </GetRecord>
68
+ </OAI-PMH>'
69
+ ng_xml = Nokogiri::XML(oai_resp)
70
+ @mods_node = ng_xml.xpath('//mods:mods', mods: 'http://www.loc.gov/mods/v3').first
71
+ @mods_ng_doc = Mods::Record.new.from_nk_node(@mods_node)
72
+ bad_ns_wrapped = '<?xml version="1.0" encoding="UTF-8"?>
73
+ <metadata>
74
+ <n:mods xmlns:n="http://www.not.mods.org">
75
+ <n:titleInfo>
76
+ <n:title>What? No namespaces?</n:title>
77
+ </n:titleInfo>
78
+ </n:mods>
79
+ </metadata>'
80
+ ng_xml = Nokogiri::XML(bad_ns_wrapped)
81
+ @mods_node_no_ns = ng_xml.xpath('//n:mods', {'n'=>'http://www.not.mods.org'}).first
82
+ end
83
+ it "should return a mods record" do
84
+ expect(@mods_ng_doc).to be_a_kind_of(Mods::Record)
85
+ end
86
+ it "should have namespace aware parsing turned on by default" do
87
+ expect(@mods_ng_doc.namespaces.size).to be > 0
88
+ end
89
+ it "terminology should work with Record object defaults when mods string has namespaces" do
90
+ expect(@mods_ng_doc.title_info.title.map { |e| e.text }).to eq(["boo"])
91
+ end
92
+ it "terminology should not work with Record object defaults when mods node has NO namespaces" do
93
+ mods_ng_doc = Mods::Record.new.from_nk_node(@mods_node_no_ns)
94
+ expect(mods_ng_doc.title_info.title.size).to eq(0)
95
+ end
96
+ end # context from_nk_node
97
+
98
+ context "getting term values" do
99
+ subject(:mods_rec) do
100
+ mods_record(<<-XML)
101
+ <abstract>single</abstract>
102
+ <genre></genre>
103
+ <note>mult1</note>
104
+ <note>mult2</note>
105
+ <subject><topic>topic1</topic><topic>topic2</topic></subject>
106
+ <subject><topic>topic3</topic></subject>
107
+ XML
108
+ end
109
+
110
+ context "term_value (single value result)" do
111
+ it "should return nil if there are no such values in the MODS" do
112
+ expect(mods_rec.term_value(:identifier)).to be_nil
113
+ end
114
+ it "should return nil if there are only empty values in the MODS" do
115
+ expect(mods_rec.term_value(:genre)).to be_nil
116
+ end
117
+ it "should return a String for a single value" do
118
+ expect(mods_rec.term_value(:abstract)).to eq('single')
119
+ end
120
+ it "should return a String containing all values, with separator, for multiple values" do
121
+ expect(mods_rec.term_value(:note)).to eq('mult1 mult2')
122
+ end
123
+ it "should work with an Array of messages passed as the argument" do
124
+ expect(mods_rec.term_value([:subject, 'topic'])).to eq('topic1 topic2 topic3')
125
+ end
126
+ it "should take a separator argument" do
127
+ expect(mods_rec.term_value(:note, ' -|-')).to eq('mult1 -|-mult2')
128
+ end
129
+ end
130
+
131
+ context "term_values (multiple values)" do
132
+ it "should return nil if there are no such values in the MODS" do
133
+ expect(mods_rec.term_values(:identifier)).to be_nil
134
+ end
135
+ it "should return nil if there are only empty values in the MODS" do
136
+ expect(mods_rec.term_values(:genre)).to be_nil
137
+ end
138
+ it "should return an array of size one for a single value" do
139
+ expect(mods_rec.term_values(:abstract)).to eq(['single'])
140
+ end
141
+ it "should return an array of values for multiple values" do
142
+ expect(mods_rec.term_values(:note)).to eq(['mult1', 'mult2'])
143
+ end
144
+ it "should work with an Array of messages passed as the argument" do
145
+ expect(mods_rec.term_values([:subject, 'topic'])).to eq(['topic1', 'topic2', 'topic3'])
146
+ end
147
+ it "should work with a String passed as the argument" do
148
+ expect(mods_rec.term_values('abstract')).to eq(['single'])
149
+ end
150
+ it "should raise an error for an unrecognized message symbol" do
151
+ expect { mods_rec.term_values(:not_there) }.to raise_error(ArgumentError, "term_values called with unknown argument: :not_there")
152
+ end
153
+ it "should raise an error if the argument is an Array containing non-symbols" do
154
+ expect { mods_rec.term_values([:subject, mods_rec.subject]) }.to raise_error(ArgumentError, /term_values called with Array containing unrecognized class:.*NodeSet.*/)
155
+ end
156
+ it "should raise an error if the argument isn't a Symbol or an Array" do
157
+ expect { mods_rec.term_values(mods_rec.subject) }.to raise_error(ArgumentError, /term_values called with unrecognized argument class:.*NodeSet.*/)
158
+ end
159
+ end
160
+ end # getting term values
161
+
162
+ context "convenience methods for accessing tricky bits of terminology" do
163
+ context "title methods" do
164
+ it "short_titles should return an Array of Strings (multiple titles are legal in Mods)" do
165
+ record = mods_record("<titleInfo><title>Jerk</title><nonSort>The</nonSort></titleInfo><titleInfo><title>Joke</title></titleInfo>")
166
+ expect(record.short_titles).to eq(["The Jerk", "Joke"])
167
+ end
168
+ it "full_titles should return an Array of Strings (multiple titles are legal in Mods)" do
169
+ record = mods_record("<titleInfo><title>Jerk</title><nonSort>The</nonSort></titleInfo><titleInfo><title>Joke</title></titleInfo>")
170
+ expect(record.full_titles).to eq(["The Jerk", "Joke"])
171
+ end
172
+ it "sort_title should return a String (sortable fields are single valued)" do
173
+ record = mods_record("<titleInfo><title>Jerk</title><subTitle>A Tale of Tourettes</subTitle><nonSort>The</nonSort></titleInfo>")
174
+ expect(record.sort_title).to eq("Jerk A Tale of Tourettes")
175
+ end
176
+ it "alternative_titles should return an Array of Strings (multiple alternative titles when there are multiple titleInfo elements)" do
177
+ record = mods_record("<titleInfo type='alternative'><title>1</title></titleInfo><titleInfo type='alternative'><title>2</title></titleInfo>")
178
+ expect(record.alternative_titles).to eq(['1', '2'])
179
+ record = mods_record("<titleInfo type='alternative'><title>1</title><title>2</title></titleInfo>")
180
+ expect(record.alternative_titles).to eq(['12'])
181
+ end
182
+ end
183
+
184
+ context "personal_names" do
185
+ before(:all) do
186
+ @pers_name = 'Crusty'
187
+ @pers_role = 'creator'
188
+ @given_family = mods_record('<name type="personal"><namePart type="given">Jorge Luis</namePart><namePart type="family">Borges</namePart></name>')
189
+ @given_family_date = mods_record('<name type="personal"><namePart type="given">Zaphod</namePart>
190
+ <namePart type="family">Beeblebrox</namePart>
191
+ <namePart type="date">1912-2362</namePart></name>')
192
+ @all_name_parts = mods_record('<name type="personal"><namePart type="given">Given</namePart>
193
+ <namePart type="family">Family</namePart>
194
+ <namePart type="termsOfAddress">Mr.</namePart>
195
+ <namePart type="date">date</namePart></name>')
196
+ @family_only = mods_record('<name type="personal"><namePart type="family">Family</namePart></name>')
197
+ @given_only = mods_record('<name type="personal"><namePart type="given">Given</namePart></name>')
198
+ end
199
+
200
+ it "should return an Array of Strings" do
201
+ record = mods_record("<name type='personal'><namePart>#{@pers_name}</namePart></name>")
202
+ expect(record.personal_names).to eq([@pers_name])
203
+ end
204
+
205
+ it "should not include the role text" do
206
+ record = mods_record("<name type='personal'><namePart>#{@pers_name}</namePart></name>")
207
+ expect(record.personal_names.first).not_to match(@pers_role)
208
+ end
209
+
210
+ it "should prefer displayForm over namePart pieces" do
211
+ display_form_and_name_parts = mods_record('<name type="personal"><namePart type="given">Jorge Luis</namePart>
212
+ <namePart type="family">Borges</namePart>
213
+ <displayForm>display form</displayForm></name>')
214
+ expect(display_form_and_name_parts.personal_names).to include("display form")
215
+ end
216
+
217
+ it "should put the family namePart first" do
218
+ expect(@given_family.personal_names.first).to match(/^Borges/)
219
+ expect(@given_family_date.personal_names.first).to match(/^Beeblebrox/)
220
+ end
221
+ it "should not include date" do
222
+ expect(@given_family_date.personal_names.first).not_to match(/19/)
223
+ expect(@all_name_parts.personal_names.first).not_to match('date')
224
+ end
225
+ it "should include a comma when there is both a family and a given name" do
226
+ expect(@all_name_parts.personal_names).to include("Family, Given Mr.")
227
+ end
228
+ it "should include multiple words in a namePart" do
229
+ expect(@given_family.personal_names).to include("Borges, Jorge Luis")
230
+ end
231
+ it "should not include a comma when there is only a family or given name" do
232
+ expect(@family_only.personal_names.first).not_to match(/,/)
233
+ expect(@given_only.personal_names.first).not_to match(/,/)
234
+ end
235
+ it "should include terms of address" do
236
+ expect(@all_name_parts.personal_names.first).to match(/Mr./)
237
+ end
238
+ end # personal_names
239
+
240
+ context "personal_names_w_dates" do
241
+ before(:all) do
242
+ @given_family = mods_record('<name type="personal"><namePart type="given">Jorge Luis</namePart>
243
+ <namePart type="family">Borges</namePart></name>')
244
+ @given_family_date = mods_record('<name type="personal"><namePart type="given">Zaphod</namePart>
245
+ <namePart type="family">Beeblebrox</namePart>
246
+ <namePart type="date">1912-2362</namePart></name>')
247
+ @all_name_parts = mods_record('<name type="personal"><namePart type="given">Given</namePart>
248
+ <namePart type="family">Family</namePart>
249
+ <namePart type="termsOfAddress">Mr.</namePart>
250
+ <namePart type="date">date</namePart></name>')
251
+ end
252
+ it "should return an Array of Strings" do
253
+ expect(@given_family_date.personal_names_w_dates).to be_an_instance_of(Array)
254
+ end
255
+ it "should include the date when it is available" do
256
+ expect(@given_family_date.personal_names_w_dates.first).to match(/, 1912-2362$/)
257
+ expect(@all_name_parts.personal_names_w_dates.first).to match(/, date$/)
258
+ end
259
+ it "should be just the personal_name if no date is available" do
260
+ expect(@given_family.personal_names_w_dates.first).to eq('Borges, Jorge Luis')
261
+ end
262
+ end
263
+
264
+ context "corporate_names" do
265
+ before(:all) do
266
+ @corp_name = 'ABC corp'
267
+ end
268
+ it "should return an Array of Strings" do
269
+ record = mods_record("<name type='corporate'><namePart>#{@corp_name}</namePart></name></mods>")
270
+ expect(record.corporate_names).to eq([@corp_name])
271
+ end
272
+ it "should not include the role text" do
273
+ corp_role = 'lithographer'
274
+ mods_w_corp_name_role = mods_record("<name type='corporate'><namePart>#{@corp_name}</namePart>
275
+ <role><roleTerm type='text'>#{corp_role}</roleTerm></role></name>")
276
+ expect(mods_w_corp_name_role.corporate_names.first).not_to match(corp_role)
277
+ end
278
+
279
+ it "should prefer displayForm over namePart pieces" do
280
+ display_form_and_name_parts = mods_record("<name type='corporate'><namePart>Food, Inc.</namePart>
281
+ <displayForm>display form</displayForm></name>")
282
+ expect(display_form_and_name_parts.corporate_names).to include("display form")
283
+ end
284
+ end # corporate_names
285
+ end # convenience methods for tricky bits of terminology
286
+
287
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Mods <relatedItem> Element" do
4
+ context 'with a bibliography' do
5
+ subject(:related_item) do
6
+ mods_record(<<-XML).related_item.first
7
+ <relatedItem displayLabel='Bibliography' type='host'>
8
+ <titleInfo>
9
+ <title/>
10
+ </titleInfo>
11
+ <recordInfo>
12
+ <recordIdentifier source='Gallica ARK'/>
13
+ </recordInfo>
14
+ <typeOfResource>text</typeOfResource>
15
+ </relatedItem>
16
+ XML
17
+ end
18
+
19
+ it 'has the expected attributes' do
20
+ expect(related_item).to have_attributes(
21
+ displayLabel: 'Bibliography',
22
+ type_at: 'host',
23
+ recordInfo: match_array([
24
+ have_attributes(recordIdentifier: match_array([have_attributes(source: 'Gallica ARK')]))
25
+ ]),
26
+ typeOfResource: match_array([have_attributes(text: 'text')])
27
+ )
28
+ end
29
+ end
30
+
31
+ context 'with a related item' do
32
+ subject(:related_items) do
33
+ mods_record(<<-XML).related_item
34
+ <relatedItem>
35
+ <titleInfo>
36
+ <title>Complete atlas, or, Distinct view of the known world</title>
37
+ </titleInfo>
38
+ <name type='personal'>
39
+ <namePart>Bowen, Emanuel,</namePart>
40
+ <namePart type='date'>d. 1767</namePart>
41
+ </name>
42
+ </relatedItem>
43
+ <relatedItem type='host' displayLabel='From:'>
44
+ <titleInfo>
45
+ <title>Complete atlas, or, Distinct view of the known world</title>
46
+ </titleInfo>
47
+ <name>
48
+ <namePart>Bowen, Emanuel, d. 1767.</namePart>
49
+ </name>
50
+ <identifier type='local'>(AuCNL)1669726</identifier>
51
+ </relatedItem>
52
+ XML
53
+ end
54
+
55
+ it 'has the expected attributes' do
56
+ expect(related_items).to match_array([
57
+ have_attributes(
58
+ displayLabel: [],
59
+ titleInfo: match_array([have_attributes(title: have_attributes(text: 'Complete atlas, or, Distinct view of the known world'))]),
60
+ personal_name: match_array([
61
+ have_attributes(type_at: 'personal', display_value: 'Bowen, Emanuel,', date: match_array(have_attributes(text: 'd. 1767')))
62
+ ])
63
+ ),
64
+ have_attributes(
65
+ displayLabel: 'From:',
66
+ type_at: 'host',
67
+ identifier: match_array([
68
+ have_attributes(type_at: 'local', text: '(AuCNL)1669726')
69
+ ])
70
+ )
71
+ ])
72
+ end
73
+ end
74
+
75
+ context 'with a constituent item' do
76
+ subject(:related_item) do
77
+ mods_record(<<-XML).related_item.first
78
+ <relatedItem type='constituent' ID='MODSMD_ARTICLE1'>
79
+ <titleInfo>
80
+ <title>Nuppineula.</title>
81
+ </titleInfo>
82
+ <genre>article</genre>
83
+ <part ID='DIVL15' type='paragraph' order='1'/>
84
+ <part ID='DIVL17' type='paragraph' order='2'/>
85
+ <part ID='DIVL19' type='paragraph' order='3'/>
86
+ </relatedItem>
87
+ XML
88
+ end
89
+
90
+ it 'has the expected attributes' do
91
+ expect(related_item).to have_attributes(
92
+ id_at: 'MODSMD_ARTICLE1',
93
+ type_at: 'constituent',
94
+ titleInfo: match_array([have_attributes(title: have_attributes(text: 'Nuppineula.'))]),
95
+ genre: have_attributes(text: 'article'),
96
+ part: match_array([
97
+ have_attributes(id_at: 'DIVL15', type_at: 'paragraph', order: '1'),
98
+ have_attributes(id_at: 'DIVL17', type_at: 'paragraph', order: '2'),
99
+ have_attributes(id_at: 'DIVL19', type_at: 'paragraph', order: '3')
100
+ ])
101
+ )
102
+ end
103
+ end
104
+
105
+ context 'with a collection' do
106
+ subject(:related_item) do
107
+ mods_record(<<-XML).related_item.first
108
+ <relatedItem type='host'>
109
+ <titleInfo>
110
+ <title>The Collier Collection of the Revs Institute for Automotive Research</title>
111
+ </titleInfo>
112
+ <typeOfResource collection='yes'/>
113
+ </relatedItem>
114
+ XML
115
+ end
116
+
117
+ it 'has the expected attributes' do
118
+ expect(related_item).to have_attributes(
119
+ titleInfo: match_array([have_attributes(title: have_attributes(text: 'The Collier Collection of the Revs Institute for Automotive Research'))]),
120
+ typeOfResource: match_array([have_attributes(collection: 'yes')])
121
+ )
122
+ end
123
+ end
124
+ end