cocina_display 0.6.0 → 0.7.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/lib/cocina_display/cocina_record.rb +2 -0
- data/lib/cocina_display/concerns/contributors.rb +5 -1
- data/lib/cocina_display/concerns/languages.rb +20 -0
- data/lib/cocina_display/concerns/subjects.rb +29 -15
- data/lib/cocina_display/contributor.rb +5 -5
- data/lib/cocina_display/dates/date_range.rb +21 -9
- data/lib/cocina_display/events/imprint.rb +0 -1
- data/lib/cocina_display/events/location.rb +2 -2
- data/lib/cocina_display/language.rb +47 -0
- data/lib/cocina_display/subjects/subject.rb +63 -0
- data/lib/cocina_display/subjects/subject_value.rb +104 -0
- data/lib/cocina_display/utils.rb +5 -3
- data/lib/cocina_display/version.rb +1 -1
- data/lib/cocina_display/vocabularies/marc_country_codes.rb +393 -0
- data/lib/cocina_display/vocabularies/marc_relator_codes.rb +318 -0
- data/lib/cocina_display/vocabularies/searchworks_languages.rb +526 -0
- metadata +23 -5
- data/lib/cocina_display/marc_country_codes.rb +0 -394
- data/lib/cocina_display/marc_relator_codes.rb +0 -314
- data/lib/cocina_display/subject.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 575cbfb8b11f3cf71950bfb5234ada4a916a5e55d21116422de84361854f0713
|
4
|
+
data.tar.gz: 4e06ee33d664822bdb6a3057ad8c40539f1f8363350f4648a463c7be0cb533ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76ecb30d8a60bbb42bab880b0c3c5288d8daa7504cc044fb06c157e1b5e2fe21fd3e56668c3479d291f1e683eab4d719c11b2ac5d185b6e98a062e917f5ea74a
|
7
|
+
data.tar.gz: ce4c61a927f9bc034aeebcd9cfc0cb56c8cd1b3c4ee13f7d8d0fcd0cc83290e8d2d1fe31ead879bdb4b1d0090ee60164158ba24d9c8eeb4a7499dfd315b9cf07
|
@@ -14,6 +14,7 @@ require_relative "concerns/titles"
|
|
14
14
|
require_relative "concerns/access"
|
15
15
|
require_relative "concerns/subjects"
|
16
16
|
require_relative "concerns/forms"
|
17
|
+
require_relative "concerns/languages"
|
17
18
|
require_relative "utils"
|
18
19
|
|
19
20
|
module CocinaDisplay
|
@@ -26,6 +27,7 @@ module CocinaDisplay
|
|
26
27
|
include CocinaDisplay::Concerns::Access
|
27
28
|
include CocinaDisplay::Concerns::Subjects
|
28
29
|
include CocinaDisplay::Concerns::Forms
|
30
|
+
include CocinaDisplay::Concerns::Languages
|
29
31
|
|
30
32
|
# Fetch a public Cocina document from PURL and create a CocinaRecord.
|
31
33
|
# @note This is intended to be used in development or testing only.
|
@@ -79,9 +79,13 @@ module CocinaDisplay
|
|
79
79
|
end
|
80
80
|
|
81
81
|
# All contributors for the object, including authors, editors, etc.
|
82
|
+
# Checks both description.contributor and description.event.contributor.
|
82
83
|
# @return [Array<Contributor>]
|
83
84
|
def contributors
|
84
|
-
@contributors ||=
|
85
|
+
@contributors ||= Enumerator::Chain.new(
|
86
|
+
path("$.description.contributor.*"),
|
87
|
+
path("$.description.event.*.contributor.*")
|
88
|
+
).map { |c| Contributor.new(c) }
|
85
89
|
end
|
86
90
|
|
87
91
|
# All contributors with a "publisher" role.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "../language"
|
2
|
+
|
3
|
+
module CocinaDisplay
|
4
|
+
module Concerns
|
5
|
+
# Methods for extracting language information from a Cocina object.
|
6
|
+
module Languages
|
7
|
+
# Languages objects associated with the object.
|
8
|
+
# @return [Array<CocinaDisplay::Language>]
|
9
|
+
def languages
|
10
|
+
@languages ||= path("$.description.language.*").map { |lang| CocinaDisplay::Language.new(lang) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Names of languages associated with the object, if recognized by Searchworks.
|
14
|
+
# @return [Array<String>]
|
15
|
+
def searchworks_language_names
|
16
|
+
languages.filter_map { |lang| lang.display_str if lang.searchworks_language? }.compact_blank.uniq
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,45 +1,46 @@
|
|
1
|
-
require_relative "../subject"
|
1
|
+
require_relative "../subjects/subject"
|
2
|
+
require_relative "../subjects/subject_value"
|
2
3
|
|
3
4
|
module CocinaDisplay
|
4
5
|
module Concerns
|
5
6
|
# Methods for extracting and formatting subject information.
|
6
7
|
module Subjects
|
7
|
-
# All unique
|
8
|
+
# All unique subject values that are topics.
|
8
9
|
# @return [Array<String>]
|
9
10
|
def subject_topics
|
10
|
-
|
11
|
+
subject_values.filter { |s| s.type == "topic" }.map(&:display_str).uniq
|
11
12
|
end
|
12
13
|
|
13
|
-
# All unique
|
14
|
+
# All unique subject values that are genres.
|
14
15
|
# @return [Array<String>]
|
15
16
|
def subject_genres
|
16
|
-
|
17
|
+
subject_values.filter { |s| s.type == "genre" }.map(&:display_str).uniq
|
17
18
|
end
|
18
19
|
|
19
|
-
# All unique
|
20
|
+
# All unique subject values that are titles.
|
20
21
|
# @return [Array<String>]
|
21
22
|
def subject_titles
|
22
|
-
|
23
|
+
subject_values.filter { |s| s.type == "title" }.map(&:display_str).uniq
|
23
24
|
end
|
24
25
|
|
25
|
-
# All unique
|
26
|
+
# All unique subject values that are date/time info.
|
26
27
|
# @return [Array<String>]
|
27
28
|
def subject_temporal
|
28
|
-
|
29
|
+
subject_values.filter { |s| s.type == "time" }.map(&:display_str).uniq
|
29
30
|
end
|
30
31
|
|
31
|
-
# All unique
|
32
|
+
# All unique subject values that are occupations.
|
32
33
|
# @return [Array<String>]
|
33
34
|
def subject_occupations
|
34
|
-
|
35
|
+
subject_values.filter { |s| s.type == "occupation" }.map(&:display_str).uniq
|
35
36
|
end
|
36
37
|
|
37
|
-
# All unique
|
38
|
+
# All unique subject values that are names of entities.
|
38
39
|
# @note Multiple types are handled: person, family, organization, conference, etc.
|
39
|
-
# @see CocinaDisplay::
|
40
|
+
# @see CocinaDisplay::NameSubjectValue
|
40
41
|
# @return [Array<String>]
|
41
42
|
def subject_names
|
42
|
-
|
43
|
+
subject_values.filter { |s| s.is_a? CocinaDisplay::Subjects::NameSubjectValue }.map(&:display_str).uniq
|
43
44
|
end
|
44
45
|
|
45
46
|
# Combination of all subject values for searching.
|
@@ -75,6 +76,13 @@ module CocinaDisplay
|
|
75
76
|
subject_temporal + subject_genres
|
76
77
|
end
|
77
78
|
|
79
|
+
# Combination of all subjects with nested values concatenated for display.
|
80
|
+
# @see Subject#display_str
|
81
|
+
# @return [Array<String>]
|
82
|
+
def subject_all_display
|
83
|
+
subjects.map(&:display_str).uniq
|
84
|
+
end
|
85
|
+
|
78
86
|
private
|
79
87
|
|
80
88
|
# All subjects, accessible as Subject objects.
|
@@ -84,7 +92,13 @@ module CocinaDisplay
|
|
84
92
|
@subjects ||= Enumerator::Chain.new(
|
85
93
|
path("$.description.subject[*]"),
|
86
94
|
path("$.description.geographic.*.subject[*]")
|
87
|
-
).map { |s| Subject.
|
95
|
+
).map { |s| CocinaDisplay::Subjects::Subject.new(s) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# All subject values, flattened from all subjects.
|
99
|
+
# @return [Array<SubjectValue>]
|
100
|
+
def subject_values
|
101
|
+
@subject_values ||= subjects.flat_map(&:subject_values)
|
88
102
|
end
|
89
103
|
end
|
90
104
|
end
|
@@ -5,7 +5,7 @@ require "active_support/core_ext/object/blank"
|
|
5
5
|
require "active_support/core_ext/array/conversions"
|
6
6
|
|
7
7
|
require_relative "utils"
|
8
|
-
require_relative "marc_relator_codes"
|
8
|
+
require_relative "vocabularies/marc_relator_codes"
|
9
9
|
|
10
10
|
module CocinaDisplay
|
11
11
|
# A contributor to a work, such as an author or publisher.
|
@@ -129,10 +129,10 @@ module CocinaDisplay
|
|
129
129
|
|
130
130
|
private
|
131
131
|
|
132
|
-
# The full name as a string, combining all name components.
|
132
|
+
# The full name as a string, combining all name components and terms of address.
|
133
133
|
# @return [String]
|
134
134
|
def full_name_str
|
135
|
-
Utils.compact_and_join(name_components, delimiter: ", ")
|
135
|
+
Utils.compact_and_join(name_components.push(terms_of_address_str), delimiter: ", ")
|
136
136
|
end
|
137
137
|
|
138
138
|
# Flattened form of any names explicitly marked as "display name".
|
@@ -146,7 +146,7 @@ module CocinaDisplay
|
|
146
146
|
# Otherwise, fall back to any names explicitly marked as "name" or untyped.
|
147
147
|
# @return [Array<String>]
|
148
148
|
def name_components
|
149
|
-
[surname_str, forename_ordinal_str
|
149
|
+
[surname_str, forename_ordinal_str].compact_blank.presence || Array(name_values["name"])
|
150
150
|
end
|
151
151
|
|
152
152
|
# Flatten all forenames and ordinals into a single string.
|
@@ -201,7 +201,7 @@ module CocinaDisplay
|
|
201
201
|
# Translates the MARC relator code if no value was present.
|
202
202
|
# @return [String, nil]
|
203
203
|
def display_str
|
204
|
-
cocina["value"] || (MARC_RELATOR[code] if marc_relator?)
|
204
|
+
cocina["value"] || (Vocabularies::MARC_RELATOR[code] if marc_relator?)
|
205
205
|
end
|
206
206
|
|
207
207
|
# A code associated with the role, e.g. a MARC relator code.
|
@@ -63,7 +63,18 @@ module CocinaDisplay
|
|
63
63
|
start&.encoding || stop&.encoding || super
|
64
64
|
end
|
65
65
|
|
66
|
-
#
|
66
|
+
# The qualifier for the entire range.
|
67
|
+
# If both qualifiers match, uses that qualifier. If both are empty, falls
|
68
|
+
# back to the top level qualifier, if any.
|
69
|
+
# @see CocinaDisplay::Date#qualifier
|
70
|
+
# @return [String, nil]
|
71
|
+
def qualifier
|
72
|
+
if start&.qualifier == stop&.qualifier
|
73
|
+
start&.qualifier || stop&.qualifier || super
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Is either date in the range, or the range itself, qualified?
|
67
78
|
# @see CocinaDisplay::Date#qualified?
|
68
79
|
# @return [Boolean]
|
69
80
|
def qualified?
|
@@ -105,14 +116,15 @@ module CocinaDisplay
|
|
105
116
|
# @see CocinaDisplay::Date#qualified_value
|
106
117
|
# @return [String]
|
107
118
|
def qualified_value
|
108
|
-
if
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
119
|
+
if qualifier
|
120
|
+
case qualifier
|
121
|
+
when "approximate"
|
122
|
+
"[ca. #{decoded_value}]"
|
123
|
+
when "questionable"
|
124
|
+
"[#{decoded_value}?]"
|
125
|
+
when "inferred"
|
126
|
+
"[#{decoded_value}]"
|
127
|
+
end
|
116
128
|
else
|
117
129
|
"#{start&.qualified_value} - #{stop&.qualified_value}"
|
118
130
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative "../marc_country_codes"
|
1
|
+
require_relative "../vocabularies/marc_country_codes"
|
2
2
|
|
3
3
|
module CocinaDisplay
|
4
4
|
module Events
|
@@ -36,7 +36,7 @@ module CocinaDisplay
|
|
36
36
|
# Decoded country name if the location is encoded with a MARC country code.
|
37
37
|
# @return [String, nil]
|
38
38
|
def decoded_country
|
39
|
-
MARC_COUNTRY[code] if marc_country? && valid_country_code?
|
39
|
+
Vocabularies::MARC_COUNTRY[code] if marc_country? && valid_country_code?
|
40
40
|
end
|
41
41
|
|
42
42
|
# Is this a decodable country code?
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "iso639"
|
2
|
+
require_relative "vocabularies/searchworks_languages"
|
3
|
+
|
4
|
+
module CocinaDisplay
|
5
|
+
# A language associated with part or all of a Cocina object.
|
6
|
+
class Language
|
7
|
+
attr_reader :cocina
|
8
|
+
|
9
|
+
# Create a Language object from Cocina structured data.
|
10
|
+
# @param cocina [Hash]
|
11
|
+
def initialize(cocina)
|
12
|
+
@cocina = cocina
|
13
|
+
end
|
14
|
+
|
15
|
+
# The language name for display.
|
16
|
+
# @return [String, nil]
|
17
|
+
def display_str
|
18
|
+
cocina["value"] || decoded_value
|
19
|
+
end
|
20
|
+
|
21
|
+
# The language code, e.g. an ISO 639 code like "eng" or "spa".
|
22
|
+
# @return [String, nil]
|
23
|
+
def code
|
24
|
+
cocina["code"]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Decoded name of the language based on the code, if present.
|
28
|
+
# @return [String, nil]
|
29
|
+
def decoded_value
|
30
|
+
Vocabularies::SEARCHWORKS_LANGUAGES[code] || (Iso639[code] if iso_639?)
|
31
|
+
end
|
32
|
+
|
33
|
+
# True if the language is recognized by Searchworks.
|
34
|
+
# @see CocinaDisplay::Vocabularies::SEARCHWORKS_LANGUAGES
|
35
|
+
# @return [Boolean]
|
36
|
+
def searchworks_language?
|
37
|
+
Vocabularies::SEARCHWORKS_LANGUAGES.value?(display_str)
|
38
|
+
end
|
39
|
+
|
40
|
+
# True if the language has a code sourced from the ISO 639 vocabulary.
|
41
|
+
# @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
|
42
|
+
# @return [Boolean]
|
43
|
+
def iso_639?
|
44
|
+
cocina.dig("source", "code")&.start_with? "iso639"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative "../utils"
|
2
|
+
require_relative "subject_value"
|
3
|
+
|
4
|
+
module CocinaDisplay
|
5
|
+
module Subjects
|
6
|
+
# Base class for subjects in Cocina structured data.
|
7
|
+
class Subject
|
8
|
+
attr_reader :cocina
|
9
|
+
|
10
|
+
# Initialize a Subject object with Cocina structured data.
|
11
|
+
# @param cocina [Hash] The Cocina structured data for the subject.
|
12
|
+
def initialize(cocina)
|
13
|
+
@cocina = cocina
|
14
|
+
end
|
15
|
+
|
16
|
+
# The top-level type of the subject.
|
17
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#subject-types
|
18
|
+
# @return [String, nil]
|
19
|
+
def type
|
20
|
+
cocina["type"]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Array of display strings for each value in the subject.
|
24
|
+
# Used for search, where each value should be indexed separately.
|
25
|
+
# @return [Array<String>]
|
26
|
+
def display_values
|
27
|
+
subject_values.map(&:display_str).compact_blank
|
28
|
+
end
|
29
|
+
|
30
|
+
# A string representation of the entire subject, formatted for display.
|
31
|
+
# Concatenates the values with an appropriate delimiter.
|
32
|
+
# @return [String]
|
33
|
+
def display_str
|
34
|
+
Utils.compact_and_join(display_values, delimiter: delimiter)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Individual values composing this subject.
|
38
|
+
# Can be multiple if the Cocina featured nested data.
|
39
|
+
# If no type was specified on a value, uses the top-level subject type.
|
40
|
+
# @return [Array<SubjectValue>]
|
41
|
+
def subject_values
|
42
|
+
@subject_values ||= Utils.flatten_nested_values(cocina, atomic_types: SubjectValue.atomic_types).map do |value|
|
43
|
+
subject_value = SubjectValue.from_cocina(value)
|
44
|
+
subject_value.type ||= type
|
45
|
+
subject_value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Delimiter to use for joining structured subject values.
|
52
|
+
# LCSH uses a comma (the default); catalog headings use " > ".
|
53
|
+
# @return [String]
|
54
|
+
def delimiter
|
55
|
+
if cocina["displayLabel"]&.downcase == "catalog heading"
|
56
|
+
" > "
|
57
|
+
else
|
58
|
+
", "
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative "subject"
|
2
|
+
require_relative "../contributor"
|
3
|
+
require_relative "../title_builder"
|
4
|
+
require_relative "../dates/date"
|
5
|
+
|
6
|
+
module CocinaDisplay
|
7
|
+
module Subjects
|
8
|
+
# A descriptive value that can be part of a Subject.
|
9
|
+
class SubjectValue
|
10
|
+
attr_reader :cocina
|
11
|
+
|
12
|
+
# The type of the subject value, like "person", "title", or "time".
|
13
|
+
# @see https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md#subject-part-types-for-structured-value
|
14
|
+
attr_accessor :type
|
15
|
+
|
16
|
+
# Create a SubjectValue from Cocina structured data.
|
17
|
+
# @param cocina [Hash] The Cocina structured data for the subject.
|
18
|
+
# @return [SubjectValue]
|
19
|
+
def self.from_cocina(cocina)
|
20
|
+
SUBJECT_VALUE_TYPES.fetch(cocina["type"], SubjectValue).new(cocina)
|
21
|
+
end
|
22
|
+
|
23
|
+
# All subject value types that should not be further destructured.
|
24
|
+
# @return [Array<String>]
|
25
|
+
def self.atomic_types
|
26
|
+
SUBJECT_VALUE_TYPES.keys
|
27
|
+
end
|
28
|
+
|
29
|
+
# Initialize a SubjectValue object with Cocina structured data.
|
30
|
+
# @param cocina [Hash] The Cocina structured data for the subject value.
|
31
|
+
def initialize(cocina)
|
32
|
+
@cocina = cocina
|
33
|
+
@type = cocina["type"]
|
34
|
+
end
|
35
|
+
|
36
|
+
# The display string for the subject value.
|
37
|
+
# Subclasses should override this method to provide specific formatting.
|
38
|
+
# @return [String]
|
39
|
+
def display_str
|
40
|
+
cocina["value"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# A subject value representing a named entity.
|
45
|
+
class NameSubjectValue < SubjectValue
|
46
|
+
attr_reader :name
|
47
|
+
|
48
|
+
# Initialize a NameSubjectValue object with Cocina structured data.
|
49
|
+
# @param cocina [Hash] The Cocina structured data for the subject.
|
50
|
+
def initialize(cocina)
|
51
|
+
super
|
52
|
+
@name = Contributor::Name.new(cocina)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Use the contributor name formatting rules for display.
|
56
|
+
# @return [String] The formatted name string, including life dates
|
57
|
+
# @see CocinaDisplay::Contributor::Name#display_str
|
58
|
+
def display_str
|
59
|
+
@name.display_str(with_date: true)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# A subject value representing an entity with a title.
|
64
|
+
class TitleSubjectValue < SubjectValue
|
65
|
+
# Construct a title string to use for display.
|
66
|
+
# @see CocinaDisplay::TitleBuilder.build
|
67
|
+
# @note Unclear how often structured title subjects occur "in the wild".
|
68
|
+
# @return [String]
|
69
|
+
def display_str
|
70
|
+
TitleBuilder.build([cocina])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# A subject value representing a date and/or time.
|
75
|
+
class TemporalSubjectValue < SubjectValue
|
76
|
+
attr_reader :date
|
77
|
+
|
78
|
+
def initialize(cocina)
|
79
|
+
super
|
80
|
+
@date = Dates::Date.from_cocina(cocina)
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [String] The formatted date/time string for display
|
84
|
+
def display_str
|
85
|
+
@date.qualified_value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Map Cocina subject types to specific SubjectValue classes for rendering.
|
92
|
+
# @see SubjectValue#type
|
93
|
+
SUBJECT_VALUE_TYPES = {
|
94
|
+
"person" => CocinaDisplay::Subjects::NameSubjectValue,
|
95
|
+
"family" => CocinaDisplay::Subjects::NameSubjectValue,
|
96
|
+
"organization" => CocinaDisplay::Subjects::NameSubjectValue,
|
97
|
+
"conference" => CocinaDisplay::Subjects::NameSubjectValue,
|
98
|
+
"event" => CocinaDisplay::Subjects::NameSubjectValue,
|
99
|
+
"name" => CocinaDisplay::Subjects::NameSubjectValue,
|
100
|
+
"title" => CocinaDisplay::Subjects::TitleSubjectValue,
|
101
|
+
"time" => CocinaDisplay::Subjects::TemporalSubjectValue
|
102
|
+
# TODO: special handling for geospatial subjects
|
103
|
+
# "map coordinates", "bounding box coordinates", "point coordinates"
|
104
|
+
}.freeze
|
data/lib/cocina_display/utils.rb
CHANGED
@@ -24,6 +24,7 @@ module CocinaDisplay
|
|
24
24
|
# @return [Array<Hash>] List of node hashes with "value" present
|
25
25
|
# @param cocina [Hash] The Cocina structured data to flatten
|
26
26
|
# @param output [Array] Used for recursion, should be empty on first call
|
27
|
+
# @param atomic_types [Array<String>] Types considered atomic; will not be flattened
|
27
28
|
# @example simple value
|
28
29
|
# cocina = { "value" => "John Doe", "type" => "name" }
|
29
30
|
# Utils.flatten_nested_values(cocina)
|
@@ -36,14 +37,15 @@ module CocinaDisplay
|
|
36
37
|
# cocina = { "parallelValue" => [{"value" => "foo" }, { "structuredValue" => [{"value" => "bar"}, {"value" => "baz"}] }] }
|
37
38
|
# Utils.flatten_nested_values(cocina)
|
38
39
|
# #=> [{"value" => "foo"}, {"value" => "foo"}, {"value" => "baz"}]
|
39
|
-
def self.flatten_nested_values(cocina, output = [])
|
40
|
+
def self.flatten_nested_values(cocina, output = [], atomic_types: [])
|
40
41
|
return [cocina] if cocina["value"].present?
|
41
|
-
return cocina
|
42
|
+
return [cocina] if atomic_types.include?(cocina["type"])
|
43
|
+
return cocina.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) } if cocina.is_a?(Array)
|
42
44
|
|
43
45
|
nested_values = Array(cocina["structuredValue"]) + Array(cocina["parallelValue"]) + Array(cocina["groupedValue"])
|
44
46
|
return output unless nested_values.any?
|
45
47
|
|
46
|
-
nested_values.flat_map { |node| flatten_nested_values(node, output) }
|
48
|
+
nested_values.flat_map { |node| flatten_nested_values(node, output, atomic_types: atomic_types) }
|
47
49
|
end
|
48
50
|
|
49
51
|
# Recursively remove empty values from a hash, including nested hashes and arrays.
|