cocina_display 0.2.0 → 0.4.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.
@@ -46,6 +46,16 @@ module CocinaDisplay
46
46
  [new(strategy: :all, add_punctuation: false).build(titles)].flatten - full_title(titles)
47
47
  end
48
48
 
49
+ # Like the full title, but with any non-sorting characters and punctuation removed.
50
+ # @param titles [Array<Hash>] The titles to consider.
51
+ # @param catalog_links [Array<Hash>] The folio catalog links to check for digital serials part labels.
52
+ # @return [Array<String>] The sort title value(s) for Solr - array due to possible parallelValue
53
+ def self.sort_title(titles, catalog_links: [])
54
+ part_label = catalog_links.find { |link| link["catalog"] == "folio" }&.fetch("partLabel", nil)
55
+ [new(strategy: :first, add_punctuation: false, only_one_parallel_value: false, part_label: part_label, sortable: true).build(titles)]
56
+ .flatten.compact.map { |title| title.gsub(/[[:punct:]]*/, "").strip }
57
+ end
58
+
49
59
  # @param strategy [Symbol] ":first" selects a single title value based on precedence of
50
60
  # primary, untyped, first occurrence. ":all" returns an array containing all the values.
51
61
  # @param add_punctuation [boolean] whether the title should be formatted with punctuation (think of a structured
@@ -54,11 +64,13 @@ module CocinaDisplay
54
64
  # of primary, untyped, first occurrence. When false, return an array containing all the parallel values.
55
65
  # Why? Think of e.g. title displayed in blacklight search results vs boosting values for ranking of search results
56
66
  # @param part_label [String] the partLabel to add for digital serials display
57
- def initialize(strategy:, add_punctuation:, only_one_parallel_value: true, part_label: nil)
67
+ # @param sortable [boolean] whether the title is intended for sorting, and should have non-sorting parts removed
68
+ def initialize(strategy:, add_punctuation:, only_one_parallel_value: true, part_label: nil, sortable: false)
58
69
  @strategy = strategy
59
70
  @add_punctuation = add_punctuation
60
71
  @only_one_parallel_value = only_one_parallel_value
61
72
  @part_label = part_label
73
+ @sortable = sortable
62
74
  end
63
75
 
64
76
  # @param [Array<Hash>] cocina_titles the titles to consider
@@ -161,6 +173,10 @@ module CocinaDisplay
161
173
  @only_one_parallel_value
162
174
  end
163
175
 
176
+ def sortable?
177
+ @sortable
178
+ end
179
+
164
180
  # @return [Hash, nil] title that has status=primary
165
181
  def primary_title(cocina_titles)
166
182
  primary_title = cocina_titles.find { |title| title["status"] == "primary" }
@@ -221,7 +237,7 @@ module CocinaDisplay
221
237
  # rubocop:disable Metrics/CyclomaticComplexity
222
238
  # rubocop:disable Metrics/MethodLength
223
239
  # rubocop:disable Metrics/PerceivedComplexity
224
- def rebuild_structured_value(cocina_title)
240
+ def rebuild_structured_value(cocina_title, sortable: false)
225
241
  result = ""
226
242
  part_name_number = ""
227
243
  cocina_title["structuredValue"].each do |structured_value| # rubocop:disable Metrics/BlockLength
@@ -235,8 +251,10 @@ module CocinaDisplay
235
251
  # additional types ignored here, e.g. name, uniform ...
236
252
  case structured_value["type"]&.downcase
237
253
  when "nonsorting characters"
238
- padding = non_sorting_padding(cocina_title, value)
239
- result = add_non_sorting_value(result, value, padding)
254
+ unless sortable?
255
+ padding = non_sorting_padding(cocina_title, value)
256
+ result = add_non_sorting_value(result, value, padding)
257
+ end
240
258
  when "part name", "part number"
241
259
  # even if there is a partLabel, use any existing structuredValue
242
260
  # part name/number that remains for non-digital serials purposes
@@ -286,7 +304,7 @@ module CocinaDisplay
286
304
  # rubocop:disable Metrics/PerceivedComplexity
