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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c13877b6d0839a4341a26072d23f3e31ed3d27e173f05bb0b1cbbc6e5c0d77b1
4
- data.tar.gz: d99b93bfb31352d75299990cea5810ce6668c06035fa5aa90d4ffd3f4fb31f0d
3
+ metadata.gz: a3c2bc98c15b659a075bccc6e3dea41e55f757f3318ad4d1e34109c314d10792
4
+ data.tar.gz: 3fc189b77cd0088578718fa5f1645e5390c0442825faacc51e7ec9de92f568c7
5
5
  SHA512:
6
- metadata.gz: 281488c21b60e419dc943a7af91a08f0ab32c4ba92e6ed276b1c86ab0b64722c87a4915f81b482e25c968ecc3855da7736b2b699a0dc6534635bf1822cf73ab1
7
- data.tar.gz: cebd18e35b4c81f9ac2dc3d2251a1bf9f8af7e2d9d36e1adc0561e33c48c5ca05faf057f8cd8df1943294aebd2a70914c9a25d7cf893e6aff3dd2e08061057af
6
+ metadata.gz: 2a35e824baad68b451eb64f126a3eea9eb61c0fd190c767933c4f6946336034d5f604fa8f81a24c1060ab9e29ed7c9d72c941f526b7bf42288870e51862d60fc
7
+ data.tar.gz: 9fbd968fe4409450090e9f3285a19f9eb86f3834ddf36061e6d4b5d5c617efaeb2996cdc88a8363a6cb29e79525403578e62266b0873423266d85ea484c461ed
data/Gemfile CHANGED
@@ -3,6 +3,7 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gem 'rubocop-rspec', require: false
6
+ gem 'debug'
6
7
 
7
8
  # Specify your gem's dependencies in mods_display.gemspec
8
9
  gemspec
@@ -39,163 +39,20 @@ module ModsDisplay
39
39
  end
40
40
  end
41
41
 
42
- def date_values(element)
43
- date_field_keys.map do |date_field|
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
- elements = element.send(date_field)
47
- next if elements.empty?
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[elements.first.name.to_sym],
51
- values: parse_dates(elements)
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
- date_elements = %w[dateIssued dateOther].map do |date_field_name|
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
- parse_dates(value.send(date_field_name.to_sym))
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
@@ -30,6 +30,11 @@ module ModsDisplay
30
30
  '<br />'
31
31
  end
32
32
 
33
+ # Override of Field#element_text to get text rather than HTML
34
+ def element_text(element)
35
+ element.text.strip
36
+ end
37
+
33
38
  def collapse_roles(fields)
34
39
  return [] if fields.blank?
35
40
 
@@ -3,9 +3,8 @@
3
3
  module ModsDisplay
4
4
  class Title < Field
5
5
  def fields
6
- return_values = []
7
- @values&.each do |value|
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
@@ -35,9 +35,8 @@ module ModsDisplay
35
35
  end
36
36
 
37
37
  def title
38
- return mods_field(:title).fields.first.values unless mods_field(:title).fields.empty?
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 tite natively
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ModsDisplay
4
- VERSION = '1.2.1'
4
+ VERSION = '1.3.1'
5
5
  end
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 medadata.'
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', '>= 3.0.0.alpha1', '< 4'
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.2.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: 2022-07-14 00:00:00.000000000 Z
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: '4'
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: '4'
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 medadata.
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.3.7
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