cocina_display 0.7.0 → 1.0.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.
@@ -1,234 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support"
4
- require "active_support/core_ext/object/blank"
5
- require "active_support/core_ext/array/conversions"
6
-
7
- require_relative "utils"
8
- require_relative "vocabularies/marc_relator_codes"
9
-
10
- module CocinaDisplay
11
- # A contributor to a work, such as an author or publisher.
12
- class Contributor
13
- attr_reader :cocina
14
-
15
- # Initialize a Contributor object with Cocina structured data.
16
- # @param cocina [Hash] The Cocina structured data for the contributor.
17
- def initialize(cocina)
18
- @cocina = cocina
19
- end
20
-
21
- # String representation of the contributor, including name and role.
22
- # Used for debugging and logging.
23
- # @return [String]
24
- def to_s
25
- Utils.compact_and_join([display_name, display_role], delimiter: ": ")
26
- end
27
-
28
- # Is this contributor a human?
29
- # @return [Boolean]
30
- def person?
31
- cocina["type"] == "person"
32
- end
33
-
34
- # Is this contributor an organization?
35
- # @return [Boolean]
36
- def organization?
37
- cocina["type"] == "organization"
38
- end
39
-
40
- # Is this contributor a conference?
41
- # @return [Boolean]
42
- def conference?
43
- cocina["type"] == "conference"
44
- end
45
-
46
- # Is this contributor marked as primary?
47
- # @return [Boolean]
48
- def primary?
49
- cocina["status"] == "primary"
50
- end
51
-
52
- # Does this contributor have a role that indicates they are an author?
53
- # @return [Boolean]
54
- def author?
55
- roles.any?(&:author?)
56
- end
57
-
58
- # Does this contributor have a role that indicates they are a publisher?
59
- # @return [Boolean]
60
- def publisher?
61
- roles.any?(&:publisher?)
62
- end
63
-
64
- # Does this contributor have any roles defined?
65
- # @return [Boolean]
66
- def role?
67
- roles.any?
68
- end
69
-
70
- # The display name for the contributor as a string.
71
- # Uses the first name if multiple names are present.
72
- # @param with_date [Boolean] Include life dates, if present
73
- # @return [String]
74
- def display_name(with_date: false)
75
- names.map { |name| name.display_str(with_date: with_date) }.first
76
- end
77
-
78
- # A string representation of the contributor's roles, formatted for display.
79
- # If there are multiple roles, they are joined with commas.
80
- # @return [String]
81
- def display_role
82
- roles.map(&:display_str).to_sentence
83
- end
84
-
85
- # All names in the Cocina as Name objects.
86
- # @return [Array<Name>]
87
- def names
88
- @names ||= Array(cocina["name"]).map { |name| Name.new(name) }
89
- end
90
-
91
- # All roles in the Cocina structured data.
92
- # @return [Array<Hash>]
93
- def roles
94
- @roles ||= Array(cocina["role"]).map { |role| Role.new(role) }
95
- end
96
-
97
- # A name associated with a contributor.
98
- class Name
99
- attr_reader :cocina
100
-
101
- # Initialize a Name object with Cocina structured data.
102
- # @param cocina [Hash] The Cocina structured data for the name.
103
- def initialize(cocina)
104
- @cocina = cocina
105
- end
106
-
107
- # The display string for the name, optionally including life dates.
108
- # Uses these values in order, if present:
109
- # 1. Unstructured value
110
- # 2. Any structured/parallel values marked as "display"
111
- # 3. Joined structured values, optionally with life dates
112
- # @param with_date [Boolean] Include life dates, if present
113
- # @return [String]
114
- # @example no dates
115
- # name.display_name # => "King, Martin Luther, Jr."
116
- # @example with dates
117
- # name.display_name(with_date: true) # => "King, Martin Luther, Jr., 1929-1968"
118
- def display_str(with_date: false)
119
- if cocina["value"].present?
120
- cocina["value"]
121
- elsif display_name_str.present?
122
- display_name_str
123
- elsif dates_str.present? && with_date
124
- Utils.compact_and_join([full_name_str, dates_str], delimiter: ", ")
125
- else
126
- full_name_str
127
- end
128
- end
129
-
130
- private
131
-
132
- # The full name as a string, combining all name components and terms of address.
133
- # @return [String]
134
- def full_name_str
135
- Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
136
- end
137
-
138
- # Flattened form of any names explicitly marked as "display name".
139
- # @return [String]
140
- def display_name_str
141
- Utils.compact_and_join(Array(name_values["display"]), delimiter: ", ")
142
- end
143
-
144
- # List of all name components.
145
- # If any of forename, surname, or term of address are present, those are used.
146
- # Otherwise, fall back to any names explicitly marked as "name" or untyped.
147
- # @return [Array<String>]
148
- def name_components
149
- [surname_str, forename_ordinal_str].compact_blank.presence || Array(name_values["name"])
150
- end
151
-
152
- # Flatten all forenames and ordinals into a single string.
153
- # @return [String]
154
- def forename_ordinal_str
155
- Utils.compact_and_join(Array(name_values["forename"]) + Array(name_values["ordinal"]), delimiter: " ")
156
- end
157
-
158
- # Flatten all terms of address into a single string.
159
- # @return [String]
160
- def terms_of_address_str
161
- Utils.compact_and_join(Array(name_values["term of address"]), delimiter: ", ")
162
- end
163
-
164
- # Flatten all surnames into a single string.
165
- # @return [String]
166
- def surname_str
167
- Utils.compact_and_join(Array(name_values["surname"]), delimiter: " ")
168
- end
169
-
170
- # Flatten all life and activity dates into a single string.
171
- # @return [String]
172
- def dates_str
173
- Utils.compact_and_join(Array(name_values["life dates"]) + Array(name_values["activity dates"]), delimiter: ", ")
174
- end
175
-
176
- # A hash mapping destructured name types to their values.
177
- # Name values with no type are grouped under "name".
178
- # @return [Hash<String, Array<String>>]
179
- # @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#contributor-name-part-types-for-structured-value
180
- # @note Currently we do nothing with "alternative", "inverted full name", "pseudonym", and "transliteration" types.
181
- def name_values
182
- Utils.flatten_nested_values(cocina).each_with_object({}) do |node, hash|
183
- type = node["type"] || "name"
184
- hash[type] ||= []
185
- hash[type] << node["value"]
186
- end.compact_blank
187
- end
188
- end
189
-
190
- # A role associated with a contributor.
191
- class Role
192
- attr_reader :cocina
193
-
194
- # Initialize a Role object with Cocina structured data.
195
- # @param cocina [Hash] The Cocina structured data for the role.
196
- def initialize(cocina)
197
- @cocina = cocina
198
- end
199
-
200
- # The name of the role.
201
- # Translates the MARC relator code if no value was present.
202
- # @return [String, nil]
203
- def display_str
204
- cocina["value"] || (Vocabularies::MARC_RELATOR[code] if marc_relator?)
205
- end
206
-
207
- # A code associated with the role, e.g. a MARC relator code.
208
- # @return [String, nil]
209
- def code
210
- cocina["code"]
211
- end
212
-
213
- # Does this role indicate the contributor is an author?
214
- # @return [Boolean]
215
- def author?
216
- display_str =~ /^(author|creator)/i
217
- end
218
-
219
- # Does this role indicate the contributor is a publisher?
220
- # @return [Boolean]
221
- def publisher?
222
- display_str =~ /^publisher/i
223
- end
224
-
225
- private
226
-
227
- # Does this role have a MARC relator code?
228
- # @return [Boolean]
229
- def marc_relator?
230
- cocina.dig("source", "code") == "marcrelator"
231
- end
232
- end
233
- end
234
- end