mods_display 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cd385f9d4aea6c14524378831877c327c6fdfd2664356e148b457af9c26a5623
4
- data.tar.gz: b66857f71c766dc6edb03b8783d1d63486e94c63c8e9a71a0474f6b5a11d44cc
3
+ metadata.gz: 8f82b0048ccbf7b94b1c15326c364b4133adf8c241145e98809e6e258cc15164
4
+ data.tar.gz: '04349a0206e891f2ab84d23c7a182e5a5bd1b1e8227bb08deda07d23824b4511'
5
5
  SHA512:
6
- metadata.gz: 6b4760e8eee0c1eef22badaf5087b4095c4c88766c3b128bbaa1e841e2b1aa34294ced0be7199875b6aa57cdf1b3385dd5c11d624f4ce818afcc7fa4f1da25ab
7
- data.tar.gz: 93c93c4249b55816c9716b1c632d0b4c5b5a2874365e33139fbbf6cd9421f01d3aa9800d347c72ae7837bcf98ef763650e8151a3cab547cecb287a72ff7a9ace
6
+ metadata.gz: 5e8ae4be6ab26e6e30fa74f0b71cc3a03c19ca33906770c5cf5c9819ccf11be9c92281e5b6443b8cb8905b0df285c93470a963d2c11ad2a37c1390166b77ee54
7
+ data.tar.gz: 0e982271c57bdc0058dc5f005f2f0a9a84962e4ce01f1e0740bb3143e9332c2b8f4a398adc190520dd5509bdd8b85eb58bdeeaf8544910405759a703ff121198
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # ModsDisplay
2
2
 
3
- A gem for displaying MODS Metadata in a configurable way.
3
+ A gem for displaying MODS Metadata in a configurable way.
4
+
5
+ This gem is used to centralize the display logic of MODS metadata by creating html fragments such as
6
+
7
+ ```
8
+ <dt>Publisher</dt>
9
+ <dd>Diagon Alley Publications</dd>
10
+ ```
11
+
12
+ which can be styled in consuming apps such as SearchWorks or Exhibits.
4
13
 
5
14
  ## Demo
6
15
 
@@ -20,23 +29,6 @@ Or install it yourself as:
20
29
 
21
30
  $ gem install mods_display
22
31
 
23
- ## Release/Upgrade Notes
24
-
25
- #### v0.5.0
26
- There are three major changes in this version.
27
-
28
- 1. RelatedItem nodes with a type of `constituent` or `host` are now treated separately and will render the full MODS display of any nested metadata. If accessing the `ModsDisplay::Values` directly through their accessors (e.g. custom grouping), this new metadata is available under `.nested_related_items`.
29
- * _**Note:** You may want to style and add some javascript toggling behavior (see exhibits for an example)._
30
- 2. Name nodes are now grouped/labeled by their role. If you were iterating over the names and splitting them out by their labels, that will need to change.
31
- 3. Table of contents/Summaries are now split on `--` and rendered as an unordered list.
32
- * _**Note:** You may want to style this list._
33
-
34
- #### v0.3.0
35
-
36
- Labels now have internationalization support. We have added colons to the english labels due to certain languages' punctuation rules requiring different spacing between the label and colon.
37
-
38
- Given that fact, you will want to update any pre 0.3.0 code that searches for elements by label in a way that would fail with the presence of a colon.
39
-
40
32
  ## Contributing
41
33
 
42
34
  1. Fork it
@@ -0,0 +1,11 @@
1
+ <% values.each do |value| %>
2
+ <tr>
3
+ <% if label %>
4
+ <%= tag.th label, **label_html_attributes %>
5
+ <% end %>
6
+
7
+ <%= tag.td **value_html_attributes do %>
8
+ <%= value %>
9
+ <% end %>
10
+ </tr>
11
+ <% end %>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class RowFieldComponent < ViewComponent::Base
5
+ with_collection_parameter :field
6
+
7
+ def initialize(field:, delimiter: nil, label_html_attributes: {}, value_html_attributes: {}, value_transformer: nil)
8
+ super
9
+
10
+ @field = field
11
+ @delimiter = delimiter
12
+ @value_transformer = value_transformer
13
+ @label_html_attributes = label_html_attributes
14
+ @value_html_attributes = value_html_attributes
15
+ end
16
+
17
+ attr_reader :field, :delimiter, :label_html_attributes, :value_html_attributes
18
+
19
+ def render?
20
+ field.values.any?(&:present?)
21
+ end
22
+
23
+ def format_value(value)
24
+ if @value_transformer
25
+ @value_transformer.call(value)
26
+ else
27
+ helpers.format_mods_html(value, field: field)
28
+ end
29
+ end
30
+
31
+ def values
32
+ if delimiter
33
+ [safe_join(field.values.select(&:present?).map { |value| format_value(value) }, delimiter)]
34
+ else
35
+ field.values.select(&:present?).map { |value| format_value(value) }
36
+ end
37
+ end
38
+
39
+ def label
40
+ field.label&.sub(/:$/, '')
41
+ end
42
+ end
43
+ end
@@ -17,6 +17,14 @@ module ModsDisplay
17
17
  render component.new(field: field, delimiter: delimiter, value_transformer: block)
