mods_display 1.2.1 → 1.3.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/mods_display/fields/imprint.rb +39 -158
- data/lib/mods_display/fields/name.rb +5 -0
- data/lib/mods_display/fields/title.rb +7 -3
- data/lib/mods_display/html.rb +3 -4
- data/lib/mods_display/version.rb +1 -1
- data/mods_display.gemspec +2 -3
- metadata +8 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3c2bc98c15b659a075bccc6e3dea41e55f757f3318ad4d1e34109c314d10792
|
4
|
+
data.tar.gz: 3fc189b77cd0088578718fa5f1645e5390c0442825faacc51e7ec9de92f568c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a35e824baad68b451eb64f126a3eea9eb61c0fd190c767933c4f6946336034d5f604fa8f81a24c1060ab9e29ed7c9d72c941f526b7bf42288870e51862d60fc
|
7
|
+
data.tar.gz: 9fbd968fe4409450090e9f3285a19f9eb86f3834ddf36061e6d4b5d5c617efaeb2996cdc88a8363a6cb29e79525403578e62266b0873423266d85ea484c461ed
|
data/Gemfile
CHANGED
@@ -39,163 +39,20 @@ module ModsDisplay
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def date_values(element)
|
43
|
-
|
44
|
-
next unless element.respond_to?(date_field)
|
42
|
+
def date_values(element, date_field_keys: %i[dateCreated dateCaptured dateValid dateModified copyrightDate])
|
43
|
+
imprint = Stanford::Mods::Imprint.new(element)
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
date_field_keys.map do |date_field|
|
46
|
+
date_values = imprint.dates([date_field])
|
47
|
+
next if date_values.empty?
|
48
48
|
|
49
49
|
ModsDisplay::Values.new(
|
50
|
-
label: displayLabel(element) || pub_info_labels[
|
51
|
-
values:
|
50
|
+
label: displayLabel(element) || pub_info_labels[date_field],
|
51
|
+
values: select_the_best_date(date_values)
|
52
52
|
)
|
53
53
|
end.compact
|
54
54
|
end
|
55
55
|
|
56
|
-
class DateValue
|
57
|
-
attr_reader :value
|
58
|
-
delegate :text, :date, :point, :qualifier, :encoding, to: :value
|
59
|
-
|
60
|
-
def initialize(value)
|
61
|
-
@value = value
|
62
|
-
end
|
63
|
-
|
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)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Element text reduced to digits and hyphen. Captures date ranges and
|
70
|
-
# negative (BCE) 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')
|
74
|
-
end
|
75
|
-
|
76
|
-
text.gsub(/(?<![\d])(\d{1,3})([xu-]{1,3})/i) { "#{$1}#{'0' * $2.length}"}.scan(/[\d-]/).join
|
77
|
-
end
|
78
|
-
|
79
|
-
# Decoded version of the date, if it was encoded. Strips leading zeroes.
|
80
|
-
def decoded_value
|
81
|
-
return text.strip unless date
|
82
|
-
|
83
|
-
unless encoding.present?
|
84
|
-
return text.strip unless text =~ /^-?\d+$/ || text =~ /^[\dXxu?-]{4}$/
|
85
|
-
end
|
86
|
-
|
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} BCE"
|
97
|
-
# Any dates before the year 1000 are explicitly marked CE
|
98
|
-
elsif year > 1 && year < 1000
|
99
|
-
"#{year} CE"
|
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
|
111
|
-
|
112
|
-
# Decoded date with "BCE" or "CE" 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'
|
120
|
-
|
121
|
-
date
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class DateRange
|
126
|
-
def initialize(start: nil, stop: nil)
|
127
|
-
@start = start
|
128
|
-
@stop = stop
|
129
|
-
end
|
130
|
-
|
131
|
-
# Base value as hyphen-joined string. Used for comparison/deduping.
|
132
|
-
def base_value
|
133
|
-
"#{@start&.base_value}-#{@stop&.base_value}"
|
134
|
-
end
|
135
|
-
|
136
|
-
# Base values as array. Used for comparison/deduping of individual dates.
|
137
|
-
def base_values
|
138
|
-
[@start&.base_value, @stop&.base_value].compact
|
139
|
-
end
|
140
|
-
|
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 "BCE" or "CE" 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
|
161
|
-
end
|
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?
|
178
|
-
|
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
|
185
|
-
else
|
186
|
-
group.last
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
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, CE/BCE, etc.
|
196
|
-
dates.map(&:qualified_value)
|
197
|
-
end
|
198
|
-
|
199
56
|
def other_pub_info(element)
|
200
57
|
element.children.select do |child|
|
201
58
|
pub_info_parts.include?(child.name.to_sym)
|
@@ -278,11 +135,14 @@ module ModsDisplay
|
|
278
135
|
compact_and_join_with_delimiter(publishers, ' : ')
|
279
136
|
end
|
280
137
|
|
281
|
-
def parts_element(value)
|
282
|
-
|
283
|
-
next unless value.respond_to?(date_field_name.to_sym)
|
138
|
+
def parts_element(value, date_field_keys: %i[dateIssued dateOther])
|
139
|
+
imprint = Stanford::Mods::Imprint.new(value)
|
284
140
|
|
285
|
-
|
141
|
+
date_elements = date_field_keys.map do |date_field|
|
142
|
+
date_values = imprint.dates([date_field])
|
143
|
+
next if date_values.empty?
|
144
|
+
|
145
|
+
select_the_best_date(date_values)
|
286
146
|
end.flatten.compact.reject do |date|
|
287
147
|
date.strip.empty?
|
288
148
|
end.map(&:strip)
|
@@ -293,10 +153,6 @@ module ModsDisplay
|
|
293
153
|
%i[issuance frequency]
|
294
154
|
end
|
295
155
|
|
296
|
-
def date_field_keys
|
297
|
-
%i[dateCreated dateCaptured dateValid dateModified copyrightDate]
|
298
|
-
end
|
299
|
-
|
300
156
|
def pub_info_labels
|
301
157
|
{ dateCreated: I18n.t('mods_display.date_created'),
|
302
158
|
dateCaptured: I18n.t('mods_display.date_captured'),
|
@@ -306,5 +162,30 @@ module ModsDisplay
|
|
306
162
|
issuance: I18n.t('mods_display.issuance'),
|
307
163
|
frequency: I18n.t('mods_display.frequency') }
|
308
164
|
end
|
165
|
+
|
166
|
+
def select_the_best_date(dates)
|
167
|
+
# ensure dates are unique with respect to their base values
|
168
|
+
dates = dates.group_by(&:base_value).map do |_value, group|
|
169
|
+
group.first if group.one?
|
170
|
+
|
171
|
+
# if one of the duplicates wasn't encoded, use that one. see:
|
172
|
+
# https://consul.stanford.edu/display/chimera/MODS+display+rules#MODSdisplayrules-3b.%3CoriginInfo%3E
|
173
|
+
if group.reject(&:encoding).any?
|
174
|
+
group.reject(&:encoding).first
|
175
|
+
|
176
|
+
# otherwise just randomly pick the last in the group
|
177
|
+
else
|
178
|
+
group.last
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# if any single dates are already part of a range, discard them
|
183
|
+
range_base_values = dates.select { |date| date.is_a?(Stanford::Mods::Imprint::DateRange) }
|
184
|
+
.map(&:base_values).flatten
|
185
|
+
dates = dates.reject { |date| range_base_values.include?(date.base_value) }
|
186
|
+
|
187
|
+
# output formatted dates with qualifiers, CE/BCE, etc.
|
188
|
+
dates.map(&:qualified_value)
|
189
|
+
end
|
309
190
|
end
|
310
191
|
end
|
@@ -3,9 +3,8 @@
|
|
3
3
|
module ModsDisplay
|
4
4
|
class Title < Field
|
5
5
|
def fields
|
6
|
-
return_values =
|
7
|
-
|
8
|
-
return_values << ModsDisplay::Values.new(
|
6
|
+
return_values = sorted_values.map do |value|
|
7
|
+
ModsDisplay::Values.new(
|
9
8
|
label: displayLabel(value) || title_label(value),
|
10
9
|
values: [assemble_title(value)]
|
11
10
|
)
|
@@ -15,6 +14,11 @@ module ModsDisplay
|
|
15
14
|
|
16
15
|
private
|
17
16
|
|
17
|
+
# If there is a node with usage="primary", then it should come first.
|
18
|
+
def sorted_values
|
19
|
+
Array(@values).sort_by { |node| node['usage'] == 'primary' ? 0 : 1 }
|
20
|
+
end
|
21
|
+
|
18
22
|
def delimiter
|
19
23
|
'<br />'.html_safe
|
20
24
|
end
|
data/lib/mods_display/html.rb
CHANGED
@@ -35,9 +35,8 @@ module ModsDisplay
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def title
|
38
|
-
|
39
|
-
|
40
|
-
''
|
38
|
+
title_fields = mods_field(:title).fields
|
39
|
+
title_fields.empty? ? '' : title_fields.first.values
|
41
40
|
end
|
42
41
|
|
43
42
|
def render_in(view_context)
|
@@ -45,7 +44,7 @@ module ModsDisplay
|
|
45
44
|
end
|
46
45
|
|
47
46
|
# Need to figure out how to get the 1st title out of the list.
|
48
|
-
# Maybe have a separate class that will omit the first
|
47
|
+
# Maybe have a separate class that will omit the first title natively
|
49
48
|
# and replace the first key in the the fields list with that.
|
50
49
|
def body(view_context = ApplicationController.renderer)
|
51
50
|
view_context.render ModsDisplay::RecordComponent.new(record: self), layout: false
|
data/lib/mods_display/version.rb
CHANGED
data/mods_display.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.version = ModsDisplay::VERSION
|
7
7
|
gem.authors = ['Jessie Keck']
|
8
8
|
gem.email = ['jessie.keck@gmail.com']
|
9
|
-
gem.description = 'MODS Display is a gem to centralize the display logic of MODS
|
9
|
+
gem.description = 'MODS Display is a gem to centralize the display logic of MODS metadata.'
|
10
10
|
gem.summary = 'The MODS Display gem allows implementers to configure a customized display of MODS metadata. This display implements the specifications defined at Stanford for how to display MODS.'
|
11
11
|
gem.homepage = 'https://github.com/sul-dlss/mods_display'
|
12
12
|
|
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.executables = gem.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
22
22
|
gem.require_paths = ["lib"]
|
23
23
|
|
24
|
-
gem.add_dependency 'stanford-mods', '
|
24
|
+
gem.add_dependency 'stanford-mods', '~> 3.3'
|
25
25
|
gem.add_dependency 'i18n'
|
26
26
|
gem.add_dependency 'view_component'
|
27
27
|
|
@@ -30,7 +30,6 @@ Gem::Specification.new do |gem|
|
|
30
30
|
gem.add_development_dependency 'rspec-rails'
|
31
31
|
gem.add_development_dependency 'rubocop'
|
32
32
|
gem.add_development_dependency 'capybara'
|
33
|
-
gem.add_development_dependency 'byebug'
|
34
33
|
gem.add_development_dependency 'rails', ENV['RAILS_VERSION'] || '~> 6.0'
|
35
34
|
gem.add_development_dependency 'combustion', '~> 1.3'
|
36
35
|
end
|
metadata
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mods_display
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jessie Keck
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: stanford-mods
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 3.0.0.alpha1
|
20
|
-
- - "<"
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
19
|
+
version: '3.3'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 3.0.0.alpha1
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
26
|
+
version: '3.3'
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: i18n
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,20 +122,6 @@ dependencies:
|
|
128
122
|
- - ">="
|
129
123
|
- !ruby/object:Gem::Version
|
130
124
|
version: '0'
|
131
|
-
- !ruby/object:Gem::Dependency
|
132
|
-
name: byebug
|
133
|
-
requirement: !ruby/object:Gem::Requirement
|
134
|
-
requirements:
|
135
|
-
- - ">="
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
version: '0'
|
138
|
-
type: :development
|
139
|
-
prerelease: false
|
140
|
-
version_requirements: !ruby/object:Gem::Requirement
|
141
|
-
requirements:
|
142
|
-
- - ">="
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: '0'
|
145
125
|
- !ruby/object:Gem::Dependency
|
146
126
|
name: rails
|
147
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -170,7 +150,7 @@ dependencies:
|
|
170
150
|
- - "~>"
|
171
151
|
- !ruby/object:Gem::Version
|
172
152
|
version: '1.3'
|
173
|
-
description: MODS Display is a gem to centralize the display logic of MODS
|
153
|
+
description: MODS Display is a gem to centralize the display logic of MODS metadata.
|
174
154
|
email:
|
175
155
|
- jessie.keck@gmail.com
|
176
156
|
executables: []
|
@@ -245,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
225
|
- !ruby/object:Gem::Version
|
246
226
|
version: '0'
|
247
227
|
requirements: []
|
248
|
-
rubygems_version: 3.
|
228
|
+
rubygems_version: 3.4.6
|
249
229
|
signing_key:
|
250
230
|
specification_version: 4
|
251
231
|
summary: The MODS Display gem allows implementers to configure a customized display
|