mods_display 1.4.0 → 1.5.0
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/.rspec +2 -0
- data/README.md +10 -18
- data/app/components/mods_display/row_field_component.html.erb +11 -0
- data/app/components/mods_display/row_field_component.rb +43 -0
- data/app/helpers/mods_display/record_helper.rb +20 -0
- data/config/locales/en.yml +2 -0
- data/lib/mods_display/fields/cartographics.rb +1 -1
- data/lib/mods_display/fields/copyright_date.rb +9 -0
- data/lib/mods_display/fields/date_captured.rb +9 -0
- data/lib/mods_display/fields/date_created.rb +9 -0
- data/lib/mods_display/fields/date_issued.rb +9 -0
- data/lib/mods_display/fields/date_modified.rb +9 -0
- data/lib/mods_display/fields/date_valid.rb +9 -0
- data/lib/mods_display/fields/edition.rb +22 -0
- data/lib/mods_display/fields/field.rb +41 -0
- data/lib/mods_display/fields/frequency.rb +19 -0
- data/lib/mods_display/fields/imprint.rb +4 -27
- data/lib/mods_display/fields/issuance.rb +19 -0
- data/lib/mods_display/fields/nested_related_item.rb +38 -31
- data/lib/mods_display/fields/note.rb +3 -3
- data/lib/mods_display/fields/place.rb +35 -0
- data/lib/mods_display/fields/publisher.rb +21 -0
- data/lib/mods_display/fields/reference_title.rb +30 -0
- data/lib/mods_display/fields/related_item.rb +50 -29
- data/lib/mods_display/fields/title.rb +34 -18
- data/lib/mods_display/html.rb +24 -10
- data/lib/mods_display/version.rb +1 -1
- data/lib/mods_display.rb +12 -0
- data/mods_display.gemspec +1 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f82b0048ccbf7b94b1c15326c364b4133adf8c241145e98809e6e258cc15164
|
4
|
+
data.tar.gz: '04349a0206e891f2ab84d23c7a182e5a5bd1b1e8227bb08deda07d23824b4511'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e8ae4be6ab26e6e30fa74f0b71cc3a03c19ca33906770c5cf5c9819ccf11be9c92281e5b6443b8cb8905b0df285c93470a963d2c11ad2a37c1390166b77ee54
|
7
|
+
data.tar.gz: 0e982271c57bdc0058dc5f005f2f0a9a84962e4ce01f1e0740bb3143e9332c2b8f4a398adc190520dd5509bdd8b85eb58bdeeaf8544910405759a703ff121198
|
data/.rspec
ADDED
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,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 = []
|
data/config/locales/en.yml
CHANGED
@@ -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:
|
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,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:
|
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
|
-
|
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
|
-
|
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
|
-
|
24
|
-
|
33
|
+
class ValueRenderer
|
34
|
+
def initialize(value)
|
35
|
+
@value = value
|
36
|
+
end
|
25
37
|
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
34
|
-
end
|
42
|
+
protected
|
35
43
|
|
36
|
-
|
44
|
+
attr_reader :value
|
37
45
|
|
38
|
-
|
39
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
57
|
-
|
58
|
+
r.from_nk_node(munged_node)
|
59
|
+
end
|
60
|
+
end
|
58
61
|
|
59
|
-
|
62
|
+
def body_presence(body)
|
63
|
+
return if body == '<dl></dl>'
|
60
64
|
|
61
|
-
|
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
|
-
|
22
|
-
|
23
|
-
|
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 =
|
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
|
-
|
26
|
+
class ValueRenderer
|
27
|
+
def initialize(value)
|
28
|
+
@value = value
|
29
|
+
end
|
22
30
|
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
45
|
-
citation = item.note_nodeset.find { |note| note['type'] == 'preferred citation' }
|
53
|
+
end
|
46
54
|
|
47
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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?
|
data/lib/mods_display/html.rb
CHANGED
@@ -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(
|
33
|
-
@
|
34
|
-
@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
|
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.
|
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
|
data/lib/mods_display/version.rb
CHANGED
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.
|
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
|
+
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
|
+
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.
|
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.
|
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
|