pennmarc 0.0.1

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