cocina_display 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.standard.yml +1 -1
- data/README.md +21 -2
- data/config/i18n-tasks.yml +0 -0
- data/config/licenses.yml +59 -0
- data/config/locales/en.yml +109 -0
- data/config/marc_countries.yml +385 -0
- data/config/marc_relators.yml +310 -0
- data/config/searchworks_languages.yml +520 -0
- data/lib/cocina_display/cocina_record.rb +29 -64
- data/lib/cocina_display/concerns/accesses.rb +78 -0
- data/lib/cocina_display/concerns/contributors.rb +32 -11
- data/lib/cocina_display/concerns/events.rb +19 -6
- data/lib/cocina_display/concerns/forms.rb +98 -11
- data/lib/cocina_display/concerns/geospatial.rb +9 -5
- data/lib/cocina_display/concerns/identifiers.rb +25 -5
- data/lib/cocina_display/concerns/languages.rb +6 -2
- data/lib/cocina_display/concerns/notes.rb +36 -0
- data/lib/cocina_display/concerns/related_resources.rb +20 -0
- data/lib/cocina_display/concerns/subjects.rb +25 -8
- data/lib/cocina_display/concerns/titles.rb +67 -25
- data/lib/cocina_display/concerns/{access.rb → url_helpers.rb} +3 -3
- data/lib/cocina_display/concerns.rb +6 -0
- data/lib/cocina_display/contributors/contributor.rb +47 -26
- data/lib/cocina_display/contributors/name.rb +18 -14
- data/lib/cocina_display/contributors/role.rb +31 -13
- data/lib/cocina_display/dates/date.rb +55 -14
- data/lib/cocina_display/dates/date_range.rb +0 -2
- data/lib/cocina_display/description/access.rb +41 -0
- data/lib/cocina_display/description/access_contact.rb +11 -0
- data/lib/cocina_display/description/url.rb +17 -0
- data/lib/cocina_display/display_data.rb +104 -0
- data/lib/cocina_display/events/event.rb +8 -4
- data/lib/cocina_display/events/imprint.rb +0 -10
- data/lib/cocina_display/events/location.rb +9 -3
- data/lib/cocina_display/events/note.rb +33 -0
- data/lib/cocina_display/forms/form.rb +71 -0
- data/lib/cocina_display/forms/genre.rb +12 -0
- data/lib/cocina_display/forms/resource_type.rb +38 -0
- data/lib/cocina_display/geospatial.rb +1 -1
- data/lib/cocina_display/identifier.rb +101 -0
- data/lib/cocina_display/json_backed_record.rb +27 -0
- data/lib/cocina_display/language.rb +18 -12
- data/lib/cocina_display/license.rb +32 -0
- data/lib/cocina_display/note.rb +103 -0
- data/lib/cocina_display/related_resource.rb +74 -0
- data/lib/cocina_display/subjects/subject.rb +32 -9
- data/lib/cocina_display/subjects/subject_value.rb +34 -16
- data/lib/cocina_display/title.rb +221 -0
- data/lib/cocina_display/utils.rb +4 -4
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display.rb +32 -2
- metadata +46 -12
- data/lib/cocina_display/title_builder.rb +0 -397
- data/lib/cocina_display/vocabularies/marc_country_codes.rb +0 -393
- data/lib/cocina_display/vocabularies/marc_relator_codes.rb +0 -318
- data/lib/cocina_display/vocabularies/searchworks_languages.rb +0 -526
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CocinaDisplay
|
|
4
|
+
# A resource related to the record. See https://github.com/sul-dlss/cocina-models/blob/main/lib/cocina/models/related_resource.rb
|
|
5
|
+
# @note Related resources have no structural metadata.
|
|
6
|
+
class RelatedResource < JsonBackedRecord
|
|
7
|
+
include CocinaDisplay::Concerns::Accesses
|
|
8
|
+
include CocinaDisplay::Concerns::Events
|
|
9
|
+
include CocinaDisplay::Concerns::Contributors
|
|
10
|
+
include CocinaDisplay::Concerns::Identifiers
|
|
11
|
+
include CocinaDisplay::Concerns::Notes
|
|
12
|
+
include CocinaDisplay::Concerns::Titles
|
|
13
|
+
include CocinaDisplay::Concerns::UrlHelpers
|
|
14
|
+
include CocinaDisplay::Concerns::Subjects
|
|
15
|
+
include CocinaDisplay::Concerns::Forms
|
|
16
|
+
include CocinaDisplay::Concerns::Languages
|
|
17
|
+
include CocinaDisplay::Concerns::Geospatial
|
|
18
|
+
|
|
19
|
+
# Description of the relation to the source record.
|
|
20
|
+
# @return [String]
|
|
21
|
+
# @example "is part of"
|
|
22
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#relatedresource-types
|
|
23
|
+
attr_reader :type
|
|
24
|
+
|
|
25
|
+
# Restructure the hash so that everything is under "description" key, since
|
|
26
|
+
# it's all descriptive metadata. This makes most CocinaRecord methods work.
|
|
27
|
+
def initialize(cocina_doc)
|
|
28
|
+
@type = cocina_doc["type"]
|
|
29
|
+
super({"description" => cocina_doc.except("type")})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Label used to group the related resource for display.
|
|
33
|
+
# @return [String]
|
|
34
|
+
def label
|
|
35
|
+
cocina_doc.dig("description", "displayLabel").presence || type_label
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# String representation of the related resource.
|
|
39
|
+
# @return [String, nil]
|
|
40
|
+
def to_s
|
|
41
|
+
main_title || url
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# URL to the related resource for link construction.
|
|
45
|
+
# If there are multiple URLs, uses the first.
|
|
46
|
+
# @return [String, nil]
|
|
47
|
+
def url
|
|
48
|
+
urls.first&.to_s || purl_url
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Is this a related resource with a URL?
|
|
52
|
+
# @return [Boolean]
|
|
53
|
+
def url?
|
|
54
|
+
url.present?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Nested display data for the related resource.
|
|
58
|
+
# Combines titles, contributors, notes, and access information.
|
|
59
|
+
# @note Used for extended display of citations, e.g. on hp566jq8781.
|
|
60
|
+
# @return [Array<DisplayData>]
|
|
61
|
+
def display_data
|
|
62
|
+
title_display_data + contributor_display_data + general_note_display_data + preferred_citation_display_data + access_display_data
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
# Key used for i18n lookup of the label, based on the type.
|
|
68
|
+
# Falls back to a generic label for any unknown types.
|
|
69
|
+
# @return [String]
|
|
70
|
+
def type_label
|
|
71
|
+
I18n.t(type&.parameterize&.underscore, default: :related_to, scope: "cocina_display.field_label.related_resource")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
require_relative "../utils"
|
|
2
|
-
require_relative "subject_value"
|
|
3
|
-
|
|
4
1
|
module CocinaDisplay
|
|
5
2
|
module Subjects
|
|
6
3
|
# Base class for subjects in Cocina structured data.
|
|
@@ -27,23 +24,49 @@ module CocinaDisplay
|
|
|
27
24
|
subject_values.map(&:to_s).compact_blank
|
|
28
25
|
end
|
|
29
26
|
|
|
30
|
-
#
|
|
27
|
+
# The value to use for display.
|
|
28
|
+
# Genre values are capitalized; other subject values are not.
|
|
31
29
|
# @return [String]
|
|
32
30
|
def to_s
|
|
31
|
+
(type == "genre") ? display_value&.upcase_first : display_value
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# A string representation of the entire subject, concatenated for display.
|
|
35
|
+
# @return [String]
|
|
36
|
+
def display_value
|
|
33
37
|
Utils.compact_and_join(display_values, delimiter: " > ")
|
|
34
38
|
end
|
|
35
39
|
|
|
40
|
+
# Label used to render the subject for display.
|
|
41
|
+
# Uses a displayLabel if available, otherwise looks up via type.
|
|
42
|
+
# @return [String]
|
|
43
|
+
def label
|
|
44
|
+
cocina["displayLabel"].presence || type_label
|
|
45
|
+
end
|
|
46
|
+
|
|
36
47
|
# Individual values composing this subject.
|
|
37
48
|
# Can be multiple if the Cocina featured nested data.
|
|
38
|
-
#
|
|
49
|
+
# All SubjectValues inherit the type of their parent Subject.
|
|
39
50
|
# @return [Array<SubjectValue>]
|
|
40
51
|
def subject_values
|
|
41
|
-
@subject_values ||=
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
@subject_values ||= (Array(cocina["parallelValue"]).presence || [cocina]).flat_map do |node|
|
|
53
|
+
if SubjectValue.atomic_types.include?(type)
|
|
54
|
+
SubjectValue.from_cocina(node, type: type)
|
|
55
|
+
else
|
|
56
|
+
Utils.flatten_nested_values(node, atomic_types: SubjectValue.atomic_types).flat_map do |value|
|
|
57
|
+
SubjectValue.from_cocina(value, type: value["type"] || type)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
45
60
|
end
|
|
46
61
|
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
# Type-specific label for this subject.
|
|
66
|
+
# @return [String]
|
|
67
|
+
def type_label
|
|
68
|
+
I18n.t(type&.parameterize&.underscore, default: :subject, scope: "cocina_display.field_label.subject")
|
|
69
|
+
end
|
|
47
70
|
end
|
|
48
71
|
end
|
|
49
72
|
end
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
require "geo/coord"
|
|
2
|
-
|
|
3
|
-
require_relative "subject"
|
|
4
|
-
require_relative "../contributors/name"
|
|
5
|
-
require_relative "../title_builder"
|
|
6
|
-
require_relative "../dates/date"
|
|
7
|
-
require_relative "../geospatial"
|
|
8
|
-
|
|
9
1
|
module CocinaDisplay
|
|
10
2
|
module Subjects
|
|
11
3
|
# A descriptive value that can be part of a Subject.
|
|
@@ -16,11 +8,29 @@ module CocinaDisplay
|
|
|
16
8
|
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#subject-part-types-for-structured-value
|
|
17
9
|
attr_accessor :type
|
|
18
10
|
|
|
19
|
-
# Create
|
|
11
|
+
# Create SubjectValues from Cocina structured data.
|
|
12
|
+
# Pre-coordinated string values will be split into multiple SubjectValues.
|
|
20
13
|
# @param cocina [Hash] The Cocina structured data for the subject.
|
|
21
|
-
# @
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
# @param type [String, nil] The type, coming from the parent Subject.
|
|
15
|
+
# @return [Array<SubjectValue>]
|
|
16
|
+
def self.from_cocina(cocina, type:)
|
|
17
|
+
split_pre_coordinated_values(cocina, type: type).map do |value|
|
|
18
|
+
SUBJECT_VALUE_TYPES.fetch(type, SubjectValue).new(value).tap do |obj|
|
|
19
|
+
obj.type ||= type
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Split a pre-coordinated subject value joined with "--" into multiple values.
|
|
25
|
+
# Ignores the "--" string for coordinate subject types, which use it differently.
|
|
26
|
+
# @param cocina [Hash] The Cocina structured data for the subject.
|
|
27
|
+
# @return [Array<Hash>] An array of Cocina hashes, one for each split value
|
|
28
|
+
def self.split_pre_coordinated_values(cocina, type:)
|
|
29
|
+
if cocina["value"].is_a?(String) && cocina["value"].include?("--") && !type&.include?("coordinates")
|
|
30
|
+
cocina["value"].split("--").map { |v| cocina.merge("value" => v.strip) }
|
|
31
|
+
else
|
|
32
|
+
[cocina]
|
|
33
|
+
end
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
# All subject value types that should not be further destructured.
|
|
@@ -65,12 +75,20 @@ module CocinaDisplay
|
|
|
65
75
|
|
|
66
76
|
# A subject value representing an entity with a title.
|
|
67
77
|
class TitleSubjectValue < SubjectValue
|
|
78
|
+
attr_reader :title
|
|
79
|
+
|
|
80
|
+
# Initialize a TitleSubjectValue object with Cocina structured data.
|
|
81
|
+
# @param cocina [Hash] The Cocina structured data for the subject.
|
|
82
|
+
def initialize(cocina)
|
|
83
|
+
super
|
|
84
|
+
@title = Title.new(cocina)
|
|
85
|
+
end
|
|
86
|
+
|
|
68
87
|
# Construct a title string to use for display.
|
|
69
|
-
# @see CocinaDisplay::
|
|
70
|
-
# @
|
|
71
|
-
# @return [String]
|
|
88
|
+
# @see CocinaDisplay::Title#to_s
|
|
89
|
+
# @return [String, nil]
|
|
72
90
|
def to_s
|
|
73
|
-
|
|
91
|
+
title.to_s
|
|
74
92
|
end
|
|
75
93
|
end
|
|
76
94
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
module CocinaDisplay
|
|
2
|
+
# A group of related {TitleValue}s associated with an item.
|
|
3
|
+
class Title
|
|
4
|
+
# The underlying Cocina hash.
|
|
5
|
+
attr_reader :cocina
|
|
6
|
+
|
|
7
|
+
# Type of the title, e.g. "uniform", "alternative", etc.
|
|
8
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#title-types
|
|
9
|
+
# @return [String, nil]
|
|
10
|
+
attr_accessor :type
|
|
11
|
+
|
|
12
|
+
# Status of the title, e.g. "primary".
|
|
13
|
+
# @return [String, nil]
|
|
14
|
+
attr_accessor :status
|
|
15
|
+
|
|
16
|
+
# Create a new Title object.
|
|
17
|
+
# @param cocina [Hash]
|
|
18
|
+
# @param part_label [String, nil] part label for digital serials
|
|
19
|
+
# @param part_numbers [Array<String>] part numbers for related resources
|
|
20
|
+
def initialize(cocina, part_label: nil, part_numbers: nil)
|
|
21
|
+
@cocina = cocina
|
|
22
|
+
@part_label = part_label
|
|
23
|
+
@part_numbers = part_numbers
|
|
24
|
+
@type = cocina["type"].presence
|
|
25
|
+
@status = cocina["status"].presence
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Label used when displaying the title.
|
|
29
|
+
# @return [String]
|
|
30
|
+
def label
|
|
31
|
+
cocina["displayLabel"].presence || type_label
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Does this title have a type?
|
|
35
|
+
# @return [Boolean]
|
|
36
|
+
def type?
|
|
37
|
+
type.present?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Is this marked as a primary title?
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
def primary?
|
|
43
|
+
status == "primary"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The string representation of the title, for display.
|
|
47
|
+
# @see #display_title
|
|
48
|
+
# @return [String, nil]
|
|
49
|
+
def to_s
|
|
50
|
+
display_title
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The short form of the title, without subtitle, part name, etc.
|
|
54
|
+
# @note This corresponds to the "short title" in MODS XML, or MARC 245$a only.
|
|
55
|
+
# @return [String, nil]
|
|
56
|
+
# @example "M. de Courville"
|
|
57
|
+
def short_title
|
|
58
|
+
short_title_str.presence || cocina["value"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The long form of the title, including subtitle, part name, etc.
|
|
62
|
+
# @note This corresponds to the entire MARC 245 field.
|
|
63
|
+
# @return [String, nil]
|
|
64
|
+
# @example "M. de Courville [estampe]"
|
|
65
|
+
def full_title
|
|
66
|
+
full_title_str.presence || cocina["value"]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The long form of the title, with added punctuation between parts if not present.
|
|
70
|
+
# @note This corresponds to the entire MARC 245 field.
|
|
71
|
+
# @return [String, nil]
|
|
72
|
+
# @example "M. de Courville : [estampe]"
|
|
73
|
+
def display_title
|
|
74
|
+
display_title_str.presence || cocina["value"]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# A string value for sorting by title.
|
|
78
|
+
# Ignores punctuation, leading/trailing spaces, and non-sorting characters.
|
|
79
|
+
# If no title is present, returns a high Unicode value so it sorts last.
|
|
80
|
+
# @return [String]
|
|
81
|
+
def sort_title
|
|
82
|
+
return "\u{10FFFF}" unless full_title
|
|
83
|
+
|
|
84
|
+
sort_title_str
|
|
85
|
+
.unicode_normalize(:nfd) # Prevent accents being stripped
|
|
86
|
+
.gsub(/[[:punct:]]*/, "")
|
|
87
|
+
.gsub(/\W{2,}/, " ") # Collapse whitespace after removing punctuation
|
|
88
|
+
.strip
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
# Generate the short title by joining main title and nonsorting characters with spaces.
|
|
94
|
+
# @return [String, nil]
|
|
95
|
+
def short_title_str
|
|
96
|
+
Utils.compact_and_join([nonsorting_chars_str, main_title_str])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Generate the full title by joining all title components with spaces.
|
|
100
|
+
# @return [String, nil]
|
|
101
|
+
def full_title_str
|
|
102
|
+
nonsorting_chars_str + sort_title_str
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# All of the sorting parts of the title joined together with spaces.
|
|
106
|
+
# @return [String]
|
|
107
|
+
def sort_title_str
|
|
108
|
+
Utils.compact_and_join([main_title_str, subtitle_str, parts_str])
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Generate the display title by joining all components with punctuation:
|
|
112
|
+
# - Join main title and subtitle with " : "
|
|
113
|
+
# - Join part name/number/label with ", "
|
|
114
|
+
# - Join part string with preceding title with ". "
|
|
115
|
+
# - Prepend preformatted nonsorting characters
|
|
116
|
+
# - Prepend associated names with ". "
|
|
117
|
+
# @return [String, nil]
|
|
118
|
+
def display_title_str
|
|
119
|
+
title_str = Utils.compact_and_join([main_title_str, subtitle_str], delimiter: " : ")
|
|
120
|
+
title_str = Utils.compact_and_join([title_str, parts_str(delimiter: ", ")], delimiter: ". ")
|
|
121
|
+
title_str = nonsorting_chars_str + title_str # pre-formatted padding
|
|
122
|
+
title_str = Utils.compact_and_join([names_str, title_str], delimiter: ". ") if names_str.present?
|
|
123
|
+
title_str.presence
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# All nonsorting characters joined together with padding applied.
|
|
127
|
+
# Handles languages that do not separate nonsorting characters with spaces.
|
|
128
|
+
# @return [String, nil]
|
|
129
|
+
def nonsorting_chars_str
|
|
130
|
+
pad_nonsorting(Utils.compact_and_join(Array(title_components["nonsorting characters"])))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# The main title component(s), joined together.
|
|
134
|
+
# @return [String, nil]
|
|
135
|
+
def main_title_str
|
|
136
|
+
Utils.compact_and_join(Array(title_components["main title"]))
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# The subtitle components, joined together.
|
|
140
|
+
# @return [String, nil]
|
|
141
|
+
def subtitle_str
|
|
142
|
+
Utils.compact_and_join(Array(title_components["subtitle"]))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# The part name, number, and label components, joined together.
|
|
146
|
+
# Default delimiter is a space, but can be overridden.
|
|
147
|
+
# @return [String, nil]
|
|
148
|
+
def parts_str(delimiter: " ")
|
|
149
|
+
Utils.compact_and_join(
|
|
150
|
+
Array(title_components["part number"] || @part_numbers) +
|
|
151
|
+
Array(title_components["part name"]) +
|
|
152
|
+
[@part_label],
|
|
153
|
+
delimiter: delimiter
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# The associated names, joined together with periods.
|
|
158
|
+
# @note Only present for uniform titles.
|
|
159
|
+
# @return [String, nil]
|
|
160
|
+
def names_str
|
|
161
|
+
Utils.compact_and_join(names, delimiter: ". ")
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Destructured title components, organized by type.
|
|
165
|
+
# Unstructured titles and components with no type are grouped under "main title".
|
|
166
|
+
# @return [Hash<String, Array<String>>]
|
|
167
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#title-part-types-for-structured-value
|
|
168
|
+
def title_components
|
|
169
|
+
Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
|
|
170
|
+
type = case node["type"]
|
|
171
|
+
when "uniform", "alternative", "abbreviated", "translated", "transliterated", "parallel", "supplied", nil
|
|
172
|
+
"main title"
|
|
173
|
+
else
|
|
174
|
+
node["type"]
|
|
175
|
+
end
|
|
176
|
+
hash[type] ||= []
|
|
177
|
+
hash[type] << node["value"]
|
|
178
|
+
end.compact_blank
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Uniform titles can have associated person names.
|
|
182
|
+
# @return [String, nil]
|
|
183
|
+
def names
|
|
184
|
+
Janeway.enum_for("$.note[?(@.type=='associated name')]", cocina).map do |name|
|
|
185
|
+
Contributors::Name.new(name).to_s(with_date: true)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Number of nonsorting characters to ignore at the start of the title.
|
|
190
|
+
# @return [Integer, nil]
|
|
191
|
+
def nonsorting_char_count
|
|
192
|
+
Janeway.enum_for("$.note[?(@.type=='nonsorting character count')].value", cocina).first&.to_i
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Type-specific label for the title, falling back to a generic "Title".
|
|
196
|
+
# @return [String]
|
|
197
|
+
def type_label
|
|
198
|
+
I18n.t(type&.parameterize&.underscore, scope: "cocina_display.field_label.title", default: :title)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
private
|
|
202
|
+
|
|
203
|
+
# Add or remove padding from nonsorting portion of the title.
|
|
204
|
+
# @param value [String]
|
|
205
|
+
# @return [String]
|
|
206
|
+
def pad_nonsorting(value)
|
|
207
|
+
case value.strip
|
|
208
|
+
when /.*-$/, /.*'$/, "ה" # Arabic, French, Hebrew prefixes use no padding
|
|
209
|
+
value.strip
|
|
210
|
+
when "" # No nonsorting characters; return empty string
|
|
211
|
+
""
|
|
212
|
+
else # Pad to nonsorting char count if set, otherwise add a single space
|
|
213
|
+
if nonsorting_char_count.present?
|
|
214
|
+
value.ljust(nonsorting_char_count, " ")
|
|
215
|
+
else
|
|
216
|
+
value + " "
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
data/lib/cocina_display/utils.rb
CHANGED
|
@@ -21,7 +21,7 @@ module CocinaDisplay
|
|
|
21
21
|
end.delete_suffix(delimiter)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# Recursively flatten structured,
|
|
24
|
+
# Recursively flatten structured, and grouped values in Cocina metadata.
|
|
25
25
|
# Returns a list of hashes representing the "leaf" nodes with +value+ key.
|
|
26
26
|
# @return [Array<Hash>] List of node hashes with "value" present
|
|
27
27
|
# @param cocina [Hash] The Cocina structured data to flatten
|
|
@@ -35,8 +35,8 @@ module CocinaDisplay
|
|
|
35
35
|
# cocina = { "structuredValue" => [{"value" => "foo"}, {"value" => "bar"}] }
|
|
36
36
|
# Utils.flatten_nested_values(cocina)
|
|
37
37
|
# #=> [{"value" => "foo"}, {"value" => "bar"}]
|
|
38
|
-
# @example
|
|
39
|
-
# cocina = { "
|
|
38
|
+
# @example nested structured and simple values
|
|
39
|
+
# cocina = { "structuredValue" => [{"value" => "foo" }, { "structuredValue" => [{"value" => "bar"}, {"value" => "baz"}] }] }
|
|
40
40
|
# Utils.flatten_nested_values(cocina)
|
|
41
41
|
# #=> [{"value" => "foo"}, {"value" => "foo"}, {"value" => "baz"}]
|
|
42
42
|
def self.flatten_nested_values(cocina, output = [], atomic_types: [])
|
|
@@ -44,7 +44,7 @@ module CocinaDisplay
|
|
|
44
44
|
return [cocina] if atomic_types.include?(cocina["type"])
|
|
45
45
|
return cocina.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) } if cocina.is_a?(Array)
|
|
46
46
|
|
|
47
|
-
nested_values = Array(cocina["structuredValue"]) + Array(cocina["
|
|
47
|
+
nested_values = Array(cocina["structuredValue"]) + Array(cocina["groupedValue"])
|
|
48
48
|
return output unless nested_values.any?
|
|
49
49
|
|
|
50
50
|
nested_values.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) }
|
data/lib/cocina_display.rb
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "cocina_display/version"
|
|
4
|
-
|
|
3
|
+
# require_relative "cocina_display/version"
|
|
4
|
+
|
|
5
|
+
require "janeway"
|
|
6
|
+
require "json"
|
|
7
|
+
require "net/http"
|
|
8
|
+
require "active_support"
|
|
9
|
+
require "active_support/core_ext/object/blank"
|
|
10
|
+
require "active_support/core_ext/hash/conversions"
|
|
11
|
+
require "geo/coord"
|
|
12
|
+
require "edtf"
|
|
13
|
+
require "i18n"
|
|
14
|
+
require "i18n/backend/fallbacks"
|
|
15
|
+
I18n::Backend::Simple.include I18n::Backend::Fallbacks
|
|
16
|
+
I18n.load_path += Dir["#{File.expand_path("..", __dir__)}/config/locales/*.yml"]
|
|
17
|
+
I18n.backend.load_translations
|
|
18
|
+
|
|
19
|
+
require "zeitwerk"
|
|
20
|
+
loader = Zeitwerk::Loader.new
|
|
21
|
+
loader.tag = File.basename(__FILE__, ".rb")
|
|
22
|
+
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
|
23
|
+
loader.push_dir(File.dirname(__FILE__))
|
|
24
|
+
loader.setup
|
|
25
|
+
|
|
26
|
+
module CocinaDisplay
|
|
27
|
+
# set to an object with a #notify method. This is called if an error is encountered.
|
|
28
|
+
mattr_accessor :notifier
|
|
29
|
+
|
|
30
|
+
# @return [Pathname] Returns the root path of this gem
|
|
31
|
+
def self.root
|
|
32
|
+
@root ||= Pathname.new(File.expand_path("..", __dir__))
|
|
33
|
+
end
|
|
34
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cocina_display
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1
|
|
4
|
+
version: 1.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nick Budak
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: janeway-jsonpath
|
|
@@ -53,19 +53,19 @@ dependencies:
|
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.2'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
56
|
+
name: i18n
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
59
|
+
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '0'
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
|
-
- - "
|
|
66
|
+
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '0'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: geo_coord
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '0.2'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: zeitwerk
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '2.7'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '2.7'
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: rake
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -210,36 +224,56 @@ files:
|
|
|
210
224
|
- LICENSE
|
|
211
225
|
- README.md
|
|
212
226
|
- Rakefile
|
|
227
|
+
- config/i18n-tasks.yml
|
|
228
|
+
- config/licenses.yml
|
|
229
|
+
- config/locales/en.yml
|
|
230
|
+
- config/marc_countries.yml
|
|
231
|
+
- config/marc_relators.yml
|
|
232
|
+
- config/searchworks_languages.yml
|
|
213
233
|
- lib/cocina_display.rb
|
|
214
234
|
- lib/cocina_display/cocina_record.rb
|
|
215
|
-
- lib/cocina_display/concerns
|
|
235
|
+
- lib/cocina_display/concerns.rb
|
|
236
|
+
- lib/cocina_display/concerns/accesses.rb
|
|
216
237
|
- lib/cocina_display/concerns/contributors.rb
|
|
217
238
|
- lib/cocina_display/concerns/events.rb
|
|
218
239
|
- lib/cocina_display/concerns/forms.rb
|
|
219
240
|
- lib/cocina_display/concerns/geospatial.rb
|
|
220
241
|
- lib/cocina_display/concerns/identifiers.rb
|
|
221
242
|
- lib/cocina_display/concerns/languages.rb
|
|
243
|
+
- lib/cocina_display/concerns/notes.rb
|
|
244
|
+
- lib/cocina_display/concerns/related_resources.rb
|
|
222
245
|
- lib/cocina_display/concerns/structural.rb
|
|
223
246
|
- lib/cocina_display/concerns/subjects.rb
|
|
224
247
|
- lib/cocina_display/concerns/titles.rb
|
|
248
|
+
- lib/cocina_display/concerns/url_helpers.rb
|
|
225
249
|
- lib/cocina_display/contributors/contributor.rb
|
|
226
250
|
- lib/cocina_display/contributors/name.rb
|
|
227
251
|
- lib/cocina_display/contributors/role.rb
|
|
228
252
|
- lib/cocina_display/dates/date.rb
|
|
229
253
|
- lib/cocina_display/dates/date_range.rb
|
|
254
|
+
- lib/cocina_display/description/access.rb
|
|
255
|
+
- lib/cocina_display/description/access_contact.rb
|
|
256
|
+
- lib/cocina_display/description/url.rb
|
|
257
|
+
- lib/cocina_display/display_data.rb
|
|
230
258
|
- lib/cocina_display/events/event.rb
|
|
231
259
|
- lib/cocina_display/events/imprint.rb
|
|
232
260
|
- lib/cocina_display/events/location.rb
|
|
261
|
+
- lib/cocina_display/events/note.rb
|
|
262
|
+
- lib/cocina_display/forms/form.rb
|
|
263
|
+
- lib/cocina_display/forms/genre.rb
|
|
264
|
+
- lib/cocina_display/forms/resource_type.rb
|
|
233
265
|
- lib/cocina_display/geospatial.rb
|
|
266
|
+
- lib/cocina_display/identifier.rb
|
|
267
|
+
- lib/cocina_display/json_backed_record.rb
|
|
234
268
|
- lib/cocina_display/language.rb
|
|
269
|
+
- lib/cocina_display/license.rb
|
|
270
|
+
- lib/cocina_display/note.rb
|
|
271
|
+
- lib/cocina_display/related_resource.rb
|
|
235
272
|
- lib/cocina_display/subjects/subject.rb
|
|
236
273
|
- lib/cocina_display/subjects/subject_value.rb
|
|
237
|
-
- lib/cocina_display/
|
|
274
|
+
- lib/cocina_display/title.rb
|
|
238
275
|
- lib/cocina_display/utils.rb
|
|
239
276
|
- lib/cocina_display/version.rb
|
|
240
|
-
- lib/cocina_display/vocabularies/marc_country_codes.rb
|
|
241
|
-
- lib/cocina_display/vocabularies/marc_relator_codes.rb
|
|
242
|
-
- lib/cocina_display/vocabularies/searchworks_languages.rb
|
|
243
277
|
- script/deep_compact.rb
|
|
244
278
|
- script/find_records.rb
|
|
245
279
|
- sig/cocina_display.rbs
|