18
18
  end
19
19
 
20
+ def mods_record_definition_field(field, delimiter: nil, label_html_attributes: {}, &block)
21
+ render ModsDisplay::FieldComponent.new(field: field, delimiter: delimiter, label_html_attributes: label_html_attributes, value_transformer: block)
22
+ end
23
+
24
+ def mods_record_row_field(field, delimiter: ', ', &block)
25
+ render ModsDisplay::RowFieldComponent.new(field: field, delimiter: delimiter, value_transformer: block)
26
+ end
27
+
20
28
  # this returns a role's label and the display names for ModsDisplay:Name:Person
21
29
  def mods_name_field(field)
22
30
  mods_record_field(field) do |name|
@@ -41,12 +49,24 @@ module ModsDisplay
41
49
  end
42
50
  end
43
51
 
52
+ def mods_subject_row_field(field, delimiter: nil, &block)
53
+ mods_record_row_field(field, delimiter: delimiter) do |subject_line|
54
+ safe_join(link_mods_subjects(subject_line, &block), ' > ')
55
+ end
56
+ end
57
+
44
58
  def mods_genre_field(field, &block)
45
59
  mods_record_field(field) do |genre_line|
46
60
  link_to_mods_subject(genre_line, &block)
47
61
  end
48
62
  end
49
63
 
64
+ def mods_genre_row_field(field, delimiter: nil, &block)
65
+ mods_record_row_field(field, delimiter: delimiter) do |genre_line|
66
+ link_to_mods_subject(genre_line, &block)
67
+ end
68
+ end
69
+
50
70
  # @private
51
71
  def link_mods_genres(genre, &block)
52
72
  link_buffer = []
@@ -17,11 +17,13 @@ en:
17
17
  creation_production_credits: "Creation/Production credits:"
18
18
  date_captured: "Date captured:"
19
19
  date_created: "Date created:"
20
+ date_issued: "Publication date:"
20
21
  date_modified: "Date modified:"
21
22
  date_sequential_designation: "Date/Sequential designation:"
22
23
  date_valid: "Date valid:"
23
24
  digital_origin: "Digital origin:"
24
25
  doi: "DOI:"
26
+ edition: "Edition:"
25
27
  extent: "Extent:"
26
28
  form: "Form:"
27
29
  frequency: "Frequency:"
@@ -18,7 +18,7 @@ module ModsDisplay
18
18
  [projection, coordinates].compact.join(' ')
19
19
  end
20
20
  return_fields << ModsDisplay::Values.new(
21
- label: (displayLabel(field) || label || I18n.t('mods_display.map_data')),
21
+ label: displayLabel(field) || label || I18n.t('mods_display.map_data'),
22
22
  values: [[scale, post_scale].compact.join(' ; ')]
23
23
  )
24
24
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class CopyrightDate < Field
5
+ def fields
6
+ date_fields(:copyrightDate)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class DateCaptured < Field
5
+ def fields
6
+ date_fields(:dateCaptured)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class DateCreated < Field
5
+ def fields
6
+ date_fields(:dateCreated)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class DateIssued < Field
5
+ def fields
6
+ date_fields(:dateIssued)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class DateModified < Field
5
+ def fields
6
+ date_fields(:dateModified)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class DateValid < Field
5
+ def fields
6
+ date_fields(:dateValid)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class Edition < Field
5
+ def fields
6
+ return_fields = @values.map do |value|
7
+ edition_value = Stanford::Mods::Imprint.new(value).edition_vals_str
8
+ next unless edition_value.present?
9
+
10
+ # remove trailing spaces (thanks MARC for catalog card formatting!)
11
+ edition_value.gsub!(%r{ */$}, '')
12
+
13
+ ModsDisplay::Values.new(
14
+ label: I18n.t('mods_display.edition'),
15
+ values: [edition_value],
16
+ field: self
17
+ )
18
+ end.compact
19
+ collapse_fields(return_fields)
20
+ end
21
+ end
22
+ end
@@ -57,5 +57,46 @@ module ModsDisplay
57
57
  def element_text(element)
