pennmarc 1.0.18 → 1.0.20
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.
- checksums.yaml +4 -4
- data/lib/pennmarc/helpers/creator.rb +55 -58
- data/lib/pennmarc/helpers/edition.rb +1 -1
- data/lib/pennmarc/helpers/format.rb +58 -66
- data/lib/pennmarc/helpers/genre.rb +4 -6
- data/lib/pennmarc/helpers/identifier.rb +1 -1
- data/lib/pennmarc/helpers/note.rb +8 -8
- data/lib/pennmarc/helpers/production.rb +1 -1
- data/lib/pennmarc/helpers/relation.rb +33 -35
- data/lib/pennmarc/helpers/title.rb +5 -5
- data/lib/pennmarc/mappings/locations.yml +4 -0
- data/lib/pennmarc/util.rb +63 -2
- data/lib/pennmarc/version.rb +1 -1
- data/spec/lib/pennmarc/helpers/creator_spec.rb +140 -56
- data/spec/lib/pennmarc/helpers/format_spec.rb +156 -27
- data/spec/lib/pennmarc/helpers/genre_spec.rb +3 -7
- data/spec/lib/pennmarc/helpers/note_spec.rb +3 -2
- data/spec/lib/pennmarc/helpers/relation_spec.rb +47 -3
- data/spec/lib/pennmarc/marc_util_spec.rb +118 -0
- metadata +2 -2
@@ -36,7 +36,7 @@ module PennMARC
|
|
36
36
|
# @return [Array<String>] array of title values for search
|
37
37
|
def search(record)
|
38
38
|
record.fields(%w[245 880]).filter_map { |field|
|
39
|
-
next if field.tag == '880' &&
|
39
|
+
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^245/)
|
40
40
|
|
41
41
|
join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
|
42
42
|
}.uniq
|
@@ -63,7 +63,7 @@ module PennMARC
|
|
63
63
|
return [] if not_a_serial?(record)
|
64
64
|
|
65
65
|
record.fields(%w[245 880]).filter_map { |field|
|
66
|
-
next if field.tag == '880' &&
|
66
|
+
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^245/)
|
67
67
|
|
68
68
|
join_subfields(field, &subfield_not_in?(%w[c 6 8 h]))
|
69
69
|
}.uniq
|
@@ -166,7 +166,7 @@ module PennMARC
|
|
166
166
|
end
|
167
167
|
titles = standardized_titles + record.fields('880').filter_map do |field|
|
168
168
|
next unless subfield_undefined?(field, 'i') ||
|
169
|
-
|
169
|
+
subfield_value?(field, '6', /^(130|240|730)/)
|
170
170
|
|
171
171
|
join_subfields field, &subfield_not_in?(%w[5 6 8 e w])
|
172
172
|
end
|
@@ -191,7 +191,7 @@ module PennMARC
|
|
191
191
|
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
192
192
|
end
|
193
193
|
titles = other_titles + record.fields('880').filter_map do |field|
|
194
|
-
next unless
|
194
|
+
next unless subfield_value? field, '6', /^(246|740)/
|
195
195
|
|
196
196
|
join_subfields(field, &subfield_not_in?(%w[5 6 8]))
|
197
197
|
end
|
@@ -263,7 +263,7 @@ module PennMARC
|
|
263
263
|
record.fields(tags).filter_map do |field|
|
264
264
|
next if field.tag == '505' && indicators_are_not_value?(field, '0')
|
265
265
|
|
266
|
-
next if field.tag == '880' &&
|
266
|
+
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^(#{tags.join('|')})/)
|
267
267
|
|
268
268
|
join_subfields(field, &join_selector)
|
269
269
|
end
|
@@ -1579,6 +1579,10 @@ vpfeatdvd:
|
|
1579
1579
|
specific_location: Van Pelt - Featured DVD Display - First Floor
|
1580
1580
|
library: Van Pelt-Dietrich Library Center
|
1581
1581
|
display: Van Pelt - Featured DVD Display - First Floor
|
1582
|
+
vpfolio:
|
1583
|
+
specific_location: Van Pelt - Folios
|
1584
|
+
library: Van Pelt-Dietrich Library Center
|
1585
|
+
display: Van Pelt Library
|
1582
1586
|
vpjuv:
|
1583
1587
|
specific_location: Van Pelt - Notable Juvenile Books
|
1584
1588
|
library: Van Pelt-Dietrich Library Center
|
data/lib/pennmarc/util.rb
CHANGED
@@ -46,6 +46,16 @@ module PennMARC
|
|
46
46
|
field&.any? { |sf| sf.code == subfield.to_s && sf.value =~ regex }
|
47
47
|
end
|
48
48
|
|
49
|
+
# returns true if field has no value that matches
|
50
|
+
# passed-in regex and passed in subfield
|
51
|
+
# @param [MARC::DataField] field
|
52
|
+
# @param [String|Integer|Symbol] subfield
|
53
|
+
# @param [Regexp] regex
|
54
|
+
# @return [TrueClass, FalseClass, nil]
|
55
|
+
def no_subfield_value_matches?(field, subfield, regex)
|
56
|
+
field&.none? { |sf| sf.code == subfield.to_s && sf.value =~ regex }
|
57
|
+
end
|
58
|
+
|
49
59
|
# returns true if a given field has a given subfield value in a given array
|
50
60
|
# TODO: example usage
|
51
61
|
# @param [MARC:DataField] field
|
@@ -161,7 +171,7 @@ module PennMARC
|
|
161
171
|
# @return [Array] array of linked alternates
|
162
172
|
def linked_alternate(record, subfield6_value, &selector)
|
163
173
|
record.fields('880').filter_map do |field|
|
164
|
-
next unless subfield_value?(field, '6',
|
174
|
+
next unless subfield_value?(field, '6', /^(#{Array.wrap(subfield6_value).join('|')})/)
|
165
175
|
|
166
176
|
field.select(&selector).map(&:value).join(' ')
|
167
177
|
end
|
@@ -251,7 +261,7 @@ module PennMARC
|
|
251
261
|
record.fields(%w[650 880]).filter_map { |field|
|
252
262
|
next unless field.indicator2 == '4'
|
253
263
|
|
254
|
-
next if field.tag == '880' &&
|
264
|
+
next if field.tag == '880' && no_subfield_value_matches?(field, '6', /^650/)
|
255
265
|
|
256
266
|
next unless field.any? { |sf| sf.code == 'a' && sf.value =~ /^(#{prefix}|%#{prefix})/ }
|
257
267
|
|
@@ -268,5 +278,56 @@ module PennMARC
|
|
268
278
|
def valid_subject_genre_source_code?(field)
|
269
279
|
subfield_value_in?(field, '2', PennMARC::HeadingControl::ALLOWED_SOURCE_CODES)
|
270
280
|
end
|
281
|
+
|
282
|
+
# Does a field or its linked alternate match any of the specified tags?
|
283
|
+
# @param [MARC::Field] field
|
284
|
+
# @param [Array<String>] tags
|
285
|
+
# @return [TrueClass, FalseClass]
|
286
|
+
def field_or_its_linked_alternate?(field, tags)
|
287
|
+
return true if field.tag.in? tags
|
288
|
+
return true if field.tag == '880' && subfield_value?(field, '6', /^(#{tags.join('|')})/)
|
289
|
+
|
290
|
+
false
|
291
|
+
end
|
292
|
+
|
293
|
+
# Match any open dates ending a given string to determine join separator for relator term in 1xx/7xx fields.
|
294
|
+
# @param [String] str
|
295
|
+
# @return [String (frozen)]
|
296
|
+
def relator_join_separator(str)
|
297
|
+
/\b\d+-\z/.match?(str) ? ' ' : ', '
|
298
|
+
end
|
299
|
+
|
300
|
+
# For a given field, determine in which subfield to find relator term
|
301
|
+
# The following fields and their linked alternates use $j for relator terms:
|
302
|
+
# {https://www.loc.gov/marc/bibliographic/bd111.html 111}, {https://www.loc.gov/marc/bibliographic/bd411.html 411},
|
303
|
+
# {https://www.loc.gov/marc/bibliographic/bd611.html 611}, {https://www.loc.gov/marc/bibliographic/bd711.html 711},
|
304
|
+
# {https://www.loc.gov/marc/bibliographic/bd811.html 811}
|
305
|
+
# @param [MARC:Field] field
|
306
|
+
# @return [String (frozen)]
|
307
|
+
def relator_term_subfield(field)
|
308
|
+
field_or_its_linked_alternate?(field, %w[111 411 611 711 811]) ? 'j' : 'e'
|
309
|
+
end
|
310
|
+
|
311
|
+
# Appends a relator value to the given string. It prioritizes relator codes found in subfield $4
|
312
|
+
# and falls back to the specified relator term subfield (defaulting to 'e') if no valid codes are found in $4.
|
313
|
+
# Use with 1xx/7xx fields.
|
314
|
+
# @param [MARC::Field] field where relator values are stored
|
315
|
+
# @param [String] joined_subfields the string to which the relator is appended
|
316
|
+
# @param [String] relator_term_sf MARC subfield that stores relator term
|
317
|
+
# @param [Hash] relator_map
|
318
|
+
# @return [String]
|
319
|
+
def append_relator(field:, joined_subfields:, relator_term_sf:, relator_map: Mappers.relator)
|
320
|
+
joined_subfields = trim_trailing(:comma, joined_subfields)
|
321
|
+
|
322
|
+
join_separator = relator_join_separator(joined_subfields)
|
323
|
+
|
324
|
+
relator = subfield_values(field, '4').filter_map { |code| translate_relator(code, relator_map) }
|
325
|
+
|
326
|
+
relator = subfield_values(field, relator_term_sf) if relator.blank?
|
327
|
+
|
328
|
+
relator = append_trailing(:period, relator.join(', ')) if relator.present?
|
329
|
+
|
330
|
+
[joined_subfields, relator].compact_blank.join(join_separator).squish
|
331
|
+
end
|
271
332
|
end
|
272
333
|
end
|
data/lib/pennmarc/version.rb
CHANGED
@@ -18,8 +18,8 @@ describe 'PennMARC::Creator' do
|
|
18
18
|
|
19
19
|
it 'contains the expected search field values for a single author work' do
|
20
20
|
expect(helper.search(record, relator_map: mapping)).to contain_exactly(
|
21
|
-
'Name Surname http://cool.uri/12345
|
22
|
-
'Surname, Name http://cool.uri/12345
|
21
|
+
'Name Surname http://cool.uri/12345 1900-2000, author.',
|
22
|
+
'Surname, Name http://cool.uri/12345 1900-2000, author.',
|
23
23
|
'Alternative Surname'
|
24
24
|
)
|
25
25
|
end
|
@@ -63,62 +63,103 @@ describe 'PennMARC::Creator' do
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
describe '.
|
66
|
+
describe '.show' do
|
67
67
|
let(:record) { marc_record fields: fields }
|
68
68
|
|
69
69
|
context 'with a single author record' do
|
70
70
|
let(:fields) do
|
71
|
-
[marc_field(tag: '100', subfields: { a: '
|
72
|
-
|
71
|
+
[marc_field(tag: '100', subfields: { a: 'Surname, Name', '0': 'http://cool.uri/12345', d: '1900-2000',
|
72
|
+
e: 'author.', '4': 'http://cool.uri/vocabulary/relators/aut' }),
|
73
|
+
marc_field(tag: '880', subfields: { a: 'Surname, Alternative', '6': '100' })]
|
73
74
|
end
|
74
75
|
|
75
|
-
it 'returns
|
76
|
-
values = helper.
|
77
|
-
expect(values).to contain_exactly '
|
78
|
-
expect(values.join.downcase).not_to include '
|
76
|
+
it 'returns single author values with no URIs anywhere' do
|
77
|
+
values = helper.show(record)
|
78
|
+
expect(values).to contain_exactly 'Surname, Name 1900-2000, author.', 'Surname, Alternative'
|
79
|
+
expect(values.join.downcase).not_to include 'http'
|
79
80
|
end
|
80
81
|
end
|
81
82
|
|
82
83
|
context 'with a corporate author record' do
|
83
84
|
let(:fields) do
|
84
|
-
[marc_field(tag: '110', subfields: { a: '
|
85
|
+
[marc_field(tag: '110', subfields: { a: 'Group of People', b: 'Annual Meeting', '4': 'aut' }),
|
86
|
+
marc_field(tag: '880', subfields: { '6': '110', a: 'Alt. Group Name', b: 'Alt. Annual Meeting' })]
|
85
87
|
end
|
86
88
|
|
87
|
-
it 'returns
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
it 'returns corporate author values with no URIs anywhere' do
|
90
|
+
values = helper.show(record, relator_map: mapping)
|
91
|
+
expect(values).to contain_exactly 'Alt. Group Name Alt. Annual Meeting',
|
92
|
+
'Group of People Annual Meeting, Author.'
|
93
|
+
expect(values.join.downcase).not_to include 'http'
|
91
94
|
end
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
95
|
-
describe '.
|
98
|
+
describe '.show_aux' do
|
96
99
|
let(:record) { marc_record fields: fields }
|
97
100
|
|
98
101
|
context 'with a single author record' do
|
99
102
|
let(:fields) do
|
100
|
-
[marc_field(tag: '100', subfields: { a: '
|
101
|
-
|
102
|
-
marc_field(tag: '880', subfields: { a: 'Surname, Alternative', '6': '100' })]
|
103
|
+
[marc_field(tag: '100', subfields: { a: 'Person', c: 'Loquacious', d: 'active 24th century AD', '4': 'aut' }),
|
104
|
+
marc_field(tag: '880', subfields: { '6': '100', a: 'Alt Author', c: 'Alt Fanciness' })]
|
103
105
|
end
|
104
106
|
|
105
|
-
it 'returns
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
it 'returns mapped relator code from ǂ4 at the end with a terminal period' do
|
108
|
+
expect(helper.show_aux(record, relator_map: mapping).first).to end_with ', Author.'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'does not include linked 880 field' do
|
112
|
+
expect(helper.show_aux(record, relator_map: mapping).join(' ')).not_to include 'Alt'
|
109
113
|
end
|
110
114
|
end
|
111
115
|
|
112
116
|
context 'with a corporate author record' do
|
113
117
|
let(:fields) do
|
114
|
-
[marc_field(tag: '110', subfields: { a: '
|
115
|
-
marc_field(tag: '880', subfields: { '6': '110', a: 'Alt. Group Name', b: 'Alt. Annual Meeting' })]
|
118
|
+
[marc_field(tag: '110', subfields: { a: 'Annual Report', b: 'Leader', e: 'author', '4': 'aut' })]
|
116
119
|
end
|
117
120
|
|
118
|
-
it 'returns corporate author
|
119
|
-
|
120
|
-
|
121
|
-
|
121
|
+
it 'returns values for the corporate author, including mapped relator code from ǂ4' do
|
122
|
+
expect(helper.show_aux(record, relator_map: mapping)).to contain_exactly(
|
123
|
+
'Annual Report Leader, Author.'
|
124
|
+
)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'with relator term and translatable relator code' do
|
129
|
+
let(:fields) do
|
130
|
+
[marc_field(tag: '100', subfields: { a: 'Person', c: 'Loquacious', d: 'active 24th century AD', e: 'Ignore',
|
131
|
+
'4': 'aut' })]
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'only appends translatable relator' do
|
135
|
+
expect(helper.show_aux(record, relator_map: mapping)).to contain_exactly(
|
136
|
+
'Person Loquacious active 24th century AD, Author.'
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'with multiple translatable relator codes' do
|
142
|
+
let(:mapping) { { aut: 'Author', stl: 'Storyteller' } }
|
143
|
+
let(:fields) do
|
144
|
+
[marc_field(tag: '100', subfields: { a: 'Person', c: 'Loquacious', d: 'active 24th century AD',
|
145
|
+
'4': %w[aut stl] })]
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'appends all translatable relators' do
|
149
|
+
expect(helper.show_aux(record, relator_map: mapping)).to contain_exactly(
|
150
|
+
'Person Loquacious active 24th century AD, Author, Storyteller.'
|
151
|
+
)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'without translatable relator code' do
|
156
|
+
let(:fields) do
|
157
|
+
[marc_field(tag: '100', subfields: { a: 'Person', c: 'Loquacious', d: 'active 24th century AD',
|
158
|
+
e: 'author' })]
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'appends all translatable relators' do
|
162
|
+
expect(helper.show_aux(record)).to contain_exactly('Person Loquacious active 24th century AD, author.')
|
122
163
|
end
|
123
164
|
end
|
124
165
|
end
|
@@ -197,18 +238,22 @@ describe 'PennMARC::Creator' do
|
|
197
238
|
describe '.conference_detail_show' do
|
198
239
|
let(:record) do
|
199
240
|
marc_record fields: [
|
200
|
-
marc_field(tag: '111', subfields: { a: 'MARC History Symposium', c: 'Moscow'
|
241
|
+
marc_field(tag: '111', subfields: { a: 'MARC History Symposium', e: 'Advisory Committee', c: 'Moscow',
|
242
|
+
j: 'author', '4': 'aut' }),
|
201
243
|
marc_field(tag: '711', subfields: { a: 'Russian Library Conference', j: 'author' }),
|
202
244
|
marc_field(tag: '711', indicator2: '1', subfields: { a: 'Ignored Entry', j: 'author' }),
|
203
245
|
marc_field(tag: '880', subfields: { a: 'Proceedings', '6': '111' }),
|
246
|
+
marc_field(tag: '880', subfields: { a: 'Opening Remarks', j: 'author', '4': 'aut', '6': '711' }),
|
204
247
|
marc_field(tag: '880', subfields: { a: 'Not Included', i: 'something', '6': '111' })
|
205
248
|
]
|
206
249
|
end
|
207
250
|
|
208
251
|
it 'returns detailed conference name information for display, including linked 880 fields without ǂi, and ignoring
|
209
252
|
any 111 or 711 with a defined indicator 2 value' do
|
210
|
-
expect(helper.conference_detail_show(record)).to
|
211
|
-
|
253
|
+
expect(helper.conference_detail_show(record, relator_map: mapping)).to contain_exactly(
|
254
|
+
'MARC History Symposium Moscow Advisory Committee, Author.',
|
255
|
+
'Russian Library Conference, author.', 'Proceedings', 'Opening Remarks, Author.'
|
256
|
+
)
|
212
257
|
end
|
213
258
|
end
|
214
259
|
|
@@ -225,33 +270,72 @@ describe 'PennMARC::Creator' do
|
|
225
270
|
end
|
226
271
|
|
227
272
|
describe '.contributor_show' do
|
228
|
-
let(:record)
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
d: 'Alt date', e: 'Alt relator', j: 'Alt qualifier', q: 'Alt Fuller Name',
|
240
|
-
u: 'Alt affiliation', '3': 'Alt materials' }),
|
241
|
-
marc_field(tag: '880', subfields: { '6': '710', a: 'Alt Corp Name', b: 'Alt unit', c: 'Alt location',
|
242
|
-
d: 'Alt date', e: 'Alt relator', u: 'Alt Affiliation',
|
243
|
-
'3': 'Alt materials' }),
|
244
|
-
marc_field(tag: '880', subfields: { i: 'Ignore', '6': '700' })
|
245
|
-
]
|
273
|
+
let(:record) { marc_record fields: fields }
|
274
|
+
|
275
|
+
context 'when idicator2 is "1"' do
|
276
|
+
let(:fields) do
|
277
|
+
[marc_field(tag: '700', subfields: { a: 'Ignore' }, indicator2: '1')]
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'ignores the field' do
|
281
|
+
values = helper.contributor_show(record, relator_map: mapping)
|
282
|
+
expect(values).to be_empty
|
283
|
+
end
|
246
284
|
end
|
247
285
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
286
|
+
context 'with subfield "i"' do
|
287
|
+
let(:fields) do
|
288
|
+
[
|
289
|
+
marc_field(tag: '700', subfields: { i: 'Ignore' }),
|
290
|
+
marc_field(tag: '880', subfields: { i: 'Ignore', '6': '700' })
|
291
|
+
]
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'ignores the field' do
|
295
|
+
values = helper.contributor_show(record, relator_map: mapping)
|
296
|
+
expect(values).to be_empty
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
context 'with a single contributor and linked alternate' do
|
301
|
+
let(:fields) do
|
302
|
+
[
|
303
|
+
marc_field(tag: '700', subfields: { a: 'Name', b: 'I', c: 'laureate', d: '1968', e: 'author',
|
304
|
+
j: 'pseud', q: 'Fuller Name', u: 'affiliation', '3': 'materials',
|
305
|
+
'4': 'aut' }),
|
306
|
+
marc_field(tag: '880', subfields: { '6': '700', a: 'Alt Name', b: 'Alt num', c: 'Alt title',
|
307
|
+
d: 'Alt date', e: 'Alt relator', j: 'Alt qualifier',
|
308
|
+
q: 'Alt Fuller Name', u: 'Alt affiliation', '3': 'Alt material' })
|
309
|
+
]
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'returns expected contributor values' do
|
313
|
+
values = helper.contributor_show(record, relator_map: mapping)
|
314
|
+
expect(values).to contain_exactly(
|
315
|
+
'Name I laureate 1968 pseud Fuller Name affiliation materials, Author.',
|
316
|
+
'Alt Name Alt num Alt title Alt date Alt qualifier Alt Fuller Name Alt affiliation Alt material, Alt relator.'
|
317
|
+
)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context 'with a corporate contributor and linked alternate' do
|
322
|
+
let(:fields) do
|
323
|
+
[
|
324
|
+
marc_field(tag: '710', subfields: { a: 'Corporation', b: 'A division', c: 'Office', d: '1968', e: 'author',
|
325
|
+
u: 'affiliation', '3': 'materials', '4': 'aut' }),
|
326
|
+
marc_field(tag: '880', subfields: { '6': '710', a: 'Alt Corp Name', b: 'Alt unit', c: 'Alt location',
|
327
|
+
d: 'Alt date', e: ['Alt relator', 'another'], u: 'Alt Affiliation',
|
328
|
+
'3': 'Alt materials' })
|
329
|
+
]
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'returns expected contributor values' do
|
333
|
+
values = helper.contributor_show(record)
|
334
|
+
expect(values).to contain_exactly(
|
335
|
+
'Corporation A division Office 1968 affiliation materials, Author.',
|
336
|
+
'Alt Corp Name Alt unit Alt location Alt date Alt Affiliation Alt materials, Alt relator, another.'
|
337
|
+
)
|
338
|
+
end
|
255
339
|
end
|
256
340
|
end
|
257
341
|
end
|