pennmarc 0.0.1

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +119 -0
  7. data/README.md +82 -0
  8. data/legacy/indexer.rb +568 -0
  9. data/legacy/marc.rb +2964 -0
  10. data/legacy/test_file_output.json +49 -0
  11. data/lib/pennmarc/encoding_level.rb +43 -0
  12. data/lib/pennmarc/enriched_marc.rb +36 -0
  13. data/lib/pennmarc/heading_control.rb +11 -0
  14. data/lib/pennmarc/helpers/citation.rb +31 -0
  15. data/lib/pennmarc/helpers/creator.rb +237 -0
  16. data/lib/pennmarc/helpers/database.rb +89 -0
  17. data/lib/pennmarc/helpers/date.rb +85 -0
  18. data/lib/pennmarc/helpers/edition.rb +90 -0
  19. data/lib/pennmarc/helpers/format.rb +312 -0
  20. data/lib/pennmarc/helpers/genre.rb +71 -0
  21. data/lib/pennmarc/helpers/helper.rb +11 -0
  22. data/lib/pennmarc/helpers/identifier.rb +134 -0
  23. data/lib/pennmarc/helpers/language.rb +37 -0
  24. data/lib/pennmarc/helpers/link.rb +12 -0
  25. data/lib/pennmarc/helpers/location.rb +97 -0
  26. data/lib/pennmarc/helpers/note.rb +132 -0
  27. data/lib/pennmarc/helpers/production.rb +131 -0
  28. data/lib/pennmarc/helpers/relation.rb +135 -0
  29. data/lib/pennmarc/helpers/series.rb +118 -0
  30. data/lib/pennmarc/helpers/subject.rb +304 -0
  31. data/lib/pennmarc/helpers/title.rb +197 -0
  32. data/lib/pennmarc/mappings/language.yml +516 -0
  33. data/lib/pennmarc/mappings/locations.yml +1801 -0
  34. data/lib/pennmarc/mappings/relator.yml +263 -0
  35. data/lib/pennmarc/parser.rb +177 -0
  36. data/lib/pennmarc/util.rb +240 -0
  37. data/lib/pennmarc.rb +6 -0
  38. data/pennmarc.gemspec +22 -0
  39. data/spec/fixtures/marcxml/test.xml +167 -0
  40. data/spec/lib/pennmarc/helpers/citation_spec.rb +27 -0
  41. data/spec/lib/pennmarc/helpers/creator_spec.rb +183 -0
  42. data/spec/lib/pennmarc/helpers/database_spec.rb +60 -0
  43. data/spec/lib/pennmarc/helpers/date_spec.rb +105 -0
  44. data/spec/lib/pennmarc/helpers/edition_spec.rb +38 -0
  45. data/spec/lib/pennmarc/helpers/format_spec.rb +200 -0
  46. data/spec/lib/pennmarc/helpers/genre_spec.rb +89 -0
  47. data/spec/lib/pennmarc/helpers/identifer_spec.rb +105 -0
  48. data/spec/lib/pennmarc/helpers/language_spec.rb +30 -0
  49. data/spec/lib/pennmarc/helpers/location_spec.rb +70 -0
  50. data/spec/lib/pennmarc/helpers/note_spec.rb +233 -0
  51. data/spec/lib/pennmarc/helpers/production_spec.rb +193 -0
  52. data/spec/lib/pennmarc/helpers/relation_spec.rb +120 -0
  53. data/spec/lib/pennmarc/helpers/subject_spec.rb +262 -0
  54. data/spec/lib/pennmarc/helpers/title_spec.rb +169 -0
  55. data/spec/lib/pennmarc/marc_util_spec.rb +206 -0
  56. data/spec/lib/pennmarc/parser_spec.rb +13 -0
  57. data/spec/spec_helper.rb +104 -0
  58. data/spec/support/marc_spec_helpers.rb +84 -0
  59. metadata +171 -0