58
58
  element.xpath('.//text()').to_html.strip
59
59
  end
60
+
61
+ # used for originInfo date fields, e.g. DateCreated, DateIssued ...
62
+ def date_fields(date_symbol)
63
+ return_fields = @values.map do |value|
64
+ date_values = Stanford::Mods::Imprint.new(value).dates([date_symbol])
65
+ next unless date_values.present?
66
+
67
+ ModsDisplay::Values.new(
68
+ label: I18n.t("mods_display.#{date_symbol.to_s.underscore}"),
69
+ values: select_best_date(date_values),
70
+ field: self
71
+ )
72
+ end.compact
73
+ collapse_fields(return_fields)
74
+ end
75
+
76
+ # used for originInfo dates, e.g. for Imprint, DateCreated, DateIssued, etc.
77
+ def select_best_date(dates)
78
+ # ensure dates are unique with respect to their base values
79
+ dates = dates.group_by(&:base_value).map do |_value, group|
80
+ group.first if group.one?
81
+
82
+ # if one of the duplicates wasn't encoded, use that one. see:
83
+ # https://consul.stanford.edu/display/chimera/MODS+display+rules#MODSdisplayrules-3b.%3CoriginInfo%3E
84
+ if group.reject(&:encoding).any?
85
+ group.reject(&:encoding).first
86
+
87
+ # otherwise just randomly pick the last in the group
88
+ else
89
+ group.last
90
+ end
91
+ end
92
+
93
+ # if any single dates are already part of a range, discard them
94
+ range_base_values = dates.select { |date| date.is_a?(Stanford::Mods::Imprint::DateRange) }
95
+ .map(&:base_values).flatten
96
+ dates = dates.reject { |date| range_base_values.include?(date.base_value) }
97
+
98
+ # output formatted dates with qualifiers, CE/BCE, etc.
99
+ dates.map(&:qualified_value)
100
+ end
60
101
  end
61
102
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class Frequency < Field
5
+ def fields
6
+ return_fields = @values.map do |origin_info_element|
7
+ frequency_value = origin_info_element.frequency&.text&.strip
8
+ next unless frequency_value.present?
9
+
10
+ ModsDisplay::Values.new(
11
+ label: I18n.t('mods_display.frequency'),
12
+ values: [frequency_value],
13
+ field: self
14
+ )
15
+ end.compact
16
+ collapse_fields(return_fields)
17
+ end
18
+ end
19
+ end
@@ -48,7 +48,7 @@ module ModsDisplay
48
48
 
49
49
  ModsDisplay::Values.new(
50
50
  label: displayLabel(element) || pub_info_labels[date_field],
51
- values: select_the_best_date(date_values)
51
+ values: select_best_date(date_values)
52
52
  )
53
53
  end.compact
54
54
  end
@@ -117,6 +117,8 @@ module ModsDisplay
117
117
  end.map(&:text).join(' ').strip
118
118
  end
119
119
 
120
+ # not an exact duplicate of the method in ModsDisplay::Place, particularly trailing punctuation code
121
+ # as ModsDisplay::Place is not intended to combine with publisher in an imprint string
120
122
  def place_element(value)
121
123
  return if value.place.text.strip.empty?
122
124
 
@@ -142,7 +144,7 @@ module ModsDisplay
142
144
  date_values = imprint.dates([date_field])
143
145
  next if date_values.empty?
144
146
 
145
- select_the_best_date(date_values)
147
+ select_best_date(date_values)
146
148
  end.flatten.compact.reject do |date|
147
149
  date.strip.empty?
148
150
  end.map(&:strip)
@@ -162,30 +164,5 @@ module ModsDisplay
162
164
  issuance: I18n.t('mods_display.issuance'),
163
165
  frequency: I18n.t('mods_display.frequency') }
164
166
  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
190
167
  end
