cocina-models 0.108.3 → 0.109.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ac0ee1d7ed8ae53486e4b711aa05db3d73b2390a8083f9f1597f111ebbe3c9c
4
- data.tar.gz: 33583ad6d49089fba2434b8924302cf9c3f4ee18150100e56db61d2bc36e3972
3
+ metadata.gz: b84997ec614fd5688aafaecffc4e76731ccebe63129e215ad77bf8d13a9e8e4f
4
+ data.tar.gz: 8018ef77c9f21f189f2ec1d3fdeeba8c6b7638023beca2b1b4ca99e15e7bd910
5
5
  SHA512:
6
- metadata.gz: f57738f86a52485a98f8a6f6793a026a2a5e38d42e4cfd4c444891a3d1d115331e93d3e839d9e406550f07cd78db405111c91cb9701e5bc85b24eefb9cb7c8cd
7
- data.tar.gz: 12de4f311c73204458f59af08db308bf83c7949b72378a3c4a044590c943a494db19a98f072e4691946dbd44ae1e0ba58174ae8fdcae1745ae688d0b9447022c
6
+ metadata.gz: 44c4c1c7070a6d5ad8f1122eecce1eb45809f567f20086b46bf096fb07a66590adf8b51ae307994c9edbf80f837082121a584b3a1884fd151578f63c0991b101
7
+ data.tar.gz: 274a80c4ce5f170f468a4416a0f3948ee65a678136458f4bf4ed255676e5b1950c7f317f74f30752667f4943164ec42c0a1fcc25582d8adf50024d47a67766bf
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cocina-models (0.108.3)
4
+ cocina-models (0.109.0)
5
5
  activesupport
6
6
  deprecation
7
7
  dry-struct (~> 1.0)
@@ -19,7 +19,7 @@ PATH
19
19
  GEM
20
20
  remote: https://rubygems.org/
21
21
  specs:
22
- activesupport (8.1.1)
22
+ activesupport (8.1.2)
23
23
  base64
24
24
  bigdecimal
25
25
  concurrent-ruby (~> 1.0, >= 1.3.1)
@@ -35,15 +35,15 @@ GEM
35
35
  ast (2.4.3)
36
36
  attr_extras (7.1.0)
37
37
  base64 (0.3.0)
38
- bigdecimal (3.3.1)
38
+ bigdecimal (4.0.1)
39
39
  committee (5.0.0)
40
40
  json_schema (~> 0.14, >= 0.14.3)
41
41
  openapi_parser (~> 1.0)
42
42
  rack (>= 1.5)
43
- concurrent-ruby (1.3.5)
44
- connection_pool (2.5.4)
45
- date (3.5.0)
46
- debug (1.11.0)
43
+ concurrent-ruby (1.3.6)
44
+ connection_pool (3.0.2)
45
+ date (3.5.1)
46
+ debug (1.11.1)
47
47
  irb (~> 1.10)
48
48
  reline (>= 0.3.8)
49
49
  deprecation (1.1.0)
@@ -51,11 +51,11 @@ GEM
51
51
  diff-lcs (1.6.2)
52
52
  docile (1.4.1)
53
53
  drb (2.2.3)
54
- dry-core (1.1.0)
54
+ dry-core (1.2.0)
55
55
  concurrent-ruby (~> 1.0)
56
56
  logger
57
57
  zeitwerk (~> 2.6)
58
- dry-inflector (1.2.0)
58
+ dry-inflector (1.3.1)
59
59
  dry-logic (1.6.0)
60
60
  bigdecimal
61
61
  concurrent-ruby (~> 1.0)
@@ -66,8 +66,8 @@ GEM
66
66
  dry-types (~> 1.8, >= 1.8.2)
67
67
  ice_nine (~> 0.11)
68
68
  zeitwerk (~> 2.6)
69
- dry-types (1.8.3)
70
- bigdecimal (~> 3.0)
69
+ dry-types (1.9.0)
70
+ bigdecimal (>= 3.0)
71
71
  concurrent-ruby (~> 1.0)
72
72
  dry-core (~> 1.0)
73
73
  dry-inflector (~> 1.0)
@@ -77,16 +77,16 @@ GEM
77
77
  activesupport (>= 3.0, < 9.0)
78
78
  equivalent-xml (0.6.0)
79
79
  nokogiri (>= 1.4.3)
80
- erb (6.0.0)
81
- i18n (1.14.7)
80
+ erb (6.0.1)
81
+ i18n (1.14.8)
82
82
  concurrent-ruby (~> 1.0)
83
83
  ice_nine (0.11.2)
84
- io-console (0.8.1)
85
- irb (1.15.3)
84
+ io-console (0.8.2)
85
+ irb (1.16.0)
86
86
  pp (>= 0.6.0)
