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