cocina_display 1.2.2 → 1.3.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/README.md +74 -1
- data/lib/cocina_display/contributors/affiliation.rb +70 -0
- data/lib/cocina_display/contributors/contributor.rb +40 -6
- data/lib/cocina_display/utils.rb +11 -11
- data/lib/cocina_display/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd051f7ce562501ccdbfb06831d81619742b1eeda24df081b79502473000dbc
|
4
|
+
data.tar.gz: 958bfe184a73058d469d6961284b0480544d7d26bad6e2a0c278cd968d143f1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0df0a44ee42f7ecb5163e8ce71d9f1564ff4f6b5ffbca529c41838393d81122d365e3dcae1e86f018131e48ccb40cca8552163d6655e14482191d860d838282
|
7
|
+
data.tar.gz: 78ac4f16998a9adf1bebd70f7d05a0f0bf1d6f5489f0ff4f5b76eb573c4f2c74c85bcf682a409459721de6e42b88bdaa7eeaef69cb1f046658629fd82be6fae7
|
data/README.md
CHANGED
@@ -88,6 +88,79 @@ cat spec/fixtures/bb112zx3193.json | janeway "$.description.contributor[?@.role[
|
|
88
88
|
]
|
89
89
|
```
|
90
90
|
|
91
|
+
### Formatting for display
|
92
|
+
|
93
|
+
Methods ending in `_display_data` usually return arrays of a class called `DisplayData`, which is designed for rendering into HTML by a consuming application. Each `DisplayData` object has a `label`, which serves as a heading under which its data is grouped. The `values` method returns an array of strings, which are the individual values to be displayed under that heading.
|
94
|
+
|
95
|
+
For example, when displaying contributors, the label is usually determined by the role of the contributor, and the values are the display names of the contributors with that role:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
> rec.contributor_display_data.first.label
|
99
|
+
=> "Former owner"
|
100
|
+
> rec.contributor_display_data.first.values
|
101
|
+
=> ["Hearst Magazines, Inc."]
|
102
|
+
```
|
103
|
+
|
104
|
+
The `#to_hash` helper method, which collapses all provided `DisplayData` into a single hash, can be used as a quick way to check the overall structure:
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
> CocinaDisplay::DisplayData.to_hash(record.subject_display_data)
|
108
|
+
=> {"Marque"=>["Bugatti"], "Model"=>["Bugatti T51A"], "Subject"=>["Bugatti automobile"]}
|
109
|
+
```
|
110
|
+
|
111
|
+
Note that usage of the `displayLabel` attribute in Cocina overrides the `label` that would ordinarily be used to group an item. If some items in a field have a `displayLabel` and others do not, each unique `displayLabel` will get its own `DisplayData` object, because those items will be grouped under a separate heading. If you call a method like `#subject_display_data`, it's always possible that you will get some items grouped under the default label "Subject" as well as others grouped under custom labels.
|
112
|
+
|
113
|
+
#### Creating display data
|
114
|
+
|
115
|
+
In some cases, you may wish to create `DisplayData` for some data that isn't immediately available via a `_display_data` method. Depending on what you have, there are several helper methods available to do this that will label and group the data for you.
|
116
|
+
|
117
|
+
If the data you have is an array of objects that respond to `#label` and `#to_s`, you can use `DisplayData.from_objects`, which will automatically group the objects by their `label` and set the `values` to the result of calling `#to_s` on each object. Most of the objects returned by `CocinaRecord` methods, like `Contributor` and `Subject`, respond to these methods, and also handle nested `structuredValue`s in the Cocina when rendering to string. Handling of `parallelValue`s is also included for some object types like `Name` and `Title`.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
# This is actually equivalent to record.contributor_display_data!
|
121
|
+
> CocinaDisplay::DisplayData.from_objects(record.contributors)
|
122
|
+
```
|
123
|
+
|
124
|
+
If the data you have is a simple array of strings, you can use `DisplayData.from_strings`. You need to provide a `label` to group the strings under:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
> CocinaDisplay::DisplayData.from_strings(["Bugatti", "Bugatti T51A", "Bugatti automobile"], label: "Subject")
|
128
|
+
```
|
129
|
+
|
130
|
+
If the data you have is a hash from parsed Cocina JSON, you can use `DisplayData.from_cocina`. This will respect any `displayLabel` attributes in the provided Cocina. You can optionally provide a `label` to use if the Cocina did not contain a `displayLabel` attribute:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
> cocina = { 'value' => 'Bugatti', 'displayLabel' => 'Marque' }
|
134
|
+
# Will create a DisplayData with label "Marque" and value "Bugatti"
|
135
|
+
> CocinaDisplay::DisplayData.from_cocina(cocina)
|
136
|
+
# The same, but if the Cocina did not contain a displayLabel, it would use "Brand" instead
|
137
|
+
> CocinaDisplay::DisplayData.from_cocina(cocina, label: 'Brand')
|
138
|
+
```
|
139
|
+
|
140
|
+
Note that `DisplayData.from_cocina` does not handle `structuredValue`s or `parallelValue`s in the provided Cocina. Because the correct handling is dependent on the type of data, you're usually better off selecting the appropriate objects from the `CocinaRecord` and using `DisplayData.from_objects` instead. To find out more about the various objects returned by `CocinaRecord` methods, see the [API Documentation](https://sul-dlss.github.io/cocina_display/).
|
141
|
+
|
142
|
+
#### Custom formatting
|
143
|
+
|
144
|
+
In some cases, you may need more control over the formatting. The `DisplayData#objects` method gives access to the underlying objects that were grouped under a particular `label`, allowing you to format them as needed.
|
145
|
+
|
146
|
+
For contributors, the underlying objects are `Contributor` instances, which provide access to the associated `Name` and `Role` objects, as well as some other useful methods like `#forename` and `#organization?`:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
> record.contributor_display_data.first.label
|
150
|
+
=> "Former owner"
|
151
|
+
# All of the Contributors grouped under "Former owner"
|
152
|
+
> former_owners = record.contributor_display_data.first.objects
|
153
|
+
=>
|
154
|
+
[#<CocinaDisplay::Contributors::Contributor:0x0000000123ad6550
|
155
|
+
...
|
156
|
+
> former_owners.first.names.first.to_s
|
157
|
+
=> "Hearst Magazines, Inc."
|
158
|
+
> former_owners.first.organization?
|
159
|
+
=> true
|
160
|
+
```
|
161
|
+
|
162
|
+
To review all the methods available on `Contributor`, see the [API Documentation](https://sul-dlss.github.io/cocina_display/CocinaDisplay/Contributors/Contributor.html).
|
163
|
+
|
91
164
|
### Searching for records
|
92
165
|
|
93
166
|
Sometimes you need to determine if records exist "in the wild" that exhibit particular characteristics in the Cocina metadata, like the presence or absence of a field, or a specific value in a field. There is a template script in the `scripts/` directory that can be used to crawl all DRUIDs released to a particular target, like Searchworks, and examine each record.
|
@@ -104,13 +177,13 @@ find /stacks -name cocina.json | head -10000 |
|
|
104
177
|
You may create a custom error handler by implementing the `Honeybadger` interface (or just using Honeybadger) and assigning it to the `CocinaRecord.notifier`.
|
105
178
|
|
106
179
|
For example:
|
180
|
+
|
107
181
|
```ruby
|
108
182
|
Rails.application.config.to_prepare do
|
109
183
|
CocinaDisplay.notifier = Honeybadger
|
110
184
|
end
|
111
185
|
```
|
112
186
|
|
113
|
-
|
114
187
|
## Development
|
115
188
|
|
116
189
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. This is a useful place to try out JSONPath expressions with `CocinaRecord#path`.
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
module Contributors
|
5
|
+
# An affiliation associated with a contributor.
|
6
|
+
class Affiliation
|
7
|
+
attr_reader :cocina
|
8
|
+
|
9
|
+
# Initialize an Affiliation from Cocina structured data.
|
10
|
+
# @param cocina [Hash]
|
11
|
+
def initialize(cocina)
|
12
|
+
@cocina = cocina
|
13
|
+
end
|
14
|
+
|
15
|
+
# String representation of the affiliation, using display name.
|
16
|
+
# @return [String, nil]
|
17
|
+
def to_s
|
18
|
+
display_name
|
19
|
+
end
|
20
|
+
|
21
|
+
# The name of the institution or organization.
|
22
|
+
# @return [String, nil]
|
23
|
+
# @example "Stanford University, Department of Special Collections"
|
24
|
+
def display_name
|
25
|
+
name_components.join(", ") if name_components.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Does this Affiliation have a ROR ID?
|
29
|
+
# @return [Boolean]
|
30
|
+
def ror?
|
31
|
+
ror_identifier.present?
|
32
|
+
end
|
33
|
+
|
34
|
+
# ROR URI for the Affiliation, if present.
|
35
|
+
# @return [String, nil]
|
36
|
+
# @example https://ror.org/00f54p054
|
37
|
+
def ror
|
38
|
+
ror_identifier&.uri
|
39
|
+
end
|
40
|
+
|
41
|
+
# ROR ID for the Affiliation, if present.
|
42
|
+
# @return [String, nil]
|
43
|
+
# @example 00f54p054
|
44
|
+
def ror_id
|
45
|
+
ror_identifier&.identifier
|
46
|
+
end
|
47
|
+
|
48
|
+
# Identifiers associated with the Affiliation.
|
49
|
+
# @return [Array<CocinaDisplay::Identifier>]
|
50
|
+
def identifiers
|
51
|
+
@identifiers ||= Utils.flatten_nested_values(cocina)
|
52
|
+
.pluck("identifier").flatten.compact_blank.map { |id| CocinaDisplay::Identifier.new(id) }
|
53
|
+
end
|
54
|
+
|
55
|
+
# All components of the Affiliation name as an array of strings.
|
56
|
+
# @return [Array<String>]
|
57
|
+
# @example ["Stanford University", "Department of Special Collections"]
|
58
|
+
def name_components
|
59
|
+
@name_components ||= Utils.flatten_nested_values(cocina).pluck("value").compact_blank
|
60
|
+
end
|
61
|
+
|
62
|
+
# The first Identifier object that contains a ROR ID.
|
63
|
+
# @note This will usually be the most general ROR ID, if multiple.
|
64
|
+
# @return [CocinaDisplay::Identifier, nil]
|
65
|
+
def ror_identifier
|
66
|
+
identifiers.find { |id| id.type == "ROR" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -24,12 +24,6 @@ module CocinaDisplay
|
|
24
24
|
other.is_a?(Contributor) && other.cocina == cocina
|
25
25
|
end
|
26
26
|
|
27
|
-
# Identifiers for the contributor.
|
28
|
-
# @return [Array<Identifier>]
|
29
|
-
def identifiers
|
30
|
-
Array(cocina["identifier"]).map { |id| Identifier.new(id) }
|
31
|
-
end
|
32
|
-
|
33
27
|
# Is this contributor a human?
|
34
28
|
# @return [Boolean]
|
35
29
|
def person?
|
@@ -136,6 +130,46 @@ module CocinaDisplay
|
|
136
130
|
def roles
|
137
131
|
@roles ||= Array(cocina["role"]).map { |role| Role.new(role) }
|
138
132
|
end
|
133
|
+
|
134
|
+
# Affiliation data for the contributor.
|
135
|
+
# @return [Array<CocinaDisplay::Contributors::Affiliation>]
|
136
|
+
def affiliations
|
137
|
+
@affiliations ||= Array(cocina["affiliation"]).map { |affiliation| Affiliation.new(affiliation) }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Identifiers for the contributor.
|
141
|
+
# @return [Array<Identifier>]
|
142
|
+
def identifiers
|
143
|
+
@identifiers ||= Array(cocina["identifier"]).map { |id| Identifier.new(id) }
|
144
|
+
end
|
145
|
+
|
146
|
+
# Does this contributor have an ORCID?
|
147
|
+
# @return [Boolean]
|
148
|
+
def orcid?
|
149
|
+
orcid_identifier.present?
|
150
|
+
end
|
151
|
+
|
152
|
+
# ORCID URI for the contributor, if present.
|
153
|
+
# @return [String, nil]
|
154
|
+
# @example https://orcid.org/0000-0003-4168-7198
|
155
|
+
def orcid
|
156
|
+
orcid_identifier&.uri
|
157
|
+
end
|
158
|
+
|
159
|
+
# ORCID ID for the contributor, if present.
|
160
|
+
# @return [String, nil]
|
161
|
+
# @example 0000-0003-4168-7198
|
162
|
+
def orcid_id
|
163
|
+
orcid_identifier&.identifier
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
# The first Identifier object containing an ORCID.
|
169
|
+
# @return [CocinaDisplay::Identifier, nil]
|
170
|
+
def orcid_identifier
|
171
|
+
identifiers.find { |id| id.type == "ORCID" }
|
172
|
+
end
|
139
173
|
end
|
140
174
|
end
|
141
175
|
end
|
data/lib/cocina_display/utils.rb
CHANGED
@@ -51,29 +51,29 @@ module CocinaDisplay
|
|
51
51
|
end
|
52
52
|
|
53
53
|
# Recursively remove empty values from a hash, including nested hashes and arrays.
|
54
|
-
# @param
|
55
|
-
# @
|
56
|
-
# @return [Hash] The hash with empty values removed
|
54
|
+
# @param [Hash, String, NilClass] node The object to process
|
55
|
+
# @return [Hash, String] The hash with empty values removed, string if the node you pass in is a string
|
57
56
|
# @example
|
58
57
|
# hash = { "name" => "", "age" => nil, "address => { "city" => "Anytown", "state" => [] } }
|
59
58
|
# # Utils.remove_empty_values(hash)
|
60
59
|
# #=> { "address" => { "city" => "Anytown" } }
|
61
|
-
def self.deep_compact_blank(node
|
60
|
+
def self.deep_compact_blank(node)
|
62
61
|
return node unless node.is_a?(Hash)
|
63
62
|
|
64
|
-
node.
|
65
|
-
|
63
|
+
node.each_with_object({}) do |(key, value), output|
|
64
|
+
case value
|
65
|
+
when Hash
|
66
66
|
nested = deep_compact_blank(value)
|
67
67
|
output[key] = nested unless nested.empty?
|
68
|
-
|
69
|
-
compacted_array = value.map { |v| deep_compact_blank(v) }.
|
68
|
+
when Array
|
69
|
+
compacted_array = value.map { |v| deep_compact_blank(v) }.compact_blank
|
70
70
|
output[key] = compacted_array unless compacted_array.empty?
|
71
|
-
|
71
|
+
when TrueClass, FalseClass
|
72
72
|
output[key] = value
|
73
|
+
else
|
74
|
+
output[key] = value if value.present?
|
73
75
|
end
|
74
76
|
end
|
75
|
-
|
76
|
-
output
|
77
77
|
end
|
78
78
|
end
|
79
79
|
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.
|
4
|
+
version: 1.3.0
|
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-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: janeway-jsonpath
|
@@ -246,6 +246,7 @@ files:
|
|
246
246
|
- lib/cocina_display/concerns/subjects.rb
|
247
247
|
- lib/cocina_display/concerns/titles.rb
|
248
248
|
- lib/cocina_display/concerns/url_helpers.rb
|
249
|
+
- lib/cocina_display/contributors/affiliation.rb
|
249
250
|
- lib/cocina_display/contributors/contributor.rb
|
250
251
|
- lib/cocina_display/contributors/name.rb
|
251
252
|
- lib/cocina_display/contributors/role.rb
|