@@ -0,0 +1,262 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'PennMARC::Subject' do
4
+ include MarcSpecHelpers
5
+
6
+ let(:helper) { PennMARC::Subject }
7
+ let(:relator_map) do
8
+ { dpc: 'Depicted' }
9
+ end
10
+
11
+ describe '.search' do
12
+ let(:record) { marc_record fields: fields }
13
+ let(:values) { helper.search(record, relator_map) }
14
+
15
+ context 'with a mix of included and excluded tags' do
16
+ let(:fields) do
17
+ [marc_field(tag: '600', indicator2: '5', subfields: { a: 'Excluded Canadian' }),
18
+ marc_field(tag: '610', indicator2: '0', subfields: { a: 'University of Pennsylvania', b: 'Libraries' }),
19
+ marc_field(tag: '691', indicator2: '7', subfields: { a: 'Van Pelt Library', '2': 'local' }),
20
+ marc_field(tag: '696', indicator2: '4', subfields: { a: 'A Developer' }),
21
+ marc_field(tag: '880', indicator2: '0', subfields: { a: 'Alt. Name', '6': '610' })]
22
+ end
23
+
24
+ it 'includes only values from valid tags' do
25
+ expect(values).to contain_exactly 'A Developer', 'Alt. Name', 'University of Pennsylvania Libraries',
26
+ 'Van Pelt Library local'
27
+ expect(values).not_to include 'Excluded Canadian'
28
+ end
29
+ end
30
+
31
+ context 'with PRO/CHR values in sf a' do
32
+ let(:fields) do
33
+ [marc_field(tag: '650', indicator2: '4', subfields: { a: 'PRO Subject' }),
34
+ marc_field(tag: '650', indicator2: '4', subfields: { a: '%CHR Heading' })]
35
+ end
36
+
37
+ it 'removes the PRO/CHR designation' do
38
+ expect(values).to contain_exactly 'Subject', 'Heading'
39
+ end
40
+ end
41
+
42
+ context 'with a question mark at the end of sf a' do
43
+ let(:fields) do
44
+ [marc_field(tag: '650', indicator2: '4', subfields: { a: 'Potential Subject?' })]
45
+ end
46
+
47
+ it 'removes the question mark' do
48
+ expect(values).to contain_exactly 'Potential Subject'
49
+ end
50
+ end
51
+
52
+ context 'with a relator code specified' do
53
+ let(:fields) do
54
+ [marc_field(tag: '650', indicator2: '4', subfields: { a: 'Unicorns', '4': 'dpc' })]
55
+ end
56
+
57
+ it 'includes both the relator code and the mapped value, if found' do
58
+ expect(values.first).to eq 'Unicorns dpc Depicted'
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '.facet' do
64
+ let(:record) { marc_record fields: }
65
+ let(:values) { helper.facet(record) }
66
+
67
+ # TODO: find some more inspiring examples in the corpus
68
+ context 'for a record with poorly-coded heading values' do
69
+ let(:fields) { [marc_field(tag: '650', indicator2: '0', subfields: { a: 'Subject - Heading' })] }
70
+
71
+ it 'properly normalizes the heading value' do
72
+ expect(values.first).to eq 'Subject--Heading'
73
+ end
74
+ end
75
+
76
+ context 'for a record with 650 headings with a ǂa that starts with PRO or CHR' do
77
+ let(:fields) do
78
+ [marc_field(tag: '650', indicator2: '4', subfields: { a: '%CHR 1998', '5': 'PU' }),
79
+ marc_field(tag: '650', indicator2: '4', subfields: { a: 'PRO Potok, Adena (donor) (Potok Collection copy)',
80
+ '5': 'PU' })]
81
+ end
82
+
83
+ it 'does not include the headings' do
84
+ expect(values).to be_empty
85
+ end
86
+ end
87
+
88
+ context 'for a record with an indicator2 value of 3, 5 or 6' do
89
+ let(:fields) do
90
+ [marc_field(tag: '650', indicator2: '3', subfields: { a: 'Nope' }),
91
+ marc_field(tag: '650', indicator2: '5', subfields: { a: 'Nope' }),
92
+ marc_field(tag: '650', indicator2: '6', subfields: { a: 'Nope' })]
93
+ end
94
+
95
+ it 'does not include the headings' do
96
+ expect(values).to be_empty
97
+ end
98
+ end
99
+
100
+ context 'for a record with a valid tag, indicator2 and source specified' do
101
+ let(:fields) do
102
+ [marc_field(tag: '650', indicator2: '7',
103
+ subfields: {
104
+ a: 'Libraries', x: 'History', e: 'relator', d: '22nd Century',
105
+ '2': 'fast', '0': 'http://fast.org/libraries'
106
+ })]
107
+ end
108
+
109
+ it 'properly concatenates heading components' do
110
+ expect(values.first).to include 'Libraries--History'
111
+ end
112
+
113
+ it 'excludes URI values from ǂ0 or ǂ1' do
114
+ expect(values.first).not_to include 'http'
115
+ end
116
+
117
+ it 'excludes raw relator term values from ǂe' do
118
+ expect(values.first).not_to include 'relator'
119
+ end
120
+
121
+ it 'includes active dates from ǂd' do
122
+ expect(values.first).to include '22nd Century'
123
+ end
124
+
125
+ it 'joins all values in the expected way' do
126
+ expect(values.first).to eq 'Libraries--History 22nd Century'
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '.show' do
132
+ let(:record) { marc_record fields: }
133
+ let(:values) { helper.facet(record) }
134
+
135
+ context 'with a variety of headings' do
136
+ let(:fields) do
137
+ [marc_field(tag: '650', indicator2: '0', subfields: { a: 'Nephrology', v: 'Periodicals' }),
138
+ marc_field(tag: '650', indicator2: '7',
139
+ subfields: { a: 'Nephrology', '2': 'fast', '0': '(OCoLC)fst01035991' }),
140
+ marc_field(tag: '650', indicator2: '7', subfields: { a: 'Undesirable Heading', '2': 'exclude' }),
141
+ marc_field(tag: '650', indicator2: '2', subfields: { a: 'Nephrology' }),
142
+ marc_field(tag: '650', indicator2: '1', subfields: { a: 'Kidney Diseases' }),
143
+ marc_field(tag: '690', subfields: { a: 'Local Heading' }),
144
+ marc_field(tag: '690', subfields: { a: 'Local Heading' })]
145
+ end
146
+
147
+ it 'shows all valid subject headings without duplicates' do
148
+ expect(helper.show(record)).to match_array ['Nephrology--Periodicals', 'Nephrology',
149
+ 'Kidney Diseases', 'Local Heading']
150
+ end
151
+ end
152
+
153
+ context 'with a robust 650 heading including many subfields' do
154
+ let(:fields) do
155
+ [marc_field(tag: '650', indicator2: '0', subfields: {
156
+ a: 'Subways',
157
+ z: ['Pennsylvania', 'Philadelphia Metropolitan Area'],
158
+ v: 'Maps',
159
+ y: '1989',
160
+ e: 'relator'
161
+ })]
162
+ end
163
+
164
+ it 'properly formats the heading parts' do
165
+ expect(values.first).to eq 'Subways--Pennsylvania--Philadelphia Metropolitan Area--Maps--1989'
166
+ expect(values.first).not_to include 'relator'
167
+ end
168
+ end
169
+
170
+ context 'with a robust 651 heading including many subfields' do
171
+ let(:fields) do
172
+ [marc_field(tag: '651', indicator2: '4', subfields: {
173
+ a: 'Chicago (Ill.)',
174
+ x: 'Moral conditions',
175
+ '3': 'Church minutes',
176
+ y: '1875-1878',
177
+ '0': 'http://some.uri/zzz'
178
+ })]
179
+ end
180
+
181
+ it 'properly formats the heading parts' do
182
+ expect(values.first).to eq 'Chicago (Ill.)--Moral conditions--Church minutes--1875-1878'
183
+ end
184
+ end
185
+
186
+ context 'with a robust 611 heading including many subfields' do
187
+ let(:fields) do
188
+ [marc_field(tag: '611', indicator2: '0', subfields: {
189
+ a: 'Conference',
190
+ d: '(2002',
191
+ n: '2nd',
192
+ c: ['Johannesburg, South Africa', 'Cape Town, South Africa)']
193
+ })]
194
+ end
195
+
196
+ it 'properly formats the heading parts' do
197
+ expect(values.first).to eq 'Conference--2nd (2002 Johannesburg, South Africa Cape Town, South Africa)'
198
+ end
199
+ end
200
+
201
+ context 'with a robust 600 heading including many subfields' do
202
+ let(:fields) do
203
+ [marc_field(tag: '600', indicator2: '0', subfields: {
204
+ a: 'Person, Significant Author',
205
+ d: '1899-1971',
206
+ v: 'Early works to 1950',
207
+ t: 'Collection'
208
+ })]
209
+ end
210
+
211
+ it 'properly formats the heading parts' do
212
+ expect(values.first).to eq 'Person, Significant Author--Early works to 1950 1899-1971 Collection'
213
+ end
214
+ end
215
+ end
216
+
217
+ describe '.childrens_show' do
218
+ let(:record) do
219
+ marc_record(fields: [
220
+ marc_field(tag: '650', indicator2: '1', subfields: { a: 'Frogs', v: 'Fiction' }),
221
+ marc_field(tag: '650', indicator2: '1', subfields: { a: 'Toads', v: 'Fiction' }),
222
+ marc_field(tag: '650', indicator2: '2', subfields: { a: 'Herpetology' })
223
+ ])
224
+ end
225
+ let(:values) { helper.childrens_show(record) }
226
+
227
+ it 'includes heading terms only from subject tags with an indicator 2 of "1"' do
228
+ expect(values).to contain_exactly 'Frogs--Fiction', 'Toads--Fiction'
229
+ end
230
+ end
231
+
232
+ describe '.medical_show' do
233
+ let(:record) do
234
+ marc_record(
235
+ fields: [
236
+ marc_field(tag: '650', indicator2: '0', subfields: { a: 'Nephhrology', v: 'Periodicals' }),
237
+ marc_field(tag: '650', indicator2: '7',
238
+ subfields: { a: 'Nephhrology', '2': 'fast', '0': '(OCoLC)fst01035991' }),
239
+ marc_field(tag: '650', indicator2: '2', subfields: { a: 'Nephrology' }),
240
+ marc_field(tag: '650', indicator2: '1', subfields: { a: 'Kidney Diseases' })
241
+ ]
242
+ )
243
+ end
244
+
245
+ it 'includes heading terms only from subject tags with indicator 2 of "2"' do
246
+ expect(helper.medical_show(record)).to contain_exactly 'Nephrology'
247
+ end
248
+ end
249
+
250
+ describe '.local_show' do
251
+ let(:record) do
252
+ marc_record(fields: [
253
+ marc_field(tag: '650', indicator2: '4', subfields: { a: 'Local', v: 'Heading' }),
254
+ marc_field(tag: '690', indicator2: '4', subfields: { a: 'Super Local.' })
255
+ ])
256
+ end
257
+
258
+ it 'includes heading terms only from subject tags with indicator 2 of "4" or in the 69X range' do
259
+ expect(helper.local_show(record)).to contain_exactly 'Local--Heading', 'Super Local.'
260
+ end
261
+ end
262
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'PennMARC::Title' do
4
+ include MarcSpecHelpers
5
+
6
+ let(:helper) { PennMARC::Title }
7
+
8
+ describe '.search' do
9
+ let(:record) do
10
+ marc_record fields: [
11
+ marc_field(tag: '245', subfields: { a: 'Title', b: 'Subtitle', c: 'Responsibility', h: 'Medium' }),
12
+ marc_field(tag: '880', subfields: { a: 'Linked Title', '6': '245' })
13
+ ]
14
+ end
15
+
16
+ it 'returns search values without ǂc or ǂh content' do
17
+ values = helper.search(record)
18
+ expect(values).to contain_exactly 'Linked Title', 'Title Subtitle'
19
+ expect(values).not_to include 'Responsibility', 'Medium'
20
+ end
21
+ end
22
+
23
+ xdescribe '.search_aux'
24
+
25
+ describe '.show' do
26
+ let(:record) { marc_record fields: [marc_field(tag: '245', subfields: subfields)] }
27
+
28
+ context 'with ǂa, ǂk and ǂn defined' do
29
+ let(:subfields) { { a: 'Five Decades of MARC usage', k: 'journals', n: 'Part One' } }
30
+
31
+ it 'returns single title value with text from ǂa and ǂn but not ǂk' do
32
+ expect(helper.show(record)).to eq 'Five Decades of MARC usage Part One'
33
+ end
34
+ end
35
+
36
+ context 'with no ǂa but a ǂk and ǂn defined' do
37
+ let(:subfields) { { k: 'journals', n: 'Part One' } }
38
+
39
+ it 'returns single title value with text from ǂk and ǂn' do
40
+ expect(helper.show(record)).to eq 'journals Part One'
41
+ end
42
+ end
43
+
44
+ context 'with ǂa containing an " = "' do
45
+ let(:subfields) { { a: 'There is a parallel statement = ', b: 'Parallel statement / ' } }
46
+
47
+ it 'returns single title value with text from ǂa and ǂb joined with an " = " and other trailing punctuation
48
+ removed' do
49
+ expect(helper.show(record)).to eq 'There is a parallel statement = Parallel statement'
50
+ end
51
+ end
52
+
53
+ context 'with ǂa containing an " : "' do
54
+ let(:subfields) { { a: 'There is an other statement : ', b: 'Other statement' } }
55
+
56
+ it 'returns single title value with text from ǂa and ǂn' do
57
+ expect(helper.show(record)).to eq 'There is an other statement : Other statement'
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '.sort' do
63
+ context 'with a record with a valid indicator2 value' do
64
+ let(:record) do
65
+ marc_record fields: [
66
+ marc_field(tag: '245', indicator2: '4', subfields: {
67
+ a: 'The Record Title',
68
+ b: 'Remainder', n: 'Number', p: 'Section',
69
+ h: 'Do not display'
70
+ })
71
+ ]
72
+ end
73
+
74
+ it 'properly removes and appends the number of characters specified in indicator 2' do
75
+ value = helper.sort(record)
76
+ expect(value).to start_with 'Record Title'
77
+ expect(value).to end_with 'The'
78
+ end
79
+
80
+ it 'includes ǂb, ǂn and ǂp values' do
81
+ expect(helper.sort(record)).to eq 'Record Title Remainder Number Section The'
82
+ end
83
+ end
84
+
85
+ context 'with a record with no indicator2 value' do
86
+ let(:record) do
87
+ marc_record fields: [marc_field(tag: '245', subfields: { a: 'The Record Title' })]
88
+ end
89
+
90
+ it 'does not transform the title value' do
91
+ expect(helper.sort(record)).to eq 'The Record Title'
92
+ end
93
+ end
94
+
95
+ context 'with a record with no ǂa and no indicator2 value' do
96
+ let(:record) do
97
+ marc_record fields: [marc_field(tag: '245', subfields: { k: 'diaries' })]
98
+ end
99
+
100
+ it 'uses ǂk (form) value without transformation' do
101
+ expect(helper.sort(record)).to eq 'diaries'
102
+ end
103
+ end
104
+
105
+ context 'with a record with a leading bracket' do
106
+ let(:record) do
107
+ marc_record fields: [marc_field(tag: '245', subfields: { a: '[The Record Title]' })]
108
+ end
109
+
110
+ # TODO: is this the expected behavior? It would sort right, but looks silly.
111
+ it 'removes the leading bracket and appends it to the full value' do
112
+ expect(helper.sort(record)).to eq 'The Record Title] ['
113
+ end
114
+ end
115
+ end
116
+
117
+ describe '.standardized' do
118
+ let(:record) do
119
+ marc_record fields: [
120
+ marc_field(tag: '130', subfields: { a: 'Uniform Title', f: '2000', '8': 'Not Included' }),
121
+ marc_field(tag: '240', subfields: { a: 'Another Uniform Title', '0': 'Ugly Control Number' }),
122
+ marc_field(tag: '730', indicator2: '', subfields: { a: 'Yet Another Uniform Title' }),
123
+ marc_field(tag: '730', indicator1: '0', indicator2: '2', subfields: { a: 'Not Printed Title' }),
124
+ marc_field(tag: '730', indicator1: '', subfields: { i: 'Subfield i Title' }),
125
+ marc_field(tag: '880', subfields: { '6': '240', a: 'Translated Uniform Title' })
126
+ ]
127
+ end
128
+
129
+ it 'returns the expected standardized title display values' do
130
+ values = helper.standardized(record)
131
+ expect(values).to contain_exactly(
132
+ 'Another Uniform Title', 'Translated Uniform Title', 'Uniform Title 2000', 'Yet Another Uniform Title'
133
+ )
134
+ expect(values).not_to include 'Not Printed Title', 'Subfield i Title'
135
+ end
136
+ end
137
+
138
+ describe '.other' do
139
+ let(:record) do
140
+ marc_record fields: [
141
+ marc_field(tag: '246', subfields: { a: 'Varied Title', f: '2000', '8': 'Not Included' }),
142
+ marc_field(tag: '740', indicator2: '0', subfields: { a: 'Uncontrolled Title', '5': 'Penn' }),
143
+ marc_field(tag: '740', indicator2: '2', subfields: { a: 'A Title We Do Not Like' }),
144
+ marc_field(tag: '880', subfields: { '6': '246', a: 'Alternate Varied Title' })
145
+ ]
146
+ end
147
+
148
+ it 'returns the expected other title display values' do
149
+ expect(helper.other(record)).to contain_exactly(
150
+ 'Alternate Varied Title', 'Uncontrolled Title', 'Varied Title 2000'
151
+ )
152
+ end
153
+ end
154
+
155
+ describe '.former' do
156
+ let(:record) do
157
+ marc_record fields: [
158
+ marc_field(tag: '247', subfields: { a: 'Former Title', n: 'Part', '6': 'Linkage', e: 'Append' }),
159
+ marc_field(tag: '880', subfields: { a: 'Alt Title', n: 'Part', '6': '247' })
160
+ ]
161
+ end
162
+
163
+ it 'returns the expected former title value' do
164
+ values = helper.former(record)
165
+ expect(values).to contain_exactly 'Former Title Part Append', 'Alt Title Part'
166
+ expect(values).not_to include 'Linkage', '247'
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'PennMARC::Util' do
4
+ include MarcSpecHelpers
5
+
6
+ subject(:util) do
7
+ Class.new { extend PennMARC::Util }
8
+ end
9
+
10
+ describe '.join_subfields' do
11
+ let(:field) { marc_field subfields: { a: 'bad', '1': 'join', '3': '', '9': 'subfields' } }
12
+
13
+ it 'joins subfield values after selecting values using a block' do
14
+ subfield_numeric = ->(subfield) { subfield.code =~ /[0-9]/ }
15
+ expect(util.join_subfields(field, &subfield_numeric)).to eq 'join subfields'
16
+ end
17
+ end
18
+
19
+ describe '.subfield_value?' do
20
+ let(:field) { marc_field subfields: { a: '123' } }
21
+
22
+ it 'returns true if the specified subfield value matches the regex' do
23
+ expect(util.subfield_value?(field, 'a', /123/)).to be_truthy
24
+ end
25
+
26
+ it 'returns false if the subfield value does not match the regex' do
27
+ expect(util.subfield_value?(field, 'a', /\D/)).to be_falsey
28
+ end
29
+ end
30
+
31
+ describe '.subfield_value_in?' do
32
+ let(:field) { marc_field subfields: { a: '123' } }
33
+
34
+ it 'returns true if value is in array' do
35
+ expect(util.subfield_value_in?(field, 'a', ['123'])).to be true
36
+ end
37
+ end
38
+
39
+ describe '.subfield_in?' do
40
+ it 'returns a lambda that checks if a subfield code is a member of the array' do
41
+ array = %w[a b c]
42
+ subfield_in = util.subfield_in?(array)
43
+
44
+ subfield = marc_subfield('a', 'Value')
45
+ expect(subfield_in.call(subfield)).to be_truthy
46
+
47
+ subfield = marc_subfield('d', 'Value')
48
+ expect(subfield_in.call(subfield)).to be_falsey
49
+ end
50
+ end
51
+
52
+ describe '#subfield_not_in?' do
53
+ it 'returns a lambda that checks if a subfield code is not a member of the array' do
54
+ array = %w[a b c]
55
+ subfield_not_in = util.subfield_not_in?(array)
56
+
57
+ subfield = marc_subfield('a', 'Value')
58
+ expect(subfield_not_in.call(subfield)).to be_falsey
59
+
60
+ subfield = marc_subfield('d', 'Value')
61
+ expect(subfield_not_in.call(subfield)).to be_truthy
62
+ end
63
+ end
64
+
65
+ describe '.subfield_defined?' do
66
+ let(:field) { marc_field subfields: { a: 'Defined' } }
67
+
68
+ it 'returns true if subfield is present on a field' do
69
+ expect(util.subfield_defined?(field, :a)).to be_truthy
70
+ expect(util.subfield_defined?(field, 'a')).to be_truthy
71
+ end
72
+
73
+ it 'returns false if a subfield is not present on a field' do
74
+ expect(util.subfield_defined?(field, :b)).to be_falsey
75
+ end
76
+ end
77
+
78
+ describe '.subfield_undefined?' do
79
+ let(:field) { marc_field subfields: { a: 'Defined' } }
80
+
81
+ it 'returns true if subfield is not present on a field' do
82
+ expect(util.subfield_undefined?(field, :b)).to be_truthy
83
+ expect(util.subfield_undefined?(field, 'b')).to be_truthy
84
+ end
85
+
86
+ it 'returns false if a subfield is present on a field' do
87
+ expect(util.subfield_undefined?(field, :a)).to be_falsey
88
+ end
89
+ end
90
+
91
+ describe '.subfield_values' do
92
+ let(:field) { marc_field subfields: { a: %w[A B C], b: 'Not Included' } }
93
+
94
+ it 'returns subfield values from a given field' do
95
+ expect(util.subfield_values(field, :a)).to eq %w[A B C]
96
+ end
97
+ end
98
+
99
+ describe '.subfield_values_for' do
100
+ let(:record) do
101
+ marc_record fields: [marc_field(tag: '123', subfields: { a: %w[A B C], b: 'Not Included' }),
102
+ marc_field(tag: '123', subfields: { a: 'D' }),
103
+ marc_field(tag: '333', subfields: { a: 'Maybe', b: 'Nope' })]
104
+ end
105
+
106
+ it 'returns subfield values from only the specified tag and subfield' do
107
+ expect(util.subfield_values_for(tag: '123', subfield: :a, record: record)).to eq %w[A B C D]
108
+ end
109
+
110
+ it 'returns subfield values from only the specified tags and subfield' do
111
+ expect(util.subfield_values_for(tag: %w[123 333], subfield: :a, record: record)).to eq %w[A B C D Maybe]
112
+ end
113
+ end
114
+
115
+ describe '.trim_trailing' do
116
+ it 'trims the specified trailer from the string' do
117
+ expect(util.trim_trailing(:semicolon, 'Hello, world! ;')).to eq('Hello, world!')
118
+ end
119
+ end
120
+
121
+ describe '.linked_alternate' do
122
+ let(:record) do
123
+ marc_record fields: [marc_field(tag: '254', subfields: { a: 'The Bible', b: 'Test' }),
124
+ marc_field(tag: '880', subfields: { '6': '254', a: 'La Biblia', b: 'Prueba' })]
125
+ end
126
+
127
+ it 'returns the linked alternate' do
128
+ expect(util.linked_alternate(record, '254', &util.subfield_in?(%w[a b]))).to contain_exactly('La Biblia Prueba')
129
+ end
130
+ end
131
+
132
+ describe '.linked_alternate_not_6_or_8' do
133
+ let(:record) do
134
+ marc_record fields: [marc_field(tag: '510', subfields: { a: 'Perkins', b: 'Test' }),
135
+ marc_field(tag: '880', subfields: { '6': '510', '8': 'Ignore', a: 'Snikrep', b: 'Tset' })]
136
+ end
137
+
138
+ it 'returns the linked alternate without 6 or 8' do
139
+ expect(util.linked_alternate_not_6_or_8(record, '510')).to contain_exactly('Snikrep Tset')
140
+ end
141
+ end
142
+
143
+ describe '.datafield_and_linked_alternate' do
144
+ let(:record) do
145
+ marc_record fields: [marc_field(tag: '510', subfields: { a: 'Perkins' }),
146
+ marc_field(tag: '880', subfields: { '6': '510', a: 'Snikrep' })]
147
+ end
148
+
149
+ it 'returns the datafield and linked alternate' do
150
+ expect(util.datafield_and_linked_alternate(record, '510')).to contain_exactly('Perkins', 'Snikrep')
151
+ end
152
+ end
153
+
154
+ describe '.substring_before' do
155
+ it 'returns the entire substring after the first occurrence of the target' do
156
+ string = 'string.with.periods'
157
+ expect(util.substring_before(string, '.')).to eq 'string'
158
+ end
159
+ end
160
+
161
+ describe '.substring_after' do
162
+ it 'returns the entire substring after the first occurrence of the target' do
163
+ string = 'string.with.periods'
164
+ expect(util.substring_after(string, '.')).to eq 'with.periods'
165
+ end
166
+ end
167
+
168
+ describe '.join_and_squish' do
169
+ it 'joins and squishes' do
170
+ expect(util.join_and_squish(['ruby ', ' is', ' cool '])).to eq 'ruby is cool'
171
+ end
172
+ end
173
+
174
+ describe '.remove_paren_value_from_subfield_i' do
175
+ let(:field) { marc_field(tag: '666', subfields: { i: 'Test(Remove).' }) }
176
+ it 'removes the parentheses value from subfield i' do
177
+ expect(util.remove_paren_value_from_subfield_i(field)).to eq('Test')
178
+ end
179
+ end
180
+
181
+ describe '.translate_relator' do
182
+ let(:mapping) { { aut: 'Author' } }
183
+
184
+ it 'translates the code into the relator' do
185
+ expect(util.translate_relator(:aut, mapping)).to eq('Author')
186
+ end
187
+ end
188
+
189
+ describe '.prefixed_subject_and_alternate' do
190
+ let(:record) do
191
+ marc_record fields: [
192
+ marc_field(tag: '650', indicator2: '4', subfields: { a: 'PRO Heading' }),
193
+ marc_field(tag: '650', indicator2: '4', subfields: { a: 'Regular Local Heading' }),
194
+ marc_field(tag: '650', indicator2: '1', subfields: { a: 'LoC Heading' }),
195
+ marc_field(tag: '880', indicator2: '4', subfields: { '6': '650', a: 'PRO Alt. Heading' }),
196
+ marc_field(tag: '880', indicator2: '4', subfields: { '6': '999', a: 'Another Alt.' })
197
+ ]
198
+ end
199
+
200
+ it 'only includes valid headings' do
201
+ values = util.prefixed_subject_and_alternate(record, 'PRO')
202
+ expect(values).to include 'Heading', 'Alt. Heading'
203
+ expect(values).not_to include 'Regular Local Heading', 'LoC Heading', 'Another Alt.'
204
+ end
205
+ end
206
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PennMARC::Parser do
4
+ include MarcSpecHelpers
5
+
6
+ let(:record) { record_from 'test.xml' }
7
+
8
+ subject(:parser) { described_class.new }
9
+
10
+ it 'delegates to helper modules properly' do
11
+ expect { parser.title_show(record) }.not_to raise_exception
12
+ end
13
+ end