mods_display 1.0.0.alpha4 → 1.0.0.alpha5
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/.rubocop.yml +42 -0
- data/.rubocop_todo.yml +72 -401
- data/Gemfile +4 -1
- data/Rakefile +8 -6
- data/app/components/mods_display/field_component.rb +2 -0
- data/app/components/mods_display/list_field_component.rb +2 -0
- data/app/components/mods_display/record_component.rb +2 -0
- data/app/helpers/mods_display/record_helper.rb +4 -7
- data/app/models/mods_display/record.rb +2 -1
- data/config.ru +2 -2
- data/lib/mods_display/country_codes.rb +2 -1
- data/lib/mods_display/fields/abstract.rb +2 -0
- data/lib/mods_display/fields/access_condition.rb +7 -4
- data/lib/mods_display/fields/audience.rb +2 -0
- data/lib/mods_display/fields/cartographics.rb +8 -1
- data/lib/mods_display/fields/collection.rb +6 -2
- data/lib/mods_display/fields/contact.rb +2 -0
- data/lib/mods_display/fields/contents.rb +2 -0
- data/lib/mods_display/fields/description.rb +3 -2
- data/lib/mods_display/fields/extent.rb +3 -0
- data/lib/mods_display/fields/field.rb +6 -9
- data/lib/mods_display/fields/form.rb +3 -0
- data/lib/mods_display/fields/genre.rb +2 -0
- data/lib/mods_display/fields/geo.rb +5 -2
- data/lib/mods_display/fields/identifier.rb +20 -17
- data/lib/mods_display/fields/imprint.rb +168 -217
- data/lib/mods_display/fields/language.rb +5 -1
- data/lib/mods_display/fields/location.rb +4 -1
- data/lib/mods_display/fields/name.rb +35 -19
- data/lib/mods_display/fields/nested_related_item.rb +12 -2
- data/lib/mods_display/fields/note.rb +9 -7
- data/lib/mods_display/fields/related_item.rb +12 -9
- data/lib/mods_display/fields/resource_type.rb +2 -0
- data/lib/mods_display/fields/sub_title.rb +2 -0
- data/lib/mods_display/fields/subject.rb +17 -50
- data/lib/mods_display/fields/title.rb +37 -26
- data/lib/mods_display/fields/values.rb +2 -0
- data/lib/mods_display/html.rb +4 -3
- data/lib/mods_display/related_item_concerns.rb +4 -2
- data/lib/mods_display/relator_codes.rb +2 -0
- data/lib/mods_display/version.rb +3 -1
- data/lib/mods_display.rb +5 -3
- data/mods_display.gemspec +1 -1
- metadata +12 -6
@@ -1,44 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ModsDisplay
|
2
4
|
class Imprint < Field
|
3
5
|
include ModsDisplay::CountryCodes
|
6
|
+
|
4
7
|
def fields
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
other_pub_info(value).each do |pub_info|
|
26
|
-
return_fields << ModsDisplay::Values.new(
|
27
|
-
label: displayLabel(value) || pub_info_labels[pub_info.name.to_sym],
|
28
|
-
values: [pub_info.text.strip]
|
29
|
-
)
|
30
|
-
end
|
31
|
-
end
|
8
|
+
collapse_fields(origin_info_data.flatten)
|
9
|
+
end
|
10
|
+
|
11
|
+
def origin_info_data
|
12
|
+
@values.map do |value|
|
13
|
+
return_fields = []
|
14
|
+
|
15
|
+
edition = edition_element(value)
|
16
|
+
place = place_element(value)
|
17
|
+
publisher = publisher_element(value)
|
18
|
+
parts = parts_element(value)
|
19
|
+
place_pub = compact_and_join_with_delimiter([place, publisher], ' : ')
|
20
|
+
edition_place = compact_and_join_with_delimiter([edition, place_pub], ' - ')
|
21
|
+
joined_place_parts = compact_and_join_with_delimiter([edition_place, parts], ', ')
|
22
|
+
|
23
|
+
unless joined_place_parts.empty?
|
24
|
+
return_fields << ModsDisplay::Values.new(
|
25
|
+
label: displayLabel(value) || I18n.t('mods_display.imprint'),
|
26
|
+
values: [joined_place_parts]
|
27
|
+
)
|
32
28
|
end
|
29
|
+
return_fields.concat(date_values(value))
|
30
|
+
|
31
|
+
other_pub_info(value).each do |pub_info|
|
32
|
+
return_fields << ModsDisplay::Values.new(
|
33
|
+
label: displayLabel(value) || pub_info_labels[pub_info.name.to_sym],
|
34
|
+
values: [pub_info.text.strip]
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
return_fields.compact
|
33
39
|
end
|
34
|
-
collapse_fields(return_fields)
|
35
40
|
end
|
36
41
|
|
37
|
-
def
|
42
|
+
def date_values(element)
|
38
43
|
date_field_keys.map do |date_field|
|
39
44
|
next unless element.respond_to?(date_field)
|
45
|
+
|
40
46
|
elements = element.send(date_field)
|
41
47
|
next if elements.empty?
|
48
|
+
|
42
49
|
ModsDisplay::Values.new(
|
43
50
|
label: displayLabel(element) || pub_info_labels[elements.first.name.to_sym],
|
44
51
|
values: parse_dates(elements)
|
@@ -46,167 +53,147 @@ module ModsDisplay
|
|
46
53
|
end.compact
|
47
54
|
end
|
48
55
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
join_date_ranges(
|
53
|
-
process_bc_ad_dates(
|
54
|
-
process_encoded_dates(ignore_bad_dates(date_field))
|
55
|
-
)
|
56
|
-
)
|
57
|
-
)
|
58
|
-
)
|
59
|
-
end
|
56
|
+
class DateValue
|
57
|
+
attr_reader :value
|
58
|
+
delegate :text, :date, :point, :qualifier, :encoding, to: :value
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
end
|
60
|
+
def initialize(value)
|
61
|
+
@value = value
|
62
|
+
end
|
66
63
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
process_w3cdtf_date(date_field)
|
71
|
-
elsif date_is_iso8601?(date_field)
|
72
|
-
process_iso8601_date(date_field)
|
73
|
-
else
|
74
|
-
date_field
|
64
|
+
# True if the element text isn't blank or the placeholder "9999".
|
65
|
+
def valid?
|
66
|
+
text.present? && !['9999', '0000-00-00', 'uuuu'].include?(text.strip)
|
75
67
|
end
|
76
|
-
end
|
77
|
-
end
|
78
68
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
date = date.clone # clone the date object so we don't append the same one
|
85
|
-
if normalize_date(date.text) == normalize_date(start_date.text)
|
86
|
-
date.content = [start_date.text, end_date.text].join('-')
|
87
|
-
date
|
88
|
-
elsif normalize_date(date.text) != normalize_date(end_date.text)
|
89
|
-
date
|
69
|
+
# Element text reduced to digits and hyphen. Captures date ranges and
|
70
|
+
# negative (B.C.) dates. Used for comparison/deduping.
|
71
|
+
def base_value
|
72
|
+
if text =~ /^\[?1\d{3}-\d{2}\??\]?$/
|
73
|
+
return text.sub(/(\d{2})(\d{2})-(\d{2})/, '\1\2-\1\3')
|
90
74
|
end
|
91
|
-
end.compact
|
92
|
-
elsif dates_are_open_range?(date_fields)
|
93
|
-
start_date = date_fields.find { |d| d.attributes['point'] && d.attributes['point'].value == 'start' }
|
94
|
-
date_fields.map do |date|
|
95
|
-
date = date.clone # clone the date object so we don't append the same one
|
96
|
-
date.content = "#{start_date.text}-" if date.text == start_date.text
|
97
|
-
date
|
98
|
-
end
|
99
|
-
else
|
100
|
-
date_fields
|
101
|
-
end
|
102
|
-
end
|
103
75
|
|
104
|
-
|
105
|
-
return_fields = date_fields.map do |date|
|
106
|
-
date = date.clone
|
107
|
-
if date_is_approximate?(date)
|
108
|
-
date.content = "[ca. #{date.text}]"
|
109
|
-
elsif date_is_questionable?(date)
|
110
|
-
date.content = "[#{date.text}?]"
|
111
|
-
elsif date_is_inferred?(date)
|
112
|
-
date.content = "[#{date.text}]"
|
76
|
+
text.gsub(/(?<![\d])(\d{1,3})([xu-]{1,3})/i) { "#{$1}#{'0' * $2.length}"}.scan(/[\d-]/).join
|
113
77
|
end
|
114
|
-
date
|
115
|
-
end
|
116
|
-
return_fields.map(&:text)
|
117
|
-
end
|
118
78
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
date_field.attributes['qualifier'].value == 'approximate'
|
123
|
-
end
|
79
|
+
# Decoded version of the date, if it was encoded. Strips leading zeroes.
|
80
|
+
def decoded_value
|
81
|
+
return text.strip unless date
|
124
82
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
date_field.attributes['qualifier'].value == 'questionable'
|
129
|
-
end
|
83
|
+
unless encoding.present?
|
84
|
+
return text.strip unless text =~ /^-?\d+$/ || text =~ /^[\dXxu?-]{4}$/
|
85
|
+
end
|
130
86
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
87
|
+
# Delegate to the appropriate decoding method, if any
|
88
|
+
case value.precision
|
89
|
+
when :day
|
90
|
+
date.strftime('%B %e, %Y')
|
91
|
+
when :month
|
92
|
+
date.strftime('%B %Y')
|
93
|
+
when :year
|
94
|
+
year = date.year
|
95
|
+
if year < 1
|
96
|
+
"#{year.abs + 1} B.C."
|
97
|
+
# Any dates before the year 1000 are explicitly marked A.D.
|
98
|
+
elsif year > 1 && year < 1000
|
99
|
+
"#{year} A.D."
|
100
|
+
else
|
101
|
+
year.to_s
|
102
|
+
end
|
103
|
+
when :century
|
104
|
+
return "#{(date.to_s[0..1].to_i + 1).ordinalize} century"
|
105
|
+
when :decade
|
106
|
+
return "#{date.year}s"
|
107
|
+
else
|
108
|
+
text.strip
|
109
|
+
end
|
110
|
+
end
|
136
111
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
field.attributes['point'].value == 'end'
|
146
|
-
end
|
147
|
-
end
|
112
|
+
# Decoded date with "B.C." or "A.D." and qualifier markers. See (outdated):
|
113
|
+
# https://consul.stanford.edu/display/chimera/MODS+display+rules#MODSdisplayrules-3b.%3CoriginInfo%3E
|
114
|
+
def qualified_value
|
115
|
+
date = decoded_value
|
116
|
+
|
117
|
+
return "[ca. #{date}]" if qualifier == 'approximate'
|
118
|
+
return "[#{date}?]" if qualifier == 'questionable'
|
119
|
+
return "[#{date}]" if qualifier == 'inferred'
|
148
120
|
|
149
|
-
|
150
|
-
attributes = date_fields.map do |date|
|
151
|
-
if date.attributes['point'].respond_to?(:value)
|
152
|
-
date.attributes['point'].value
|
121
|
+
date
|
153
122
|
end
|
154
123
|
end
|
155
|
-
attributes.include?('start') &&
|
156
|
-
attributes.include?('end')
|
157
|
-
end
|
158
124
|
|
159
|
-
|
160
|
-
|
161
|
-
|
125
|
+
class DateRange
|
126
|
+
def initialize(start: nil, stop: nil)
|
127
|
+
@start = start
|
128
|
+
@stop = stop
|
129
|
+
end
|
162
130
|
|
163
|
-
|
164
|
-
|
165
|
-
|
131
|
+
# Base value as hyphen-joined string. Used for comparison/deduping.
|
132
|
+
def base_value
|
133
|
+
"#{@start&.base_value}-#{@stop&.base_value}"
|
134
|
+
end
|
166
135
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
if date_field.text.strip =~ /^\d{4}-\d{2}-\d{2}$/
|
171
|
-
Date.parse(date_field.text).strftime('%B %d, %Y')
|
172
|
-
elsif date_field.text.strip =~ /^\d{4}-\d{2}$/
|
173
|
-
Date.parse("#{date_field.text}-01").strftime('%B %Y')
|
174
|
-
else
|
175
|
-
date_field.content
|
136
|
+
# Base values as array. Used for comparison/deduping of individual dates.
|
137
|
+
def base_values
|
138
|
+
[@start&.base_value, @stop&.base_value].compact
|
176
139
|
end
|
177
|
-
rescue
|
178
|
-
date_field.content
|
179
|
-
end
|
180
|
-
date_field
|
181
|
-
end
|
182
140
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
141
|
+
# The encoding value for the start of the range, or stop if not present.
|
142
|
+
def encoding
|
143
|
+
@start&.encoding || @stop&.encoding
|
144
|
+
end
|
145
|
+
|
146
|
+
# Decoded dates with "B.C." or "A.D." and qualifier markers applied to
|
147
|
+
# the entire range, or individually if dates differ.
|
148
|
+
def qualified_value
|
149
|
+
if @start&.qualifier == @stop&.qualifier
|
150
|
+
qualifier = @start&.qualifier || @stop&.qualifier
|
151
|
+
date = "#{@start&.decoded_value}-#{@stop&.decoded_value}"
|
152
|
+
return "[ca. #{date}]" if qualifier == 'approximate'
|
153
|
+
return "[#{date}?]" if qualifier == 'questionable'
|
154
|
+
return "[#{date}]" if qualifier == 'inferred'
|
155
|
+
|
156
|
+
date
|
157
|
+
else
|
158
|
+
"#{@start&.qualified_value}-#{@stop&.qualified_value}"
|
159
|
+
end
|
160
|
+
end
|
189
161
|
end
|
190
|
-
|
191
|
-
|
162
|
+
def parse_dates(elements)
|
163
|
+
# convert to DateValue objects and keep only valid ones
|
164
|
+
dates = elements.map(&:as_object).flatten.map { |element| DateValue.new(element) }.select(&:valid?)
|
165
|
+
|
166
|
+
# join any date ranges into DateRange objects
|
167
|
+
point, nonpoint = dates.partition(&:point)
|
168
|
+
if point.any?
|
169
|
+
range = DateRange.new(start: point.find { |date| date.point == 'start' },
|
170
|
+
stop: point.find { |date| date.point == 'end' })
|
171
|
+
nonpoint.unshift(range)
|
172
|
+
end
|
173
|
+
dates = nonpoint
|
174
|
+
|
175
|
+
# ensure dates are unique with respect to their base values
|
176
|
+
dates = dates.group_by(&:base_value).map do |_value, group|
|
177
|
+
group.first if group.one?
|
192
178
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
[date_fields.find { |d| !d.attributes['encoding'] }]
|
179
|
+
# if one of the duplicates wasn't encoded, use that one. see:
|
180
|
+
# https://consul.stanford.edu/display/chimera/MODS+display+rules#MODSdisplayrules-3b.%3CoriginInfo%3E
|
181
|
+
if group.reject(&:encoding).any?
|
182
|
+
group.reject(&:encoding).first
|
183
|
+
|
184
|
+
# otherwise just randomly pick the last in the group
|
200
185
|
else
|
201
|
-
|
186
|
+
group.last
|
202
187
|
end
|
203
|
-
else
|
204
|
-
date_fields
|
205
188
|
end
|
206
|
-
end
|
207
189
|
|
208
|
-
|
209
|
-
date.
|
190
|
+
# if any single dates are already part of a range, discard them
|
191
|
+
range_base_values = dates.select { |date| date.is_a?(DateRange) }
|
192
|
+
.map(&:base_values).flatten
|
193
|
+
dates = dates.reject { |date| range_base_values.include?(date.base_value) }
|
194
|
+
|
195
|
+
# output formatted dates with qualifiers, A.D./B.C., etc.
|
196
|
+
dates.map(&:qualified_value)
|
210
197
|
end
|
211
198
|
|
212
199
|
def other_pub_info(element)
|
@@ -218,6 +205,7 @@ module ModsDisplay
|
|
218
205
|
def place_terms(element)
|
219
206
|
return [] unless element.respond_to?(:place) &&
|
220
207
|
element.place.respond_to?(:placeTerm)
|
208
|
+
|
221
209
|
if unencoded_place_terms?(element)
|
222
210
|
element.place.placeTerm.select do |term|
|
223
211
|
!term.attributes['type'].respond_to?(:value) ||
|
@@ -230,6 +218,7 @@ module ModsDisplay
|
|
230
218
|
term.attributes['authority'].respond_to?(:value) &&
|
231
219
|
term.attributes['authority'].value == 'marccountry' &&
|
232
220
|
country_codes.include?(term.text.strip)
|
221
|
+
|
233
222
|
term = term.clone
|
234
223
|
term.content = country_codes[term.text.strip]
|
235
224
|
term
|
@@ -244,63 +233,23 @@ module ModsDisplay
|
|
244
233
|
end
|
245
234
|
end
|
246
235
|
|
247
|
-
def imprint_display_form(element)
|
248
|
-
display_form = element.children.find do |child|
|
249
|
-
child.name == 'displayForm'
|
250
|
-
end
|
251
|
-
ModsDisplay::Values.new(
|
252
|
-
label: displayLabel(element) || I18n.t('mods_display.imprint'),
|
253
|
-
values: [display_form.text]
|
254
|
-
) if display_form
|
255
|
-
end
|
256
|
-
|
257
236
|
private
|
258
237
|
|
259
238
|
def compact_and_join_with_delimiter(values, delimiter)
|
260
239
|
compact_values = values.compact.reject { |v| v.strip.empty? }
|
261
240
|
return compact_values.join(delimiter) if compact_values.length == 1 ||
|
262
241
|
!ends_in_terminating_punctuation?(delimiter)
|
242
|
+
|
263
243
|
compact_values.each_with_index.map do |value, i|
|
264
|
-
if (compact_values.length - 1) == i || # last item?
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
244
|
+
value << if (compact_values.length - 1) == i || # last item?
|
245
|
+
ends_in_terminating_punctuation?(value)
|
246
|
+
' '
|
247
|
+
else
|
248
|
+
delimiter
|
249
|
+
end
|
270
250
|
end.join.strip
|
271
251
|
end
|
272
252
|
|
273
|
-
def process_bc_ad_dates(date_fields)
|
274
|
-
date_fields.map do |date_field|
|
275
|
-
case
|
276
|
-
when date_is_bc_edtf?(date_field)
|
277
|
-
year = date_field.text.strip.gsub(/^-0*/, '').to_i + 1
|
278
|
-
date_field.content = "#{year} B.C."
|
279
|
-
when date_is_ad?(date_field)
|
280
|
-
date_field.content = "#{date_field.text.strip.gsub(/^0*/, '')} A.D."
|
281
|
-
end
|
282
|
-
date_field
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def date_is_bc_edtf?(date_field)
|
287
|
-
date_field.text.strip.start_with?('-') && date_is_edtf?(date_field)
|
288
|
-
end
|
289
|
-
|
290
|
-
def date_is_ad?(date_field)
|
291
|
-
date_field.text.strip.gsub(/^0*/, '').length < 4
|
292
|
-
end
|
293
|
-
|
294
|
-
def date_is_edtf?(date_field)
|
295
|
-
field_is_encoded?(date_field, 'edtf')
|
296
|
-
end
|
297
|
-
|
298
|
-
def field_is_encoded?(field, encoding)
|
299
|
-
field.attributes['encoding'] &&
|
300
|
-
field.attributes['encoding'].respond_to?(:value) &&
|
301
|
-
field.attributes['encoding'].value.downcase == encoding
|
302
|
-
end
|
303
|
-
|
304
253
|
def ends_in_terminating_punctuation?(value)
|
305
254
|
value.strip.end_with?('.', ',', ':', ';')
|
306
255
|
end
|
@@ -313,6 +262,7 @@ module ModsDisplay
|
|
313
262
|
|
314
263
|
def place_element(value)
|
315
264
|
return if value.place.text.strip.empty?
|
265
|
+
|
316
266
|
places = place_terms(value).reject do |p|
|
317
267
|
p.text.strip.empty?
|
318
268
|
end.map(&:text)
|
@@ -321,6 +271,7 @@ module ModsDisplay
|
|
321
271
|
|
322
272
|
def publisher_element(value)
|
323
273
|
return if value.publisher.text.strip.empty?
|
274
|
+
|
324
275
|
publishers = value.publisher.reject do |p|
|
325
276
|
p.text.strip.empty?
|
326
277
|
end.map(&:text)
|
@@ -328,8 +279,9 @@ module ModsDisplay
|
|
328
279
|
end
|
329
280
|
|
330
281
|
def parts_element(value)
|
331
|
-
date_elements = %w
|
282
|
+
date_elements = %w[dateIssued dateOther].map do |date_field_name|
|
332
283
|
next unless value.respond_to?(date_field_name.to_sym)
|
284
|
+
|
333
285
|
parse_dates(value.send(date_field_name.to_sym))
|
334
286
|
end.flatten.compact.reject do |date|
|
335
287
|
date.strip.empty?
|
@@ -338,11 +290,11 @@ module ModsDisplay
|
|
338
290
|
end
|
339
291
|
|
340
292
|
def pub_info_parts
|
341
|
-
[
|
293
|
+
%i[issuance frequency]
|
342
294
|
end
|
343
295
|
|
344
296
|
def date_field_keys
|
345
|
-
[
|
297
|
+
%i[dateCreated dateCaptured dateValid dateModified copyrightDate]
|
346
298
|
end
|
347
299
|
|
348
300
|
def pub_info_labels
|
@@ -352,8 +304,7 @@ module ModsDisplay
|
|
352
304
|
dateModified: I18n.t('mods_display.date_modified'),
|
353
305
|
copyrightDate: I18n.t('mods_display.copyright_date'),
|
354
306
|
issuance: I18n.t('mods_display.issuance'),
|
355
|
-
frequency: I18n.t('mods_display.frequency')
|
356
|
-
}
|
307
|
+
frequency: I18n.t('mods_display.frequency') }
|
357
308
|
end
|
358
309
|
end
|
359
310
|
end
|
@@ -1,13 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ModsDisplay
|
2
4
|
class Language < Field
|
3
5
|
def fields
|
4
6
|
return_fields = @values.map do |value|
|
5
7
|
next unless value.respond_to?(:languageTerm)
|
8
|
+
|
6
9
|
value.languageTerm.map do |term|
|
7
10
|
next unless term.attributes['type'].respond_to?(:value) && term.attributes['type'].value == 'code'
|
11
|
+
|
8
12
|
ModsDisplay::Values.new(
|
9
13
|
label: displayLabel(value) || displayLabel(term) || I18n.t('mods_display.language'),
|
10
|
-
values: [
|
14
|
+
values: [language_codes[term.text]]
|
11
15
|
)
|
12
16
|
end.flatten.compact
|
13
17
|
end.flatten.compact
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ModsDisplay
|
2
4
|
class Location < Field
|
3
5
|
def fields
|
@@ -5,6 +7,7 @@ module ModsDisplay
|
|
5
7
|
@values.each do |location|
|
6
8
|
location.children.each do |child|
|
7
9
|
next unless location_field_keys.include?(child.name.to_sym)
|
10
|
+
|
8
11
|
if child.name.to_sym == :url
|
9
12
|
loc_label = displayLabel(location) || I18n.t('mods_display.location')
|
10
13
|
value = "<a href='#{child.text}'>#{(displayLabel(child) || child.text).gsub(/:$/, '')}</a>"
|
@@ -24,7 +27,7 @@ module ModsDisplay
|
|
24
27
|
private
|
25
28
|
|
26
29
|
def location_field_keys
|
27
|
-
[
|
30
|
+
%i[physicalLocation url shelfLocator holdingSimple holdingExternal]
|
28
31
|
end
|
29
32
|
|
30
33
|
def location_label(element)
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ModsDisplay
|
2
4
|
class Name < Field
|
3
5
|
include ModsDisplay::RelatorCodes
|
4
6
|
def fields
|
5
7
|
return_fields = @values.map do |value|
|
6
|
-
person = if value.displayForm.length
|
8
|
+
person = if value.displayForm.length.positive?
|
7
9
|
ModsDisplay::Name::Person.new(name: value.displayForm.text)
|
8
10
|
elsif !name_parts(value).empty?
|
9
11
|
ModsDisplay::Name::Person.new(name: name_parts(value))
|
@@ -40,11 +42,25 @@ module ModsDisplay
|
|
40
42
|
def role_labels(element)
|
41
43
|
default_label = I18n.t('mods_display.associated_with')
|
42
44
|
return [default_label] unless element.role.present? && element.role.roleTerm.present?
|
43
|
-
|
44
|
-
|
45
|
+
|
46
|
+
element.role.collect do |role|
|
47
|
+
codes, text = role.roleTerm.partition { |term| term['type'] == 'code' }
|
48
|
+
|
49
|
+
# prefer mappable role term codes
|
50
|
+
label = codes.map { |term| relator_codes[term.text.downcase] }.first
|
51
|
+
|
52
|
+
# but fall back to given text
|
53
|
+
label ||= text.map { |term| format_role(term) }.first
|
54
|
+
|
55
|
+
# or just the default
|
56
|
+
label || default_label
|
45
57
|
end.uniq
|
46
58
|
end
|
47
59
|
|
60
|
+
def format_role(element)
|
61
|
+
element.text.capitalize.sub(/[.,:;]+$/, '')
|
62
|
+
end
|
63
|
+
|
48
64
|
def role?(element)
|
49
65
|
element.respond_to?(:role) && !element.role.empty?
|
50
66
|
end
|
@@ -55,10 +71,9 @@ module ModsDisplay
|
|
55
71
|
end
|
56
72
|
|
57
73
|
def name_parts(element)
|
58
|
-
output =
|
59
|
-
|
60
|
-
|
61
|
-
qualified_name_parts(element, 'given')].flatten.compact.join(', ')
|
74
|
+
output = [unqualified_name_parts(element),
|
75
|
+
qualified_name_parts(element, 'family'),
|
76
|
+
qualified_name_parts(element, 'given')].flatten.compact.join(', ')
|
62
77
|
terms = qualified_name_parts(element, 'termsOfAddress')
|
63
78
|
unless terms.empty?
|
64
79
|
term_delimiter = ', '
|
@@ -66,9 +81,7 @@ module ModsDisplay
|
|
66
81
|
output = [output, terms.join(', ')].flatten.compact.join(term_delimiter)
|
67
82
|
end
|
68
83
|
dates = qualified_name_parts(element, 'date')
|
69
|
-
unless dates.empty?
|
70
|
-
output = [output, qualified_name_parts(element, 'date')].flatten.compact.join(', ')
|
71
|
-
end
|
84
|
+
output = [output, qualified_name_parts(element, 'date')].flatten.compact.join(', ') unless dates.empty?
|
72
85
|
output
|
73
86
|
end
|
74
87
|
|
@@ -90,7 +103,7 @@ module ModsDisplay
|
|
90
103
|
def name_part_begins_with_roman_numeral?(part)
|
91
104
|
first_part = part.split(/\s|,/).first.strip
|
92
105
|
first_part.chars.all? do |char|
|
93
|
-
%w
|
106
|
+
%w[I X C L V].include? char
|
94
107
|
end
|
95
108
|
end
|
96
109
|
|
@@ -98,14 +111,16 @@ module ModsDisplay
|
|
98
111
|
roles = element.role.map do |role|
|
99
112
|
role.roleTerm.find do |term|
|
100
113
|
term.attributes['type'].respond_to?(:value) &&
|
101
|
-
|
114
|
+
term.attributes['type'].value == 'text'
|
102
115
|
end
|
103
116
|
end.compact
|
104
|
-
roles
|
105
|
-
role.
|
106
|
-
|
107
|
-
|
108
|
-
|
117
|
+
if roles.empty?
|
118
|
+
roles = element.role.map do |role|
|
119
|
+
role.roleTerm.find do |term|
|
120
|
+
!term.attributes['type'].respond_to?(:value)
|
121
|
+
end
|
122
|
+
end.compact
|
123
|
+
end
|
109
124
|
roles.map { |t| t.text.strip }
|
110
125
|
end
|
111
126
|
|
@@ -130,7 +145,7 @@ module ModsDisplay
|
|
130
145
|
# Normalize label headings by filtering out some punctuation and ending in :
|
131
146
|
def normalize_labels(label_order, results)
|
132
147
|
label_order.uniq.map do |k|
|
133
|
-
label = k.tr('.', '').tr(':', '').strip
|
148
|
+
label = "#{k.tr('.', '').tr(':', '').strip}:"
|
134
149
|
if label != k
|
135
150
|
results[label] = results[k]
|
136
151
|
results.delete(k)
|
@@ -152,8 +167,9 @@ module ModsDisplay
|
|
152
167
|
|
153
168
|
class Person
|
154
169
|
attr_accessor :name
|
170
|
+
|
155
171
|
def initialize(data)
|
156
|
-
@name =
|
172
|
+
@name = data[:name]
|
157
173
|
end
|
158
174
|
|
159
175
|
def to_s
|