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 +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
|