mods 2.4.0 → 3.0.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.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ruby.yml +24 -0
  3. data/.gitignore +1 -0
  4. data/Gemfile +0 -4
  5. data/README.md +1 -3
  6. data/lib/mods/date.rb +51 -17
  7. data/lib/mods/marc_country_codes.rb +12 -10
  8. data/lib/mods/nom_terminology.rb +110 -849
  9. data/lib/mods/reader.rb +9 -39
  10. data/lib/mods/record.rb +13 -28
  11. data/lib/mods/version.rb +1 -1
  12. data/mods.gemspec +3 -3
  13. data/spec/fixture_data/hp566jq8781.xml +334 -0
  14. data/spec/integration/parker_spec.rb +217 -0
  15. data/spec/{date_spec.rb → lib/date_spec.rb} +8 -1
  16. data/spec/lib/language_spec.rb +123 -0
  17. data/spec/lib/location_spec.rb +175 -0
  18. data/spec/lib/name_spec.rb +368 -0
  19. data/spec/lib/origin_info_spec.rb +134 -0
  20. data/spec/lib/part_spec.rb +162 -0
  21. data/spec/lib/physical_description_spec.rb +72 -0
  22. data/spec/{reader_spec.rb → lib/reader_spec.rb} +1 -41
  23. data/spec/lib/record_info_spec.rb +114 -0
  24. data/spec/lib/record_spec.rb +287 -0
  25. data/spec/lib/related_item_spec.rb +124 -0
  26. data/spec/lib/subject_spec.rb +427 -0
  27. data/spec/lib/title_spec.rb +108 -0
  28. data/spec/lib/top_level_elmnts_simple_spec.rb +169 -0
  29. data/spec/spec_helper.rb +87 -6
  30. data/spec/support/fixtures.rb +9 -0
  31. metadata +61 -43
  32. data/.coveralls.yml +0 -1
  33. data/.travis.yml +0 -6
  34. data/spec/language_spec.rb +0 -118
  35. data/spec/location_spec.rb +0 -295
  36. data/spec/name_spec.rb +0 -759
  37. data/spec/origin_info_spec.rb +0 -447
  38. data/spec/part_spec.rb +0 -471
  39. data/spec/physical_description_spec.rb +0 -144
  40. data/spec/record_info_spec.rb +0 -493
  41. data/spec/record_spec.rb +0 -356
  42. data/spec/related_item_spec.rb +0 -305
  43. data/spec/subject_spec.rb +0 -809
  44. data/spec/title_spec.rb +0 -226
  45. 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