287
305
  # rubocop:disable Metrics/AbcSize
288
306
  # rubocop:disable Metrics/CyclomaticComplexity
289
- def main_title_from_structured_values(cocina_title)
307
+ def main_title_from_structured_values(cocina_title, sortable: false)
290
308
  result = ""
291
309
  # combine pieces of the cocina structuredValue into a single title
292
310
  cocina_title["structuredValue"].each do |structured_value|
@@ -299,8 +317,10 @@ module CocinaDisplay
299
317
 
300
318
  case structured_value["type"]&.downcase
301
319
  when "nonsorting characters"
302
- padding = non_sorting_padding(cocina_title, value)
303
- result = add_non_sorting_value(result, value, padding)
320
+ unless sortable?
321
+ padding = non_sorting_padding(cocina_title, value)
322
+ result = add_non_sorting_value(result, value, padding)
323
+ end
304
324
  when "main title", "title"
305
325
  result = if ["'", "-"].include?(result.last)
306
326
  [result, value].join
@@ -0,0 +1,33 @@
1
+ module CocinaDisplay
2
+ # Helper methods for string formatting, etc.
3
+ module Utils
4
+ # Join non-empty values into a string using provided delimiter.
5
+ # If values already end in delimiter (ignoring whitespace), join with a space instead.
6
+ # @param values [Array<String>] The values to compact and join
7
+ # @param delimiter [String] The delimiter to use for joining, default is space
8
+ # @return [String] The compacted and joined string
9
+ def self.compact_and_join(values, delimiter: " ")
10
+ compacted_values = values.compact_blank.map(&:strip)
11
+ return compacted_values.first if compacted_values.one?
12
+
13
+ compacted_values.reduce(+"") do |result, value|
14
+ result << if value.end_with?(delimiter.strip)
15
+ value + " "
16
+ else
17
+ value + delimiter
18
+ end
19
+ end.delete_suffix(delimiter)
20
+ end
21
+
22
+ # Recursively flatten structured values in Cocina metadata.
23
+ # Returns a list of hashes representing the "leaf" nodes with values.
24
+ # @return [Array<Hash>] List of node hashes with "value" present
25
+ def self.flatten_structured_values(cocina, output = [])
26
+ return [cocina] if cocina["value"].present?
27
+ return cocina.flat_map { |node| flatten_structured_values(node, output) } if cocina.is_a?(Array)
28
+ return output unless (structured_values = Array(cocina["structuredValue"])).present?
29
+
30
+ structured_values.flat_map { |node| flatten_structured_values(node, output) }
31
+ end
32
+ end
33
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # :nodoc:
4
4
  module CocinaDisplay
5
- VERSION = "0.2.0" # :nodoc:
5
+ VERSION = "0.4.0" # :nodoc:
6
6
  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: 0.2.0
4
+ version: 0.4.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-06-26 00:00:00.000000000 Z
11
+ date: 2025-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: janeway-jsonpath
@@ -44,6 +44,20 @@ dependencies:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: 8.0.2
47
+ - !ruby/object:Gem::Dependency
48
+ name: edtf
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.2'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: rake
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -162,7 +176,17 @@ files:
162
176
  - Rakefile
163
177
  - lib/cocina_display.rb
164
178
  - lib/cocina_display/cocina_record.rb
179
+ - lib/cocina_display/concerns/contributors.rb
180
+ - lib/cocina_display/concerns/events.rb
181
+ - lib/cocina_display/concerns/identifiers.rb
182
+ - lib/cocina_display/concerns/titles.rb
183
+ - lib/cocina_display/contributor.rb
184
+ - lib/cocina_display/dates/date.rb
185
+ - lib/cocina_display/dates/date_range.rb
186
+ - lib/cocina_display/imprint.rb
187
+ - lib/cocina_display/marc_country_codes.rb
165
188
  - lib/cocina_display/title_builder.rb
189
+ - lib/cocina_display/utils.rb
166
190
  - lib/cocina_display/version.rb
167
191
  - sig/cocina_display.rbs
168
192
  homepage: https://sul-dlss.github.io/cocina_display/