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,217 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Parker manuscript' do
4
+ let(:record) do
5
+ Mods::Record.new.from_file(File.expand_path('../fixture_data/hp566jq8781.xml', __dir__))
6
+ end
7
+
8
+ it 'has title data' do
9
+ expect(record.title_info).to match_array(
10
+ [
11
+ have_attributes(
12
+ authority: 'Corpus Christi College',
13
+ title: have_attributes(text: 'Cambridge, Corpus Christi College, MS 367: Miscellaneous Compilation including Old English Material and Historical and Philosophical Works')
14
+ ),
15
+ have_attributes(
16
+ authority: 'James Catalog',
17
+ type_at: 'alternative',
18
+ title: have_attributes(text: 'Chronica. Anglo-Saxon fragments, etc.')
19
+ )
20
+ ]
21
+ )
22
+
23
+ expect(record.title_info.full_title).to match_array([
24
+ "Cambridge, Corpus Christi College, MS 367: Miscellaneous Compilation including Old English Material and Historical and Philosophical Works",
25
+ "Chronica. Anglo-Saxon fragments, etc."
26
+ ])
27
+
28
+ expect(record.title_info.short_title).to eq [
29
+ "Cambridge, Corpus Christi College, MS 367: Miscellaneous Compilation including Old English Material and Historical and Philosophical Works"
30
+ ]
31
+
32
+ expect(record.title_info.sort_title).to eq [
33
+ "Cambridge, Corpus Christi College, MS 367: Miscellaneous Compilation including Old English Material and Historical and Philosophical Works"
34
+ ]
35
+
36
+ expect(record.title_info.alternative_title).to eq [
37
+ "Chronica. Anglo-Saxon fragments, etc."
38
+ ]
39
+ end
40
+
41
+
42
+ it 'has typeOfResource data' do
43
+ expect(record.typeOfResource).to match_array([
44
+ have_attributes(
45
+ manuscript: 'yes',
46
+ text: 'mixed material'
47
+ )
48
+ ])
49
+ end
50
+
51
+ it 'has originInfo data' do
52
+ expect(record.origin_info.to_a).to include(
53
+ have_attributes(
54
+ dateCreated: match_array([
55
+ have_attributes(
56
+ encoding: 'w3cdtf',
57
+ keyDate: 'yes',
58
+ point: 'start',
59
+ qualifier: 'approximate',
60
+ text: '1400'
61
+ ),
62
+ have_attributes(
63
+ text: '1499'
64
+ )
65
+ ]),
66
+ ),
67
+ have_attributes(
68
+ dateCreated: match_array([
69
+ have_attributes(
70
+ text: '1300'
71
+ ),
72
+ have_attributes(
73
+ text: '1399'
74
+ )
75
+ ])
76
+ )
77
+ )
78
+ end
79
+
80
+ it 'has abstract data' do
81
+ expect(record.abstract).to match_array([
82
+ have_attributes(
83
+ displayLabel: 'Summary',
84
+ type_at: 'summary',
85
+ text: a_string_starting_with('CCCC MS 367')
86
+ )
87
+ ])
88
+ end
89
+
90
+ it 'has table of contents data' do
91
+ expect(record.tableOfContents).to match_array([
92
+ have_attributes(
93
+ displayLabel: 'Contents',
94
+ text: a_string_starting_with('Polychronicon (epitome and continuation to 1429)')
95
+ )
96
+ ])
97
+ end
98
+
99
+ it 'has notes' do
100
+ expect(record.note).to match_array([
101
+ have_attributes(
102
+ displayLabel: 'M.R. James Date',
103
+ type_at: 'date',
104
+ text: 'xv, xi-xii, xiv, xi'
105
+ )
106
+ ])
107
+ end
108
+
109
+ it 'has languages' do
110
+ expect(record.language.length).to eq 2
111
+ expect(record.language.code_term).to match_array([
112
+ have_attributes(
113
+ authority: 'iso639-2b',
114
+ text: 'lat',
115
+ type_at: 'code'
116
+ ),
117
+ have_attributes(
118
+ text: 'ang'
119
+ ),
120
+ ])
121
+ expect(record.language.text_term).to be_empty
122
+ end
123
+
124
+ it 'has a physical description' do
125
+ expect(record.physical_description.first).to have_attributes(
126
+ note: array_including(
127
+ have_attributes(
128
+ displayLabel: 'Material',
129
+ type_at: 'material',
130
+ text: 'Paper and vellum'
131
+ ),
132
+ have_attributes(
133
+ text: 'five volumes'
134
+ ),
135
+ have_attributes(
136
+ text: '220'
137
+ )
138
+ ),
139
+ extent: match_array([
140
+ have_attributes(text: 'ff. 53 + 52')
141
+ ])
142
+ )
143
+ end
144
+
145
+ it 'has identifiers' do
146
+ expect(record.identifier.to_a).to include(
147
+ have_attributes(
148
+ text: '342',
149
+ displayLabel: 'TJames'
150
+ ),
151
+ have_attributes(
152
+ text: '19. 9',
153
+ displayLabel: 'Stanley'
154
+ )
155
+ )
156
+ end
157
+
158
+ it 'has a location' do
159
+ expect(record.location.to_a).to include(
160
+ have_attributes(
161
+ physicalLocation: have_attributes(
162
+ text: 'UK, Cambridge, Corpus Christi College, Parker Library',
163
+ type_at: ['repository']
164
+ ),
165
+ shelfLocator: have_attributes(
166
+ text: 'MS 367'
167
+ )
168
+ )
169
+ )
170
+ end
171
+
172
+ it 'has related items' do
173
+ expect(record.related_item.to_a).to include(
174
+ have_attributes(
175
+ displayLabel: 'Downloadable James Catalogue Record',
176
+ type_at: 'isReferencedBy',
177
+ titleInfo: array_including(
178
+ have_attributes(
179
+ title: have_attributes(
180
+ text: a_string_starting_with('https://stacks.stanford.edu')
181
+ )
182
+ )
183
+ )
184
+ ),
185
+ have_attributes(
186
+ type_at: 'constituent',
187
+ titleInfo: array_including([
188
+ have_attributes(
189
+ title: [
190
+ have_attributes(text: 'Ranulf Higden OSB, Polychronicon (epitome and continuation to 1429)')
191
+ ],
192
+ partNumber: [
193
+ have_attributes(text: '1r-29v')
194
+ ]
195
+ )
196
+ ]),
197
+ note: array_including(
198
+ have_attributes(text: 'Dates are marked in the margin'),
199
+ have_attributes(type_at: 'incipit', text: a_string_starting_with('(1r) Ieronimus'))
200
+ )
201
+ )
202
+ )
203
+ end
204
+
205
+ it 'has access conditions' do
206
+ expect(record.accessCondition).to match_array([
207
+ have_attributes(
208
+ type_at: 'useAndReproduction',
209
+ text: a_string_starting_with('Images courtesy')
210
+ ),
211
+ have_attributes(
212
+ type_at: 'license',
213
+ text: a_string_starting_with('CC by-nc: Attribution')
214
+ )
215
+ ])
216
+ end
217
+ end
@@ -209,6 +209,9 @@ RSpec.describe Mods::Date do
209
209
  '1900-uu-uu' => Date.parse('1900-01-01')..Date.parse('1900-12-31'),