87
87
  rdoc (>= 4.0.0)
88
88
  reline (>= 0.4.2)
89
- json (2.16.0)
89
+ json (2.18.0)
90
90
  json_schema (0.21.0)
91
91
  jsonpath (1.1.5)
92
92
  multi_json
@@ -94,15 +94,16 @@ GEM
94
94
  lint_roller (1.1.0)
95
95
  logger (1.7.0)
96
96
  mini_portile2 (2.8.9)
97
- minitest (5.26.1)
98
- multi_json (1.17.0)
99
- nokogiri (1.18.10)
97
+ minitest (6.0.1)
98
+ prism (~> 1.5)
99
+ multi_json (1.19.1)
100
+ nokogiri (1.19.0)
100
101
  mini_portile2 (~> 2.8.2)
101
102
  racc (~> 1.4)
102
103
  openapi_parser (1.0.0)
103
104
  optimist (3.2.1)
104
105
  parallel (1.27.0)
105
- parser (3.3.10.0)
106
+ parser (3.3.10.1)
106
107
  ast (~> 2.4.1)
107
108
  racc
108
109
  patience_diff (1.2.0)
@@ -110,15 +111,15 @@ GEM
110
111
  pp (0.6.3)
111
112
  prettyprint
112
113
  prettyprint (0.2.0)
113
- prism (1.6.0)
114
- psych (5.2.6)
114
+ prism (1.8.0)
115
+ psych (5.3.1)
115
116
  date
116
117
  stringio
117
118
  racc (1.8.1)
118
119
  rack (3.2.4)
119
120
  rainbow (3.1.1)
120
121
  rake (13.3.1)
121
- rdoc (6.15.1)
122
+ rdoc (7.1.0)
122
123
  erb
123
124
  psych (>= 4.0.0)
124
125
  tsort
@@ -140,7 +141,7 @@ GEM
140
141
  rspec-support (3.13.6)
141
142
  rspec_junit_formatter (0.6.0)
142
143
  rspec-core (>= 2, < 4, != 2.12.0)
143
- rubocop (1.81.7)
144
+ rubocop (1.82.1)
144
145
  json (~> 2.3)
145
146
  language_server-protocol (~> 3.17.0.2)
146
147
  lint_roller (~> 1.1.0)
@@ -148,16 +149,16 @@ GEM
148
149
  parser (>= 3.3.0.2)
149
150
  rainbow (>= 2.2.2, < 4.0)
150
151
  regexp_parser (>= 2.9.3, < 3.0)
151
- rubocop-ast (>= 1.47.1, < 2.0)
152
+ rubocop-ast (>= 1.48.0, < 2.0)
152
153
  ruby-progressbar (~> 1.7)
153
154
  unicode-display_width (>= 2.4.0, < 4.0)
154
- rubocop-ast (1.48.0)
155
+ rubocop-ast (1.49.0)
155
156
  parser (>= 3.3.7.2)
156
- prism (~> 1.4)
157
+ prism (~> 1.7)
157
158
  rubocop-rake (0.7.1)
158
159
  lint_roller (~> 1.1)
159
160
  rubocop (>= 1.72.1)
160
- rubocop-rspec (3.8.0)
161
+ rubocop-rspec (3.9.0)
161
162
  lint_roller (~> 1.1)
162
163
  rubocop (~> 1.81)
163
164
  ruby-progressbar (1.13.0)
@@ -168,20 +169,20 @@ GEM
168
169
  simplecov_json_formatter (~> 0.1)
169
170
  simplecov-html (0.13.2)
170
171
  simplecov_json_formatter (0.1.4)
171
- stringio (3.1.8)
172
- super_diff (0.17.0)
172
+ stringio (3.2.0)
173
+ super_diff (0.18.0)
173
174
  attr_extras (>= 6.2.4)
174
175
  diff-lcs
175
176
  patience_diff
176
- thor (1.4.0)
177
+ thor (1.5.0)
177
178
  tsort (0.2.0)
178
179
  tzinfo (2.0.6)
179
180
  concurrent-ruby (~> 1.0)
180
181
  unicode-display_width (3.2.0)
181
182
  unicode-emoji (~> 4.1)
182
- unicode-emoji (4.1.0)
183
+ unicode-emoji (4.2.0)
183
184
  uri (1.1.1)
184
- zeitwerk (2.7.3)
185
+ zeitwerk (2.7.4)
185
186
 
186
187
  PLATFORMS
187
188
  ruby
data/docs/index.html CHANGED
@@ -15,6 +15,6 @@
15
15
  </head>
16
16
  <body>
17
17
  <redoc spec-url='https://raw.githubusercontent.com/sul-dlss/cocina-models/main/openapi.yml'></redoc>