191
168
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class Issuance < Field
5
+ def fields
6
+ return_fields = @values.map do |origin_info_element|
7
+ issuance_value = origin_info_element.issuance&.text&.strip
8
+ next unless issuance_value.present?
9
+
10
+ ModsDisplay::Values.new(
11
+ label: displayLabel(origin_info_element) || I18n.t('mods_display.issuance'),
12
+ values: [issuance_value],
13
+ field: self
14
+ )
15
+ end.compact
16
+ collapse_fields(return_fields)
17
+ end
18
+ end
19
+ end
@@ -8,59 +8,66 @@ module ModsDisplay
8
8
  class NestedRelatedItem < Field
9
9
  include ModsDisplay::RelatedItemConcerns
10
10
 
11
+ def initialize(values, value_renderer: ValueRenderer)
12
+ super(values)
13
+ @value_renderer = value_renderer
14
+ end
15
+
11
16
  def fields
12
17
  @fields ||= begin
13
18
  return_fields = RelatedItemValue.for_values(@values).map do |value|
14
19
  next if value.collection?
15
20
  next unless render_nested_related_item?(value)
16
21
 
17
- related_item_mods_object(value)
22
+ related_item_text = @value_renderer.new(value).render
23
+
24
+ ModsDisplay::Values.new(
25
+ label: related_item_label(value),
26
+ values: [related_item_text]
27
+ )
18
28
  end.compact
19
29
  collapse_fields(return_fields)
20
30
  end
21
31
  end
22
32
 
23
- def to_html(view_context = ApplicationController.renderer)
24
- helpers = view_context.respond_to?(:simple_format) ? view_context : ApplicationController.new.view_context
33
+ class ValueRenderer
34
+ def initialize(value)
35
+ @value = value
36
+ end
25
37
 
26
- component = ModsDisplay::ListFieldComponent.with_collection(
27
- fields,
28
- value_transformer: ->(value) { helpers.format_mods_html(value.to_s) },
29
- list_html_attributes: { class: 'mods_display_nested_related_items' },
30
- list_item_html_attributes: { class: 'mods_display_nested_related_item open' }
31
- )
38
+ def render
39
+ [Array.wrap(mods_display_html.title).first, body_presence(mods_display_html.body)].compact.join
40
+ end
32
41
 
33
- view_context.render component, layout: false
34
- end
42
+ protected
35
43
 
36
- private
44
+ attr_reader :value
37
45
 
38
- def related_item_mods_object(value)
39
- mods = ::Stanford::Mods::Record.new.tap do |r|
40
- # dup'ing the value adds the appropriate namespaces, but...
41
- munged_node = value.dup.tap do |x|
42
- # ... the mods gem also expects the root of the document to have the root tag <mods>
43
- x.name = 'mods'
44
- end
45
-
46
- r.from_nk_node(munged_node)
46
+ def mods_display_html
47
+ @mods_display_html ||= ModsDisplay::HTML.new(mods)
47
48
  end
48
- related_item = ModsDisplay::HTML.new(mods)
49
49
 
50
- ModsDisplay::Values.new(
51
- label: related_item_label(value),
52
- values: [[Array.wrap(related_item.title).first, related_item_body(related_item)].compact.join]
53
- )
54
- end
50
+ def mods
51
+ @mods ||= ::Stanford::Mods::Record.new.tap do |r|
52
+ # dup'ing the value adds the appropriate namespaces, but...
53
+ munged_node = value.dup.tap do |x|
54
+ # ... the mods gem also expects the root of the document to have the root tag <mods>
55
+ x.name = 'mods'
56
+ end
55
57
 
56
- def related_item_body(related_item)
57
- body = related_item.body
58
+ r.from_nk_node(munged_node)
59
+ end
60
+ end
58
61
 
59
- return if body == '<dl></dl>'
62
+ def body_presence(body)
63
+ return if body == '<dl></dl>'
60
64
 
61
- body
65
+ body
66
+ end
62
67
  end
63
68
 
69
+ private
70
+
64
71
  def related_item_label(item)
65
72
  return displayLabel(item) if displayLabel(item)
66
73
  return I18n.t('mods_display.constituent') if item.constituent?
@@ -18,9 +18,9 @@ module ModsDisplay
18
18
 
19
19
  def note_fields
20
20
  @values.select do |value|
21
- (!value.attributes['type'].respond_to?(:value) ||
22
- (value.attributes['type'].respond_to?(:value) &&
23
- value.attributes['type'].value.downcase != 'contact'))
21
+ !value.attributes['type'].respond_to?(:value) ||
22
+ (value.attributes['type'].respond_to?(:value) &&
23
+ value.attributes['type'].value.downcase != 'contact')
24
24
  end
25
25
  end