210
210
  '1900-uu-15' => Date.parse('1900-01-15')..Date.parse('1900-12-15'),
211
211
  '1900-06-uu' => Date.parse('1900-06-01')..Date.parse('1900-06-30'),
212
+ '-250' => Date.parse('-250-01-01')..Date.parse('-250-12-31'), # EDTF requires a 4 digit year, but what can you do.
213
+ '63' => Date.parse('0063-01-01')..Date.parse('0063-12-31'),
214
+ '125' => Date.parse('125-01-01')..Date.parse('125-12-31'),
212
215
  }.each do |data, expected|
213
216
  describe "with #{data}" do
214
217
  let(:date_element) { "<dateCreated encoding=\"edtf\">#{data}</dateCreated>" }
@@ -226,6 +229,7 @@ RSpec.describe Mods::Date do
226
229
  {
227
230
  '1753' => Date.parse('1753-01-01')..Date.parse('1753-12-31'),
228
231
  '-1753' => Date.parse('-1753-01-01')..Date.parse('-1753-12-31'),
232
+ '1992-00-00' => Date.parse('1992-01-01')..Date.parse('1992-12-31'),
229
233
  '1992-05-06' => Date.parse('1992-05-06')..Date.parse('1992-05-06'),
230
234
  '1992-04' => Date.parse('1992-04-01')..Date.parse('1992-04-30'),
231
235
  '2004-02' => Date.parse('2004-02-01')..Date.parse('2004-02-29'),
@@ -303,7 +307,8 @@ RSpec.describe Mods::Date do
303
307
  {
304
308
  'Minguo 19 [1930]' => Date.parse('1930-01-01')..Date.parse('1930-12-31'),
305
309
  '1745 mag. 14' => Date.parse('1745-01-01')..Date.parse('1745-12-31'),
306
- '-745' => Date.parse('-745-01-01')..Date.parse('-745-12-31'),
310
+ '-745' => '', # too ambiguious to even attempt.
311
+ '-1999' => '', # too ambiguious to even attempt.
307
312
  '[1923]' => Date.parse('1923-01-01')..Date.parse('1923-12-31'),
308
313
  '1532.' => Date.parse('1532-01-01')..Date.parse('1532-12-31'),
309
314
  '[ca 1834]' => Date.parse('1834-01-01')..Date.parse('1834-12-31'),
@@ -319,8 +324,11 @@ RSpec.describe Mods::Date do
319
324
  '193-' => Date.parse('1930-01-01')..Date.parse('1939-12-31'),
320
325
  '196_' => Date.parse('1960-01-01')..Date.parse('1969-12-31'),
321
326
  '196x' => Date.parse('1960-01-01')..Date.parse('1969-12-31'),
327
+ '196u' => Date.parse('1960-01-01')..Date.parse('1969-12-31'),
328
+ '1960s' => Date.parse('1960-01-01')..Date.parse('1969-12-31'),
322
329
  '186?' => Date.parse('1860-01-01')..Date.parse('1869-12-31'),
323
330
  '1700?' => Date.parse('1700-01-01')..Date.parse('1700-12-31'),
331
+ 'early 1730s' => Date.parse('1730-01-01')..Date.parse('1739-12-31'),
324
332
  '[1670-1684]' => Date.parse('1670-01-01')..Date.parse('1684-12-31'),
325
333
  '[18]74' => Date.parse('1874-01-01')..Date.parse('1874-12-31'),
326
334
  '250 B.C.' => Date.parse('-0249-01-01')..Date.parse('-249-12-31'),
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe 'Mods <language> Element' do
4
+ context 'with a simple record one with language term' do
5
+ subject(:record) do
6
+ mods_record("<language><languageTerm authority='iso639-2b' type='code'>fre</languageTerm></language>")
7
+ end
8
+
9
+ describe '#languages' do
10
+ it 'translates the code into text' do
11
+ expect(record.languages).to eq(['French'])
12
+ end
13
+ end
14
+
15
+ describe '#language' do
16
+ describe '#languageTerm' do
17
+ it 'has attribute accessors' do
18
+ expect(record.language.languageTerm).to have_attributes(
19
+ type_at: ['code'],
20
+ authority: ['iso639-2b'],
21
+ text: 'fre',
22
+ size: 1
23
+ )
24
+ end
25
+ end
26
+
27
+ describe '#code_term' do
28
+ it 'gets the languageTerms with type="code"' do
29
+ expect(record.language.code_term.map(&:text)).to eq ['fre']
30
+ end
31
+ end
32
+
33
+ describe '#text_term' do
34
+ it 'filters out the languageTerms with type="code"' do
35
+ expect(record.language.text_term).to be_empty
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ context 'with a record with a mix of language codes and plain-text languages' do
42
+ subject(:record) do
43
+ mods_record(<<-XML)
44
+ <language>
45
+ <languageTerm authority='iso639-2b' type='code'>spa</languageTerm>
46
+ <languageTerm authority='iso639-2b' type='text'>Spanish</languageTerm>
47
+ <languageTerm authority='iso639-2b' type='code'>dut</languageTerm>
48
+ <languageTerm authority='iso639-2b' type='text'>Dutch</languageTerm>
49
+ </language>
50
+ XML
51
+ end
52
+
53
+ describe '#languages' do
54
+ it 'translates the code into text' do
55
+ expect(record.languages).to include 'Spanish; Castilian', 'Dutch; Flemish'
56
+ end
57
+
58
+ it 'passes thru language values that are already text (not code)' do
59
+ expect(record.languages).to include 'Spanish', 'Dutch'
60
+ end
61
+ end
62
+
63
+ describe '#language' do
64
+ describe '#code_term' do
65
+ it 'gets the languageTerms with type="code"' do
66
+ expect(record.language.code_term.map(&:text)).to eq ['spa', 'dut']
67
+ end
68
+ end
69
+
70
+ describe '#text_term' do
71
+ it 'filters out the languageTerms with type="code"' do
72
+ expect(record.language.text_term.map(&:text)).to eq %w[Spanish Dutch]
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'with a record with multiple language codes in one element' do
79
+ subject(:record) do
80
+ mods_record(<<-XML)
81
+ <language>
82
+ <languageTerm authority='iso639-2b' type='code'>per ara, dut</languageTerm>
83
+ </language>
84
+ XML
85
+ end
86
+
87
+ describe '#languages' do
88
+ it 'creates a separate value for each language in a comma, space, or | separated list' do
89
+ expect(record.languages).to include 'Arabic', 'Persian', 'Dutch; Flemish'
90
+ end
91
+ end
92
+ end
93
+
94
+ context 'with a record without a languageTerm child' do
95
+ subject(:record) do
96
+ mods_record(<<-XML)
97
+ <language>Greek</language>
98
+ XML
99
+ end
100
+
101
+ describe '#languages' do
102
+ it 'preserves values that are not inside <languageTerm> elements' do
103
+ expect(mods_record('<language>Greek</language>').languages).to eq(['Greek'])
104
+ end
105
+ end
106
+ end
107
+
108
+ context 'wih a record with some authority attributes' do
109
+ subject(:record) do
110
+ mods_record(<<-XML)
111
+ <language><languageTerm authorityURI='http://example.com' valueURI='http://example.com/zzz'>zzz</languageTerm></language>
112
+ XML
113
+ end
114
+
115
+ describe '#language' do
116
+ describe '#languageTerm' do
117
+ it 'recognizes other authority attributes' do
118
+ expect(record.language.languageTerm).to have_attributes(authorityURI: ['http://example.com'], valueURI: ['http://example.com/zzz'])
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Mods <location> Element" do
4
+ context 'with a record with a physical location' do
5
+ subject(:record) do
6
+ mods_record("<location><physicalLocation>here</physicalLocation></location>")
7
+ end
8
+
9
+ it "should have access to text value of element" do
10
+ expect(record.location.physicalLocation.text).to eq("here")
11
+ end
12
+ end
13
+
14
+ context 'with a record with a physical location that has an authority' do
15
+ subject(:record) do
16
+ mods_record("<location><physicalLocation authority='marcorg'>MnRM</physicalLocation></location>")
17
+ end
18
+
19
+ it "should have access to text value of element" do
20
+ expect(record.location.physicalLocation.map { |n| n.text }).to eq(["MnRM"])
21
+ end
22
+
23
+ it "should recognize authority attribute" do
24
+ expect(record.location.physicalLocation.authority).to eq(["marcorg"])
25
+ end
26
+ end
27
+
28
+ context 'with a record with a displayLabel' do
29
+ subject(:record) do
30
+ mods_record("<location><physicalLocation displayLabel='Correspondence'>some address</physicalLocation></location>")
31
+ end
32
+
33
+ it "should recognize displayLabel attribute" do
34
+ expect(record.location.physicalLocation.displayLabel).to eq(["Correspondence"])
35
+ end
36
+ end
37
+
38
+ context 'with a record with a shelfLocator' do
39
+ subject(:record) do
40
+ mods_record(<<-XML)
41
+ <location>
42
+ <physicalLocation>Library of Congress </physicalLocation>
43
+ <shelfLocator>DAG no. 1410</shelfLocator>
44
+ </location>
45
+ XML
46
+ end
47
+
48
+ it "has a shelfLocator child element" do
49
+ expect(record.location.shelfLocator.map { |n| n.text }).to eq(["DAG no. 1410"])
50
+ end
51
+ end
52
+
53
+ context 'with a record with a url location' do
54
+ let(:url_attribs) do
55
+ mods_record(<<-XML).location.url
56
+ <location>
57
+ <url
58
+ displayLabel='Digital collection of 46 images available online'
59
+ usage='primary display'
60
+ note='something'
61
+ dateLastAccessed='2021-12-21'>
62
+ http://searchworks.stanford.edu/?f%5Bcollection%5D%5B%5D=The+Reid+W.+Dennis+Collection+of+California+Lithographs&amp;view=gallery
63
+ </url>
64
+ </location>
65
+ XML
66
+ end
67
+
68
+ it 'has the right attributes' do
69
+ expect(url_attribs).to have_attributes(
70
+ displayLabel: ['Digital collection of 46 images available online'],
71
+ usage: ['primary display'],
72
+ note: ['something'],
73
+ dateLastAccessed: ['2021-12-21']
74
+ )
75
+ end
76
+ end
77
+
78
+ context 'with a record with multiple urls' do
79
+ let(:mult_flavor_loc_urls) do
80
+ mods_record(<<-XML).location.url
81
+ <location>
82
+ <url access='preview'>http://preview.org</url>
83
+ <url access='object in context'>http://context.org</url>
84
+ <url access='raw object'>http://object.org</url>
85
+ </location>
86
+ XML
87
+ end
88
+
89
+ describe '#url' do
90
+ it 'has access to text value of element' do
91
+ expect(mult_flavor_loc_urls.map(&:text)).to eq ["http://preview.org", "http://context.org", "http://object.org"]
92
+ end
93
+
94
+ describe '#access' do
95
+ it 'provides access to the access attributes' do
96
+ expect(mult_flavor_loc_urls.access).to eq ['preview', 'object in context', 'raw object']
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ context 'with an empty url element' do
103
+ let(:empty_url) do
104
+ mods_record(<<-XML).location.url
105
+ <location><url /></location>
106
+ XML
107
+ end
108
+
109
+ it 'is an empty string for single empty url element' do
110
+ expect(empty_url.text).to be_empty
111
+ end
112
+ end
113
+
114
+ context 'with a record with holdingSimple data' do
115
+ subject(:record) do
116
+ mods_record(<<-XML)
117
+ <location>
118
+ <physicalLocation authority='marcorg'>MnRM</physicalLocation>
119
+ <holdingSimple>
120
+ <copyInformation>
121
+ <subLocation>Patient reading room</subLocation>
122
+ <shelfLocator>QH511.A1J68</shelfLocator>
123
+ <enumerationAndChronology unitType='1'> v.1-v.8 1970-1976</enumerationAndChronology>
124
+ </copyInformation>
125
+ </holdingSimple>
126
+ </location>
127
+ XML
128
+ end
129
+
130
+ it "has aholdingSimple child element" do
131
+ expect(record.location.holdingSimple.copyInformation.first).to have_attributes(
132
+ sub_location: have_attributes(text: 'Patient reading room'),
133
+ shelf_locator: have_attributes(text: 'QH511.A1J68'),
134
+ enumeration_and_chronology: have_attributes(text: ' v.1-v.8 1970-1976', unitType: ['1'])
135
+ )
136
+ end
137
+ end
138
+
139
+
140
+ context 'with a record with holdingComplex data' do
141
+ subject(:record) do
142
+ mods_record(<<-XML)
143
+ <location>
144
+ <physicalLocation>Menlo Park Public Library</physicalLocation>
145
+ <holdingExternal>
146
+ <holding xmlns='info:ofi/fmt:xml:xsd:iso20775' xsi:schemaLocation='info:ofi/fmt:xml:xsd:iso20775 http://www.loc.gov/standards/iso20775/N130_ISOholdings_v6_1.xsd'>
147
+ <institutionIdentifier>
148
+ <value>JRF</value>
149
+ <typeOrSource>
150
+ <pointer>http://worldcat.org/registry/institutions/</pointer>
151
+ </typeOrSource>
152
+ </institutionIdentifier>
153
+ <physicalLocation>Menlo Park Public Library</physicalLocation>
154
+ <physicalAddress>
155
+ <text>Menlo Park, CA 94025 United States </text>
156
+ </physicalAddress>
157
+ <electronicAddress>
158
+ <text>http://www.worldcat.org/wcpa/oclc/15550774? page=frame&amp;url=%3D%3FUTF-8%3FB%FaHR0cDovL2NhdGFsb2cucGxzaW5mby5vcmcvc2VhcmNoL2kwMTk1MDM4NjMw%3F%3D&amp;title=Menlo+Park+Public+Library&amp;linktype=opac&amp;detail=JRF%3AMenlo+Park+Public+Library%3APublic&amp;app=wcapi&amp;id=OCL-OCLC+Staff+use</text>
159
+ </electronicAddress>
160
+ <holdingSimple>
161
+ <copiesSummary>
162
+ <copiesCount>1</copiesCount>
163
+ </copiesSummary>
164
+ </holdingSimple>
165
+ </holding>
166
+ </holdingExternal>
167
+ </location>
168
+ XML
169
+ end
170
+
171
+ it "has a holdingComplex child element" do
172
+ expect(record.location.holdingExternal.xpath('//h:holding/h:physicalLocation', h: 'info:ofi/fmt:xml:xsd:iso20775').map(&:text)).to eq ['Menlo Park Public Library']
173
+ end
174
+ end
175
+ end