18
- <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
18
+ <script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"> </script>
19
19
  </body>
20
20
  </html>
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMarc
7
+ # Creates Cocina Description objects from MARC record
8
+ class Description
9
+ # @see #initialize
10
+ # @see #props
11
+ def self.props(...)
12
+ new(...).props
13
+ end
14
+
15
+ # @param [Hash] marc MARC record from FOLIO
16
+ # @param [String] label
17
+ # @param [String] druid
18
+ # @param [TitleBuilder] title_builder - defaults to Title class
19
+ # @param [Cocina::Models::Mapping::ErrorNotifier] notifier
20
+ def initialize(marc:, label:, druid:, title_builder: nil, notifier: nil)
21
+ @title_builder = title_builder || Title
22
+ @marc = marc
23
+ @notifier = notifier || ErrorNotifier.new(druid: druid)
24
+ @druid = druid
25
+ @label = label
26
+ end
27
+
28
+ # @return [Hash] a hash that can be mapped to a Cocina Description model
29
+ def props
30
+ return nil if marc.nil?
31
+
32
+ DescriptionBuilder.build(title_builder: title_builder,
33
+ marc: marc,
34
+ notifier: notifier,
35
+ purl: druid ? Cocina::Models::Mapping::Purl.for(druid: druid) : nil).tap do |properties|
36
+ properties[:title] = [{ value: label }] unless properties.key?(:title)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :title_builder, :marc, :notifier, :druid, :label
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMarc
7
+ # Creates Cocina Description objects from MARC records
8
+ class DescriptionBuilder
9
+ BUILDERS = {
10
+ # TODO: implement these builders for MARC
11
+ # note: Note,
12
+ # language: Language,
13
+ # contributor: Contributor,
14
+ # event: Event,
15
+ # subject: Subject,
16
+ # form: Form,
17
+ # identifier: Identifier,
18
+ # adminMetadata: AdminMetadata,
19
+ # relatedResource: RelatedResource,
20
+ # geographic: Geographic,
21
+ # access: Access
22
+ }.freeze
23
+
24
+ # @param [Hash] marc MARC record
25
+ # @param [Cocina::Models::Mapping::ErrorNotifier] notifier
26
+ # @param [TitleBuilder] title_builder - defaults to Title class
27
+ # @param [String] purl
28
+ # @return [Hash] a hash that can be mapped to a Cocina Description model
29
+ def self.build(marc:, notifier:, title_builder: Title, purl: nil)
30
+ new(title_builder: title_builder, notifier: notifier).build(marc: marc, purl: purl)
31
+ end
32
+
33
+ def initialize(notifier:, title_builder: Title)
34
+ @title_builder = title_builder
35
+ @notifier = notifier
36
+ end
37
+
38
+ # @return [Hash] a hash that can be mapped to a Cocina Description model
39
+ def build(marc:, purl: nil, require_title: true)
40
+ cocina_description = {}
41
+ title_result = title_builder.build(marc: marc, require_title: require_title,
42
+ notifier: notifier)
43
+ cocina_description[:title] = title_result if title_result.present?
44
+ cocina_description[:purl] = purl
45
+
46
+ BUILDERS.each do |description_property, builder|
47
+ result = builder.build(marc: marc, description_builder: self)
48
+ cocina_description.merge!(description_property => result) if result.present?
49
+ end
50
+ cocina_description
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :notifier, :title_builder
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMarc
7
+ # Maps titles
8
+ class Title
9
+ # @param [Hash] marc MARC record from FOLIO
10
+ # @param [boolean] require_title notify if true and title is missing.
11
+ # @param [Cocina::Models::Mapping::ErrorNotifier] notifier
12
+ # @return [Hash] a hash that can be mapped to a cocina model
13
+ def self.build(marc:, notifier:, require_title: true)
14
+ new(marc: marc, notifier: notifier).build(require_title: require_title)
15
+ end
16
+
17
+ def initialize(marc:, notifier:)
18
+ @marc = marc
19
+ @notifier = notifier
20
+ end
21
+
22
+ def build(require_title: true)
23
+ result = TitleBuilder.build(marc: marc, notifier: notifier)
24
+ notifier.error('Missing title') if result.nil? && require_title
25
+
26
+ result
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :marc, :notifier
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Mapping
6
+ module FromMarc
7
+ # Maps titles
8
+ class TitleBuilder
9
+ # @see #initialize
10
+ # @see #build
11
+ def self.build(...)
12
+ new(...).build
13
+ end
14
+
15
+ # @param [Hash] marc MARC record from FOLIO
16
+ # @param [Cocina::Models::Mapping::ErrorNotifier] notifier
17
+ def initialize(marc:, notifier:)
18
+ @marc = marc
19
+ @notifier = notifier
20
+ end
21
+
22
+ # @return [Hash] a hash that can be mapped to a cocina model
23
+ def build
24
+ title_fields = %w[245 246 240 130 740]
25
+ unless title_fields.any? { |field| fields.any? { |f| f.key?(field) } }
26
+ notifier.warn('No title fields found')
27
+ return nil
28
+ end
29
+
30
+ # Return a basic title
31
+ # TODO: implement structured values
32
+ basic_title if basic_title?
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :marc, :notifier
38
+
39
+ def fields
40
+ marc['fields']
41
+ end
42
+
43
+ def basic_title?
44
+ tag = fields.find { |field| field.key?('245') }
45
+ return false unless tag
46
+
47
+ subfields = tag.dig('245', 'subfields')
48
+ ind2 = tag.dig('245', 'ind2')
49
+ return false unless ind2 == '0' && subfields.any? { |subfield| subfield.key?('a') }
50
+
51
+ true
52
+ end
53
+
54
+ def basic_title
55
+ tag = fields.find { |field| field.key?('245') }
56
+ subfields = tag.dig('245', 'subfields')
57
+ title = subfields.find { |subfield| subfield.key?('a') }
58
+
59
+ title_value = strip_punctuation(title['a'])
60
+ [{ value: title_value }]
61
+ end
62
+
63
+ def strip_punctuation(value)
64
+ # Remove set of punctuation characters at the end of the subfield
65
+ escaped_chars = Regexp.escape(':;/[, ')
66
+ regex = /[#{escaped_chars}]+\z/
67
+ value.gsub(regex, '')
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -52,7 +52,7 @@ module Cocina
52
52
  .values
53
53
  .select { |nodes| nodes.size > 1 }
54
54
  .each do |nodes|
55
- notifier.warn('Unpaired altRepGroup') if altrepgroup_error?(nodes)
55
+ notifier.warn('Unpaired altRepGroup') if altrepgroup_error?(nodes)
56
56
  end
57
57
  end
58
58
 
@@ -30,12 +30,12 @@ module Cocina
30
30
  def write
31
31
  Array(contributors)
32
32
  .reject do |contributor|
33
- NameTitleGroup.in_name_title_group?(contributor: contributor,
34
- titles: titles)
33
+ NameTitleGroup.in_name_title_group?(contributor: contributor,
34
+ titles: titles)
35
35
  end
36
36
  .each do |contributor|
37
- NameWriter.write(xml: xml, contributor: contributor,
38
- id_generator: id_generator)
37
+ NameWriter.write(xml: xml, contributor: contributor,
38
+ id_generator: id_generator)
39
39
  end
40
40
  end
41
41
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cocina
4
4
  module Models
5
- VERSION = '0.108.3'
5
+ VERSION = '0.109.0'
6
6
  end
7
7
  end
@@ -84,7 +84,7 @@ module Cocina
84
84
  build_request_dro_properties(**)
85
85
  .merge(externalIdentifier: id)
86
86
  .tap do |props|
87
- props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
87
+ props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
88
88
  end
89
89
  end
90
90
 
@@ -138,7 +138,7 @@ module Cocina
138
138
  build_request_collection_properties(**)
139
139
  .merge(externalIdentifier: id)
140
140
  .tap do |props|
141
- props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
141
+ props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
142
142
  end
143
143
  end
144
144
 
@@ -198,7 +198,7 @@ module Cocina
198
198
  build_request_admin_policy_properties(**)
199
199
  .merge(externalIdentifier: id)
200
200
  .tap do |props|
201
- props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
201
+ props[:description][:purl] = "https://purl.stanford.edu/#{id.delete_prefix('druid:')}"
202
202
  end
203
203
  end
204
204
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cocina-models
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.108.3
4
+ version: 0.109.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Coyne
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-11-21 00:00:00.000000000 Z
10
+ date: 2026-01-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -413,6 +413,10 @@ files:
413
413
  - lib/cocina/models/location_based_download_access.rb
414
414
  - lib/cocina/models/mapping/error_notifier.rb
415
415
  - lib/cocina/models/mapping/escape_html.rb
416
+ - lib/cocina/models/mapping/from_marc/description.rb
417
+ - lib/cocina/models/mapping/from_marc/description_builder.rb
418
+ - lib/cocina/models/mapping/from_marc/title.rb
419
+ - lib/cocina/models/mapping/from_marc/title_builder.rb
416
420
  - lib/cocina/models/mapping/from_mods/access.rb
417
421
  - lib/cocina/models/mapping/from_mods/admin_metadata.rb
418
422
  - lib/cocina/models/mapping/from_mods/alt_rep_group.rb