26
26
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class Place < Field
5
+ include ModsDisplay::CountryCodes
6
+
7
+ def fields
8
+ return_fields = @values.map do |value|
9
+ place_value = place_element(value)
10
+ next unless place_value.present?
11
+
12
+ ModsDisplay::Values.new(
13
+ label: I18n.t('mods_display.place'),
14
+ values: [place_value],
15
+ field: self
16
+ )
17
+ end.compact
18
+ collapse_fields(return_fields)
19
+ end
20
+
21
+ private
22
+
23
+ # not an exact duplicate of the method in Imprint, particularly trailing punctuation code
24
+ def place_element(value)
25
+ return if value.place.text.strip.empty?
26
+
27
+ places = ModsDisplay::Imprint.new(value).place_terms(value).filter_map { |p| p.text unless p.text.strip.empty? }
28
+ compact_and_remove_trailing_delimiter(places, ':').join
29
+ end
30
+
31
+ def compact_and_remove_trailing_delimiter(values, delimiter = ':')
32
+ values.flatten.filter_map { |v| v.gsub(/ *#{delimiter}$/, '') if v.present? }.compact
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class Publisher < Field
5
+ def fields
6
+ return_fields = @values.map do |value|
7
+ publisher_value = Stanford::Mods::Imprint.new(value).publisher_vals_str
8
+ next unless publisher_value.present?
9
+
10
+ publisher_value.gsub!(/ *,$/, '')
11
+
12
+ ModsDisplay::Values.new(
13
+ label: I18n.t('mods_display.publisher'),
14
+ values: [publisher_value],
15
+ field: self
16
+ )
17
+ end.compact
18
+ collapse_fields(return_fields)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ModsDisplay
4
+ class ReferenceTitle < Field
5
+ def fields
6
+ # Reference title is a composite field that includes parts from elsewhere in the record.
7
+ return [] unless @values.present?
8
+
9
+ return_fields = [ModsDisplay::Values.new(label: displayLabel(@values.first) || label, values: [title_value].compact)]
10
+ collapse_fields(return_fields)
11
+ end
12
+
13
+ private
14
+
15
+ def root
16
+ @root ||= @values.first.document.root
17
+ end
18
+
19
+ def displayLabel(element)
20
+ super(element) || I18n.t('mods_display.referenced_by')
21
+ end
22
+
23
+ def title_value
24
+ [@values,
25
+ root.xpath('mods:originInfo/mods:dateOther', mods: MODS_NS),
26
+ root.xpath('mods:part/mods:detail/mods:number', mods: MODS_NS),
27
+ root.xpath('mods:note', mods: MODS_NS)].flatten.compact.map!(&:text).map!(&:strip).join(' ').presence
28
+ end
29
+ end
30
+ end
@@ -4,12 +4,17 @@ module ModsDisplay
4
4
  class RelatedItem < Field
5
5
  include ModsDisplay::RelatedItemConcerns
6
6
 
7
+ def initialize(values, value_renderer: ValueRenderer)
8
+ super(values)
9
+ @value_renderer = value_renderer
10
+ end
11
+
7
12
  def fields
8
13
  return_fields = RelatedItemValue.for_values(@values).map do |value|
9
14
  next if value.collection?
10
15
  next if render_nested_related_item?(value)
11
16
 
12
- text = related_item_value(value)
17
+ text = @value_renderer.new(value).render
13
18
 
14
19
  next if text.nil? || text.empty?
15
20
 
@@ -18,41 +23,57 @@ module ModsDisplay
18
23
  collapse_fields(return_fields)
19
24
  end
20
25
 
21
- private
26
+ class ValueRenderer
27
+ def initialize(value)
28
+ @value = value
29
+ end
22
30
 
23
- def delimiter
24
- '<br />'.html_safe
25
- end
31
+ def render
32
+ if value.location?
33
+ element_text(value.location_nodeset)
34
+ elsif value.reference?
35
+ reference_title(value)
36
+ elsif value.titleInfo_nodeset.any?
37
+ title = element_text(value.titleInfo_nodeset)
38
+ location = nil
39
+ location = element_text(value.location_url_nodeset) if value.location_url_nodeset.length.positive?
40
+
41
+ return if title.empty?
26
42
 
27
- def related_item_value(item)
28
- if item.location?
29
- element_text(item.location_nodeset)
30
- elsif item.reference?
31
- reference_title(item)
32
- elsif item.titleInfo_nodeset.any?
33
- title = element_text(item.titleInfo_nodeset)
34
- location = nil
35
- location = element_text(item.location_url_nodeset) if item.location_url_nodeset.length.positive?
36
-
37
- return if title.empty?
38
-
39
- if location
40
- "<a href='#{location}'>#{title}</a>".html_safe
41
- else
42
- title
43
+ if location
44
+ "<a href='#{location}'>#{title}</a>".html_safe
45
+ else
46
+ title
47
+ end
48
+ elsif value.note_nodeset.any?
49
+ citation = value.note_nodeset.find { |note| note['type'] == 'preferred citation' }
50
+
51
+ element_text(citation) if citation
43
52
  end
44
- elsif item.note_nodeset.any?
45
- citation = item.note_nodeset.find { |note| note['type'] == 'preferred citation' }
53
+ end
46
54
 
47
- element_text(citation) if citation
55
+ protected
56
+
57
+ attr_reader :value
58
+
59
+ def element_text(element)
60
+ element.xpath('.//text()').to_html.strip
61
+ end
62
+
63
+ def reference_title(item)
64
+ [item.titleInfo_nodeset,
65
+ item.originInfo.dateOther,
66
+ item.part.detail.number,
67
+ item.note_nodeset].flatten.compact.map!(&:text).map!(&:strip).join(' ')
48
68
  end
49
69
  end
50
70
 
51
- def reference_title(item)
52
- [item.titleInfo_nodeset,
53
- item.originInfo.dateOther,
54
- item.part.detail.number,
55
- item.note_nodeset].flatten.compact.map!(&:text).map!(&:strip).join(' ')
71
+ private
72
+
73
+ attr_reader :value_renderer
74
+
75
+ def delimiter
76
+ '<br />'.html_safe
56
77
  end
57
78
 
58
79
  def related_item_label(item)
@@ -2,6 +2,11 @@
2
2
 
3
3
  module ModsDisplay
4
4
  class Title < Field
5
+ def initialize(values, link: nil)
6
+ @link = link
7
+ super(values)
8
+ end
9
+
5
10
  def fields
6
11
  return_values = sorted_values.map do |value|
7
12
  ModsDisplay::Values.new(
@@ -31,21 +36,7 @@ module ModsDisplay
31
36
  str = value.text.strip
32
37
  next if str.empty?
33
38
 
34
- delimiter = if title.empty? || title.end_with?(' ')
35
- nil
36
- elsif previous_element&.name == 'nonSort' && title.end_with?('-', '\'')
37
- nil
38
- elsif title.end_with?('.', ',', ':', ';')
39
- ' '
40
- elsif value.name == 'subTitle'
41
- ' : '
42
- elsif value.name == 'partName' && previous_element.name == 'partNumber'
43
- ', '
44
- elsif value.name == 'partNumber' || value.name == 'partName'
45
- '. '
46
- else
47
- ' '
48
- end
39
+ delimiter = title_delimiter(title, previous_element, value)
49
40
 
50
41
  title += delimiter if delimiter
51
42
  title += str
@@ -53,13 +44,38 @@ module ModsDisplay
53
44
  previous_element = value
54
45
  end
55
46
 
56
- if element['type'] == 'uniform' && element['nameTitleGroup'].present?
57
- [uniform_title_name_part(element), title].compact.join('. ')
47
+ full_title = if element['type'] == 'uniform' && element['nameTitleGroup'].present?
48
+ [uniform_title_name_part(element), title].compact.join('. ')
49
+ else
50
+ title
51
+ end
52
+ linked_title(full_title)
53
+ end
54
+
55
+ def title_delimiter(title, previous_element, value)
56
+ if title.empty? || title.end_with?(' ')
57
+ nil
58
+ elsif previous_element&.name == 'nonSort' && title.end_with?('-', '\'')
59
+ nil
60
+ elsif title.end_with?('.', ',', ':', ';')
61
+ ' '
62
+ elsif value.name == 'subTitle'
63
+ ' : '
64
+ elsif value.name == 'partName' && previous_element.name == 'partNumber'
65
+ ', '
66
+ elsif value.name == 'partNumber' || value.name == 'partName'
67
+ '. '
58
68
  else
59
- title
69
+ ' '
60
70
  end
61
71
  end
62
72
 
73
+ def linked_title(title)
74
+ return title unless @link
75
+
76
+ "<a href=\"#{@link}\">#{title}</a>"
77
+ end
78
+
63
79
  def uniform_title_name_part(element)
64
80
  names = element.xpath("//m:name[@nameTitleGroup=\"#{element['nameTitleGroup']}\"]", **Mods::Record::NS_HASH)
65
81
  names = element.xpath("//name[@nameTitleGroup=\"#{element['nameTitleGroup']}\"]") if names.empty?
@@ -5,13 +5,25 @@ module ModsDisplay
5
5
  MODS_DISPLAY_FIELD_MAPPING = {
6
6
  title: :title_info,
7
7
  subTitle: :title_info,
8
+ referenceTitle: :title_info,
8
9
  name: :plain_name,
9
10
  resourceType: :typeOfResource,
10
11
  genre: :genre,
11
12
  form: :physical_description,
12
13
  extent: :physical_description,
13
14
  geo: :extension,
15
+ copyrightDate: :origin_info,
16
+ dateCaptured: :origin_info,
17
+ dateCreated: :origin_info,
18
+ dateIssued: :origin_info,
19
+ dateModified: :origin_info,
20
+ dateValid: :origin_info,
21
+ edition: :origin_info,
22
+ frequency: :origin_info,
14
23
  imprint: :origin_info,
24
+ issuance: :origin_info,
25
+ place: :origin_info,
26
+ publisher: :origin_info,
15
27
  language: :language,
16
28
  description: :physical_description,
17
29
  cartographics: :subject,
@@ -29,11 +41,13 @@ module ModsDisplay
29
41
  accessCondition: :accessCondition
30
42
  }.freeze
31
43
 
32
- def initialize(xml)
33
- @stanford_mods = xml
34
- @xml = xml.mods_ng_xml
44
+ def initialize(mods)
45
+ @mods = mods
46
+ @xml = mods.mods_ng_xml
35
47
  end
36
48
 
49
+ attr_reader :xml
50
+
37
51
  def title
38
52
  title_fields = mods_field(:title).fields
39
53
  title_fields.empty? ? '' : title_fields.first.values
@@ -46,8 +60,8 @@ module ModsDisplay
46
60
  # Need to figure out how to get the 1st title out of the list.
47
61
  # Maybe have a separate class that will omit the first title natively
48
62
  # and replace the first key in the the fields list with that.
49
- def body(view_context = ApplicationController.renderer)
50
- view_context.render ModsDisplay::RecordComponent.new(record: self), layout: false
63
+ def body(view_context = ApplicationController.renderer, html_attributes: {}, component: ModsDisplay::RecordComponent)
64
+ view_context.render component.new(record: self, html_attributes: html_attributes), layout: false
51
65
  end
52
66
 
53
67
  # @deprecated
@@ -56,22 +70,22 @@ module ModsDisplay
56
70
  view_context.render ModsDisplay::RecordComponent.new(record: self, fields: fields), layout: false
57
71
  end
58
72
 
59
- MODS_DISPLAY_FIELD_MAPPING.each do |key, _value|
73
+ MODS_DISPLAY_FIELD_MAPPING.each_key do |key|
60
74
  next if key == :title
61
75
 
62
- define_method(key) do |raw: false|
63
- field = mods_field(key)
76
+ define_method(key) do |raw: false, **field_args|
77
+ field = mods_field(key, field_args: field_args)
64
78
  next field if raw
65
79
 
66
80
  field.fields
67
81
  end
68
82
  end
69
83
 
70
- def mods_field(key)
84
+ def mods_field(key, field_args: {})
71
85
  raise ArgumentError unless MODS_DISPLAY_FIELD_MAPPING[key] && @xml.respond_to?(MODS_DISPLAY_FIELD_MAPPING[key])
72
86
 
73
87
  field = @xml.public_send(MODS_DISPLAY_FIELD_MAPPING[key])
74
- mods_field_class(key).new(field)
88
+ mods_field_class(key).new(field, **field_args)
75
89
  end
76
90
 
77
91
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ModsDisplay
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.0'
5
5
  end
data/lib/mods_display.rb CHANGED
@@ -13,20 +13,32 @@ require 'mods_display/fields/audience'
13
13
  require 'mods_display/fields/collection'
14
14
  require 'mods_display/fields/contact'
15
15
  require 'mods_display/fields/contents'
16
+ require 'mods_display/fields/copyright_date'
16
17
  require 'mods_display/fields/cartographics'
18
+ require 'mods_display/fields/date_created'
19
+ require 'mods_display/fields/date_captured'
20
+ require 'mods_display/fields/date_issued'
21
+ require 'mods_display/fields/date_modified'
22
+ require 'mods_display/fields/date_valid'
17
23
  require 'mods_display/fields/description'
24
+ require 'mods_display/fields/edition'
18
25
  require 'mods_display/fields/extent'
26
+ require 'mods_display/fields/frequency'
19
27
  require 'mods_display/fields/form'
20
28
  require 'mods_display/fields/genre'
21
29
  require 'mods_display/fields/geo'
22
30
  require 'mods_display/fields/identifier'
23
31
  require 'mods_display/fields/imprint'
32
+ require 'mods_display/fields/issuance'
24
33
  require 'mods_display/fields/language'
25
34
  require 'mods_display/fields/location'
26
35
  require 'mods_display/fields/name'
27
36
  require 'mods_display/fields/nested_related_item'
28
37
  require 'mods_display/fields/note'
38
+ require 'mods_display/fields/place'
39
+ require 'mods_display/fields/publisher'
29
40
  require 'mods_display/fields/related_item'
41
+ require 'mods_display/fields/reference_title'
30
42
  require 'mods_display/fields/resource_type'
31
43
  require 'mods_display/fields/subject'
32
44
  require 'mods_display/fields/title'
data/mods_display.gemspec CHANGED
@@ -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.3', '>=3.3.7' # require stanford-mods 3.3.7 for orcid support
24
+ gem.add_dependency 'stanford-mods', '~> 3.3', '>=3.3.9' # require stanford-mods 3.3.9 for edition field
25
25
  gem.add_dependency 'i18n'
26
26
  gem.add_dependency 'view_component'
27
27
  gem.add_dependency 'rails_autolink'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mods_display
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jessie Keck
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-10 00:00:00.000000000 Z
11
+ date: 2023-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: stanford-mods
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '3.3'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 3.3.7
22
+ version: 3.3.9
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '3.3'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 3.3.7
32
+ version: 3.3.9
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: i18n
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +177,7 @@ executables: []
177
177
  extensions: []
178
178
  extra_rdoc_files: []
179
179
  files:
180
+ - ".rspec"
180
181
  - ".rubocop.yml"
181
182
  - ".rubocop_todo.yml"
182
183
  - Gemfile
@@ -189,6 +190,8 @@ files:
189
190
  - app/components/mods_display/list_field_component.rb
190
191
  - app/components/mods_display/record_component.html.erb
191
192
  - app/components/mods_display/record_component.rb
193
+ - app/components/mods_display/row_field_component.html.erb
194
+ - app/components/mods_display/row_field_component.rb
192
195
  - app/helpers/mods_display/record_helper.rb
193
196
  - app/models/mods_display/record.rb
194
197
  - config.ru
@@ -203,19 +206,31 @@ files:
203
206
  - lib/mods_display/fields/collection.rb
204
207
  - lib/mods_display/fields/contact.rb
205
208
  - lib/mods_display/fields/contents.rb
209
+ - lib/mods_display/fields/copyright_date.rb
210
+ - lib/mods_display/fields/date_captured.rb
211
+ - lib/mods_display/fields/date_created.rb
212
+ - lib/mods_display/fields/date_issued.rb
213
+ - lib/mods_display/fields/date_modified.rb
214
+ - lib/mods_display/fields/date_valid.rb
206
215
  - lib/mods_display/fields/description.rb
216
+ - lib/mods_display/fields/edition.rb
207
217
  - lib/mods_display/fields/extent.rb
208
218
  - lib/mods_display/fields/field.rb
209
219
  - lib/mods_display/fields/form.rb
220
+ - lib/mods_display/fields/frequency.rb
210
221
  - lib/mods_display/fields/genre.rb
211
222
  - lib/mods_display/fields/geo.rb
212
223
  - lib/mods_display/fields/identifier.rb
213
224
  - lib/mods_display/fields/imprint.rb
225
+ - lib/mods_display/fields/issuance.rb
214
226
  - lib/mods_display/fields/language.rb
215
227
  - lib/mods_display/fields/location.rb
216
228
  - lib/mods_display/fields/name.rb
217
229
  - lib/mods_display/fields/nested_related_item.rb
218
230
  - lib/mods_display/fields/note.rb
231
+ - lib/mods_display/fields/place.rb
232
+ - lib/mods_display/fields/publisher.rb
233
+ - lib/mods_display/fields/reference_title.rb
219
234
  - lib/mods_display/fields/related_item.rb
220
235
  - lib/mods_display/fields/resource_type.rb
221
236
  - lib/mods_display/fields/sub_title.rb