cocina-models 0.79.0 → 0.82.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +6 -5
- data/cocina-models.gemspec +3 -0
- data/docs/description_types.md +1 -0
- data/lib/cocina/generator/schema.rb +2 -2
- data/lib/cocina/generator/schema_base.rb +10 -0
- data/lib/cocina/generator/schema_value.rb +0 -4
- data/lib/cocina/models/access_role.rb +1 -0
- data/lib/cocina/models/access_role_member.rb +1 -0
- data/lib/cocina/models/admin_policy_access_template.rb +1 -0
- data/lib/cocina/models/admin_policy_administrative.rb +1 -0
- data/lib/cocina/models/admin_policy_with_metadata.rb +1 -0
- data/lib/cocina/models/applies_to.rb +1 -0
- data/lib/cocina/models/builders/name_title_group_builder.rb +0 -1
- data/lib/cocina/models/collection.rb +1 -0
- data/lib/cocina/models/collection_access.rb +1 -0
- data/lib/cocina/models/collection_with_metadata.rb +1 -0
- data/lib/cocina/models/contributor.rb +1 -0
- data/lib/cocina/models/descriptive_access_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_admin_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_basic_value.rb +1 -0
- data/lib/cocina/models/descriptive_geographic_metadata.rb +1 -0
- data/lib/cocina/models/descriptive_grouped_value.rb +1 -0
- data/lib/cocina/models/descriptive_parallel_contributor.rb +2 -0
- data/lib/cocina/models/descriptive_parallel_event.rb +1 -0
- data/lib/cocina/models/descriptive_parallel_value.rb +1 -0
- data/lib/cocina/models/descriptive_structured_value.rb +1 -0
- data/lib/cocina/models/descriptive_value.rb +1 -0
- data/lib/cocina/models/descriptive_value_language.rb +1 -0
- data/lib/cocina/models/dro.rb +1 -0
- data/lib/cocina/models/dro_structural.rb +1 -0
- data/lib/cocina/models/dro_with_metadata.rb +1 -0
- data/lib/cocina/models/event.rb +1 -0
- data/lib/cocina/models/file.rb +1 -0
- data/lib/cocina/models/file_access.rb +1 -0
- data/lib/cocina/models/file_set.rb +1 -0
- data/lib/cocina/models/file_set_structural.rb +1 -0
- data/lib/cocina/models/geographic.rb +1 -0
- data/lib/cocina/models/language.rb +1 -0
- data/lib/cocina/models/mapping/escape_html.rb +23 -0
- data/lib/cocina/models/mapping/from_mods/name_builder.rb +79 -23
- data/lib/cocina/models/mapping/normalizers/mods/name_normalizer.rb +27 -2
- data/lib/cocina/models/mapping/to_mods/name_writer.rb +79 -19
- data/lib/cocina/models/mapping/to_mods/note.rb +3 -1
- data/lib/cocina/models/mapping/to_mods/subject.rb +2 -2
- data/lib/cocina/models/message_digest.rb +1 -0
- data/lib/cocina/models/object_metadata.rb +1 -0
- data/lib/cocina/models/presentation.rb +1 -0
- data/lib/cocina/models/related_resource.rb +1 -0
- data/lib/cocina/models/release_tag.rb +1 -0
- data/lib/cocina/models/request_admin_policy.rb +1 -0
- data/lib/cocina/models/request_collection.rb +1 -0
- data/lib/cocina/models/request_description.rb +1 -0
- data/lib/cocina/models/request_dro.rb +1 -0
- data/lib/cocina/models/request_dro_structural.rb +1 -0
- data/lib/cocina/models/request_file_set_structural.rb +1 -0
- data/lib/cocina/models/request_identification.rb +1 -0
- data/lib/cocina/models/sequence.rb +1 -0
- data/lib/cocina/models/source.rb +1 -0
- data/lib/cocina/models/standard.rb +1 -0
- data/lib/cocina/models/validators/date_time_validator.rb +102 -0
- data/lib/cocina/models/version.rb +1 -1
- data/openapi.yml +1 -0
- metadata +47 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebc24ff8dbe8eb1fe740eb977abb73abc504409aa510905395e6660adcd05b3b
|
4
|
+
data.tar.gz: 62ad4e7cafce859250323e92cc2173257957b16775e767224a4de2e9fd639bae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a3b0ed8ab383f945450d42343dbaed6e709854ff0f3deca1f62b93c79883d3157b42f14cdaafd9392ef858620a0c58ae6867c16037c627499efddf3cb33880b
|
7
|
+
data.tar.gz: 9e1084ea73c6f547eab7b8a076d9e2a61a46361c9da1541effca8529d48d032621b6cd5868bd2886308aea1f834b4c586ae1058ca6f67f0315393e4eb632bbb9
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config --auto-gen-only-exclude`
|
3
|
-
# on 2022-
|
3
|
+
# on 2022-06-08 23:20:02 UTC using RuboCop version 1.28.2.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -35,7 +35,7 @@ Lint/UnusedMethodArgument:
|
|
35
35
|
- 'lib/cocina/models/mapping/from_mods/subject.rb'
|
36
36
|
- 'lib/cocina/models/mapping/to_mods/event.rb'
|
37
37
|
|
38
|
-
# Offense count:
|
38
|
+
# Offense count: 96
|
39
39
|
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
|
40
40
|
Metrics/AbcSize:
|
41
41
|
Max: 40
|
@@ -56,16 +56,17 @@ Metrics/ParameterLists:
|
|
56
56
|
RSpec/DescribeClass:
|
57
57
|
Enabled: false
|
58
58
|
|
59
|
-
# Offense count:
|
59
|
+
# Offense count: 87
|
60
60
|
# Configuration parameters: CountAsOne.
|
61
61
|
RSpec/ExampleLength:
|
62
62
|
Max: 128
|
63
63
|
|
64
|
-
# Offense count:
|
64
|
+
# Offense count: 10
|
65
65
|
# Configuration parameters: Max.
|
66
66
|
RSpec/NestedGroups:
|
67
67
|
Exclude:
|
68
68
|
- 'spec/cocina/models/mapping/normalizers/mods/origin_info_normalizer_spec.rb'
|
69
|
+
- 'spec/cocina/models/validators/date_time_validator_spec.rb'
|
69
70
|
|
70
71
|
# Offense count: 1
|
71
72
|
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
@@ -87,7 +88,7 @@ Style/MultilineBlockChain:
|
|
87
88
|
- 'lib/cocina/models/mapping/to_mods/form.rb'
|
88
89
|
- 'lib/cocina/models/mapping/to_mods/subject.rb'
|
89
90
|
|
90
|
-
# Offense count:
|
91
|
+
# Offense count: 223
|
91
92
|
# This cop supports safe auto-correction (--auto-correct).
|
92
93
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
|
93
94
|
# URISchemes: http, https
|
data/cocina-models.gemspec
CHANGED
@@ -28,12 +28,15 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency 'deprecation'
|
29
29
|
spec.add_dependency 'dry-struct', '~> 1.0'
|
30
30
|
spec.add_dependency 'dry-types', '~> 1.1'
|
31
|
+
spec.add_dependency 'edtf' # used for date/time validation
|
31
32
|
spec.add_dependency 'equivalent-xml' # for diffing MODS
|
33
|
+
spec.add_dependency 'jsonpath' # used for date/time validation
|
32
34
|
spec.add_dependency 'nokogiri'
|
33
35
|
spec.add_dependency 'openapi3_parser' # Parsing openapi doc
|
34
36
|
# Match these version requirements to what committee wants,
|
35
37
|
# so that our client (non-committee) users have the same dependencies.
|
36
38
|
spec.add_dependency 'openapi_parser', '>= 0.11.1', '< 1.0'
|
39
|
+
spec.add_dependency 'rss' # used for date/time validation
|
37
40
|
spec.add_dependency 'super_diff'
|
38
41
|
spec.add_dependency 'thor'
|
39
42
|
spec.add_dependency 'zeitwerk', '~> 2.1'
|
data/docs/description_types.md
CHANGED
@@ -41,6 +41,12 @@ module Cocina
|
|
41
41
|
"# #{schema_doc.description}\n"
|
42
42
|
end
|
43
43
|
|
44
|
+
def deprecation
|
45
|
+
return '' unless schema_doc.deprecated?
|
46
|
+
|
47
|
+
"# DEPRECATED\n"
|
48
|
+
end
|
49
|
+
|
44
50
|
def example
|
45
51
|
return '' unless schema_doc.example
|
46
52
|
|
@@ -82,6 +88,10 @@ module Cocina
|
|
82
88
|
'Strict::String'
|
83
89
|
end
|
84
90
|
end
|
91
|
+
|
92
|
+
def preamble
|
93
|
+
"#{deprecation}#{description}#{example}#{relaxed_comment}"
|
94
|
+
end
|
85
95
|
end
|
86
96
|
end
|
87
97
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Access role conferred by an AdminPolicy to objects within it. (used by Argo)
|
5
6
|
class AccessRole < Struct
|
6
7
|
# Name of role
|
7
8
|
attribute :name, Types::Strict::String.enum('dor-apo-depositor', 'dor-apo-manager', 'dor-apo-viewer', 'sdr-administrator', 'sdr-viewer', 'hydrus-collection-creator', 'hydrus-collection-manager', 'hydrus-collection-depositor', 'hydrus-collection-item-depositor', 'hydrus-collection-reviewer', 'hydrus-collection-viewer')
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Provides the template of access settings that is copied to the items goverend by an AdminPolicy. This is almost the same as DROAccess, but it provides no defaults and has no embargo.
|
5
6
|
class AdminPolicyAccessTemplate < Struct
|
6
7
|
attribute? :view, Types::Strict::String.enum('world', 'stanford', 'location-based', 'citation-only', 'dark')
|
7
8
|
# Available for controlled digital lending.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Administrative properties for an AdminPolicy
|
5
6
|
class AdminPolicyAdministrative < Struct
|
6
7
|
attribute(:accessTemplate, AdminPolicyAccessTemplate.default { AdminPolicyAccessTemplate.new })
|
7
8
|
attribute :registrationWorkflow, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for indicating the parts, aspects, or versions of the resource to which a descriptive element is applicable.
|
5
6
|
class AppliesTo < Struct
|
6
7
|
attribute :appliesTo, Types::Strict::Array.of(DescriptiveBasicValue).default([].freeze)
|
7
8
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for describing agents contributing in some way to the creation and history of the resource.
|
5
6
|
class Contributor < Struct
|
6
7
|
attribute :name, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Information about how to access digital and physical versions of the object.
|
5
6
|
class DescriptiveAccessMetadata < Struct
|
6
7
|
attribute :url, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :physicalLocation, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Information about this resource description.
|
5
6
|
class DescriptiveAdminMetadata < Struct
|
6
7
|
attribute :contributor, Types::Strict::Array.of(Contributor).default([].freeze)
|
7
8
|
attribute :event, Types::Strict::Array.of(Event).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Basic value model for descriptive elements. Can only have one of value, parallelValue, groupedValue, or structuredValue.
|
5
6
|
class DescriptiveBasicValue < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for mods geographic extension metadata
|
5
6
|
class DescriptiveGeographicMetadata < Struct
|
6
7
|
attribute :form, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :subject, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# DEPRECATED
|
6
|
+
# Value model for multiple representations of information about the same contributor (e.g. in different languages).
|
5
7
|
class DescriptiveParallelContributor < Struct
|
6
8
|
attribute :name, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
9
|
# Entity type of the contributor (person, organization, etc.). See https://github.com/sul-dlss/cocina-models/blob/main/docs/description_types.md for valid types.
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for multiple representations of information about the same event (e.g. in different languages).
|
5
6
|
class DescriptiveParallelEvent < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Description of the event (creation, publication, etc.).
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Value model for multiple representations of the same information (e.g. in different languages).
|
5
6
|
class DescriptiveParallelValue < Struct
|
6
7
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Default value model for descriptive elements.
|
5
6
|
class DescriptiveValue < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
attribute :parallelValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
data/lib/cocina/models/dro.rb
CHANGED
data/lib/cocina/models/event.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Property model for describing events in the history of the resource.
|
5
6
|
class Event < Struct
|
6
7
|
attribute :structuredValue, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
7
8
|
# Description of the event (creation, publication, etc.).
|
data/lib/cocina/models/file.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# Languages, scripts, symbolic systems, and notations used in all or part of a resource or its descriptive metadata.
|
5
6
|
class Language < Struct
|
6
7
|
attribute :appliesTo, Types::Strict::Array.of(DescriptiveBasicValue).default([].freeze)
|
7
8
|
# Code value of the descriptive element.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Mapping
|
6
|
+
# Escaps HTML entities as CDATA for MODS since HTML is not permitted in MODS
|
7
|
+
class EscapeHtml
|
8
|
+
# @param [String] data
|
9
|
+
# @param [Nokogiri::XML::Builder]
|
10
|
+
def self.with_cdata(data, builder)
|
11
|
+
tokens = data.split(%r{(</?(?:i|cite)>)})
|
12
|
+
tokens.map do |token|
|
13
|
+
if /\A<.+>\z/.match? token
|
14
|
+
builder.cdata(token)
|
15
|
+
else
|
16
|
+
builder.text(token)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -36,13 +36,13 @@ module Cocina
|
|
36
36
|
|
37
37
|
def build_parallel
|
38
38
|
names = {
|
39
|
-
parallelValue:
|
39
|
+
parallelValue: build_parallel_values,
|
40
40
|
type: type_for(name_elements.first['type']),
|
41
41
|
status: name_elements.filter_map { |name_element| name_element['usage'] }.first
|
42
42
|
}.compact
|
43
43
|
{ name: [names] }.tap do |attrs|
|
44
|
-
|
45
|
-
attrs[:
|
44
|
+
attrs[:role] = name_elements.flat_map { |name_node| build_roles(name_node) }.compact.uniq.presence
|
45
|
+
attrs[:note] = name_elements.flat_map { |name_node| build_affiliation_notes(name_node) }.compact.uniq.presence
|
46
46
|
end.compact
|
47
47
|
end
|
48
48
|
|
@@ -51,8 +51,8 @@ module Cocina
|
|
51
51
|
status: name_node['usage']
|
52
52
|
}.compact.merge(common_lang_script(name_node))
|
53
53
|
|
54
|
-
name_attrs = name_attrs.merge(common_name(name_node, name_attrs[:name]))
|
55
|
-
name_parts = build_name_parts(name_node)
|
54
|
+
name_attrs = name_attrs.merge(common_name(name_node, name_attrs[:name], is_parallel: true))
|
55
|
+
name_parts = build_name_parts(name_node, parallel: true)
|
56
56
|
notifier.warn('Missing name/namePart element') if name_parts.all?(&:empty?)
|
57
57
|
name_parts.each { |name_part| name_attrs = name_part.merge(name_attrs) }
|
58
58
|
name_attrs.compact
|
@@ -85,9 +85,9 @@ module Cocina
|
|
85
85
|
}.compact.merge(common_name(name_node, name_parts))
|
86
86
|
end
|
87
87
|
|
88
|
-
def common_name(name_node, name)
|
88
|
+
def common_name(name_node, name, is_parallel: false)
|
89
89
|
{
|
90
|
-
note: build_notes(name_node),
|
90
|
+
note: build_notes(name_node, is_parallel: is_parallel),
|
91
91
|
identifier: build_identifier(name_node)
|
92
92
|
}.tap do |attrs|
|
93
93
|
roles = build_roles(name_node)
|
@@ -106,7 +106,17 @@ module Cocina
|
|
106
106
|
end.compact
|
107
107
|
end
|
108
108
|
|
109
|
-
def
|
109
|
+
def build_parallel_values
|
110
|
+
parallel_values = []
|
111
|
+
name_elements.each do |name_node|
|
112
|
+
parallel_values << build_parallel_name(name_node)
|
113
|
+
display_val = display_value(name_node)
|
114
|
+
parallel_values << display_val if display_val
|
115
|
+
end
|
116
|
+
parallel_values.compact
|
117
|
+
end
|
118
|
+
|
119
|
+
def build_name_parts(name_node, parallel: false)
|
110
120
|
name_part_nodes = name_node.xpath('mods:namePart', mods: Description::DESC_METADATA_NS)
|
111
121
|
alternative_name_nodes = name_node.xpath('mods:alternativeName', mods: Description::DESC_METADATA_NS)
|
112
122
|
|
@@ -116,23 +126,64 @@ module Cocina
|
|
116
126
|
parts << { valueAt: name_node['xlink:href'] } if name_node['xlink:href']
|
117
127
|
parts << common_authority(name_node) if name_node['valueURI']
|
118
128
|
when 1
|
119
|
-
|
120
|
-
|
121
|
-
.merge(common_authority(name_node)).merge(common_lang_script(name_node)).presence
|
129
|
+
name = build_simple_value_name(name_node, name_part_nodes.first, alternative_name_nodes, parallel)
|
130
|
+
parts << name.merge(common_authority(name_node)).merge(common_lang_script(name_node)).presence
|
122
131
|
else
|
123
|
-
|
124
|
-
|
125
|
-
end
|
126
|
-
parts << { structuredValue: vals }.merge(common_authority(name_node)).merge(common_lang_script(name_node))
|
132
|
+
name = build_structured_value_name(name_node, name_part_nodes)
|
133
|
+
parts << name.merge(common_authority(name_node)).merge(common_lang_script(name_node))
|
127
134
|
end
|
128
135
|
|
129
136
|
parts = build_alternative_name(alternative_name_nodes, parts) if alternative_name_nodes.present?
|
130
|
-
|
131
|
-
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
132
|
-
parts << { value: display_form.text, type: 'display' } if display_form
|
133
137
|
parts.compact
|
134
138
|
end
|
135
139
|
|
140
|
+
def build_simple_value_name(name_node, name_part_node, alternative_name_nodes, parallel)
|
141
|
+
name_value_hash = build_name_part(name_node, name_part_node, default_type: alternative_name_nodes.present?)
|
142
|
+
display_form_node = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
143
|
+
if display_form_node.present? && !parallel
|
144
|
+
cocina_contrib_name =
|
145
|
+
{
|
146
|
+
parallelValue: [
|
147
|
+
name_value_hash
|
148
|
+
]
|
149
|
+
}
|
150
|
+
add_display_parallel_value(name_node, cocina_contrib_name)
|
151
|
+
else
|
152
|
+
name_value_hash
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def build_structured_value_name(name_node, name_part_nodes)
|
157
|
+
vals = name_part_nodes.filter_map { |name_part_node| build_name_part(name_node, name_part_node, default_type: name_node['type'] != 'corporate').presence }
|
158
|
+
display_form_node = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS).first
|
159
|
+
if display_form_node.present?
|
160
|
+
cocina_contrib_name =
|
161
|
+
{
|
162
|
+
parallelValue: [
|
163
|
+
{ structuredValue: vals }
|
164
|
+
]
|
165
|
+
}
|
166
|
+
add_display_parallel_value(name_node, cocina_contrib_name)
|
167
|
+
else
|
168
|
+
{ structuredValue: vals }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def add_display_parallel_value(name_node, cocina_contrib_name)
|
173
|
+
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS)&.text
|
174
|
+
return cocina_contrib_name if display_form.blank?
|
175
|
+
|
176
|
+
display_parallel_value = display_value(name_node)
|
177
|
+
cocina_contrib_name[:parallelValue] << display_parallel_value if display_parallel_value && cocina_contrib_name[:parallelValue].present?
|
178
|
+
|
179
|
+
cocina_contrib_name
|
180
|
+
end
|
181
|
+
|
182
|
+
def display_value(name_node)
|
183
|
+
display_form = name_node.xpath('mods:displayForm', mods: Description::DESC_METADATA_NS)&.text
|
184
|
+
{ value: display_form, type: 'display' } if display_form.present?
|
185
|
+
end
|
186
|
+
|
136
187
|
def build_name_part(name_node, name_part_node, default_type: true)
|
137
188
|
if name_part_node.content.blank? && !name_part_node['xlink:href']
|
138
189
|
notifier.warn('name/namePart missing value')
|
@@ -224,12 +275,8 @@ module Cocina
|
|
224
275
|
end.presence
|
225
276
|
end
|
226
277
|
|
227
|
-
def build_notes(name_node)
|
278
|
+
def build_notes(name_node, is_parallel:)
|
228
279
|
[].tap do |parts|
|
229
|
-
name_node.xpath('mods:affiliation', mods: Description::DESC_METADATA_NS).each do |affiliation_node|
|
230
|
-
parts << { value: affiliation_node.text, type: 'affiliation' }
|
231
|
-
end
|
232
|
-
|
233
280
|
description = name_node.xpath('mods:description', mods: Description::DESC_METADATA_NS).first
|
234
281
|
if description
|
235
282
|
parts << if description.text == UNCITED_DESCRIPTION
|
@@ -238,9 +285,18 @@ module Cocina
|
|
238
285
|
{ value: description.text, type: 'description' }
|
239
286
|
end
|
240
287
|
end
|
288
|
+
parts.concat(build_affiliation_notes(name_node)) unless is_parallel
|
241
289
|
end.presence
|
242
290
|
end
|
243
291
|
|
292
|
+
def build_affiliation_notes(name_node)
|
293
|
+
[].tap do |parts|
|
294
|
+
name_node.xpath('mods:affiliation', mods: Description::DESC_METADATA_NS).each do |affiliation_node|
|
295
|
+
parts << { value: affiliation_node.text, type: 'affiliation' }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
244
300
|
def build_roles(name_node)
|
245
301
|
role_nodes = name_node.xpath('mods:role', mods: Description::DESC_METADATA_NS)
|
246
302
|
role_nodes.filter_map { |role_node| role_for(role_node) }.presence
|
@@ -20,6 +20,7 @@ module Cocina
|
|
20
20
|
|
21
21
|
def normalize
|
22
22
|
normalize_parallel_name_role
|
23
|
+
normalize_parallel_affiliation_note
|
23
24
|
normalize_text_role_term
|
24
25
|
normalize_role_term
|
25
26
|
normalize_role # must be after normalize_role_term
|
@@ -35,10 +36,13 @@ module Cocina
|
|
35
36
|
|
36
37
|
attr_reader :ng_xml
|
37
38
|
|
39
|
+
def grouped_name_nodes
|
40
|
+
name_nodes = ng_xml.root.xpath('//mods:name[@altRepGroup]', mods: ModsNormalizer::MODS_NS)
|
41
|
+
name_nodes.group_by { |name_node| name_node['altRepGroup'] }.values.reject { |name_node_group| name_node_group.size == 1 }
|
42
|
+
end
|
43
|
+
|
38
44
|
def normalize_parallel_name_role
|
39
45
|
# For parallel names, all should have the same roles.
|
40
|
-
name_nodes = ng_xml.root.xpath('//mods:name[@altRepGroup]', mods: ModsNormalizer::MODS_NS)
|
41
|
-
grouped_name_nodes = name_nodes.group_by { |name_node| name_node['altRepGroup'] }.values.reject { |name_node_group| name_node_group.size == 1 }
|
42
46
|
grouped_name_nodes.each do |name_node_group|
|
43
47
|
name_node_with_role = name_node_group.find { |name_node| role_node_for(name_node) }
|
44
48
|
next unless name_node_with_role
|
@@ -58,6 +62,27 @@ module Cocina
|
|
58
62
|
name_node.xpath('mods:role', mods: ModsNormalizer::MODS_NS).first
|
59
63
|
end
|
60
64
|
|
65
|
+
def normalize_parallel_affiliation_note
|
66
|
+
# For parallel names, all should have the same affiliation.
|
67
|
+
grouped_name_nodes.each do |name_node_group|
|
68
|
+
name_node_with_affiliation = name_node_group.find { |name_node| affiliation_node_for(name_node) }
|
69
|
+
next unless name_node_with_affiliation
|
70
|
+
|
71
|
+
name_node_group.each do |name_node|
|
72
|
+
next if name_node == name_node_with_affiliation
|
73
|
+
|
74
|
+
existing_affiliation_node = affiliation_node_for(name_node)
|
75
|
+
existing_affiliation_node&.remove
|
76
|
+
|
77
|
+
name_node << affiliation_node_for(name_node_with_affiliation).dup
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def affiliation_node_for(name_node)
|
83
|
+
name_node.xpath('mods:affiliation', mods: ModsNormalizer::MODS_NS).first
|
84
|
+
end
|
85
|
+
|
61
86
|
def normalize_text_role_term
|
62
87
|
# Add the type="text" attribute to roleTerms that don't have a type (seen in MODS 3.3 druid:yy910cj7795)
|
63
88
|
ng_xml.root.xpath('//mods:roleTerm[not(@type)]', mods: ModsNormalizer::MODS_NS).each do |role_term_node|
|
@@ -36,25 +36,11 @@ module Cocina
|
|
36
36
|
if contributor.type == 'unspecified others'
|
37
37
|
write_etal
|
38
38
|
elsif contributor.name.present?
|
39
|
+
# Expect contributor to have a single value for name property
|
39
40
|
contrib_name = contributor.name.first
|
40
|
-
|
41
|
-
if
|
42
|
-
|
43
|
-
parallel_values.each do |parallel_contrib_name|
|
44
|
-
Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_contrib_name)&.each do |parallel_contrib_name_slice|
|
45
|
-
if name_title_vals_index[parallel_contrib_name_slice]
|
46
|
-
name_title_group = name_title_vals_index[parallel_contrib_name_slice]&.values&.first
|
47
|
-
write_parallel_contributor(contributor, contrib_name, parallel_contrib_name,
|
48
|
-
name_title_group, altrepgroup_id)
|
49
|
-
else
|
50
|
-
# TODO: want a way to notify that we hit a problem - either notifier or HB error (issue #3751)
|
51
|
-
# OR validate for semantic correctness upon creation/update so we can't get here.
|
52
|
-
# notifier.warn("For contributor name '#{parallel_contrib_name_val}', no title matching '#{title_from_contrib}'")
|
53
|
-
write_parallel_contributor(contributor, contrib_name, parallel_contrib_name, nil,
|
54
|
-
altrepgroup_id)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
41
|
+
parallel_name_values = contrib_name.parallelValue
|
42
|
+
if parallel_name_values.present?
|
43
|
+
write_contributor_with_parallel_names(parallel_name_values, contrib_name)
|
58
44
|
else
|
59
45
|
write_contributor(contributor)
|
60
46
|
end
|
@@ -105,7 +91,50 @@ module Cocina
|
|
105
91
|
end
|
106
92
|
end
|
107
93
|
|
108
|
-
def
|
94
|
+
def write_contributor_with_parallel_names(parallel_name_values, contrib_name)
|
95
|
+
display_type_parallel_name = display_type_parallel_name(parallel_name_values)
|
96
|
+
if parallel_name_values.size == 1
|
97
|
+
contrib_name_value_slice = Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_name_values.first)
|
98
|
+
name_title_group = name_title_vals_index[contrib_name_value_slice]&.values&.first
|
99
|
+
write_name_from_parallel(contributor, contributor.name.first, parallel_name_values, name_title_group, nil)
|
100
|
+
elsif parallel_name_values.size == 2 && display_type_parallel_name
|
101
|
+
contrib_name_value_slice = Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_name_values.first)
|
102
|
+
name_title_group = name_title_vals_index[contrib_name_value_slice]&.values&.first
|
103
|
+
write_name_with_display_form(contributor, contributor.name.first, parallel_name_values, 0, name_title_group, nil)
|
104
|
+
else
|
105
|
+
write_multiple_parallel_contributors(parallel_name_values, contrib_name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_multiple_parallel_contributors(parallel_name_values, contrib_name)
|
110
|
+
altrepgroup_id = id_generator.next_altrepgroup
|
111
|
+
parallel_name_values.each_with_index do |parallel_contrib_name, index|
|
112
|
+
display_name_present = parallel_name_values[index + 1].present? && parallel_name_values[index + 1].type == 'display'
|
113
|
+
Cocina::Models::Builders::NameTitleGroupBuilder.value_slices(parallel_contrib_name)&.each do |parallel_contrib_name_slice|
|
114
|
+
if name_title_vals_index[parallel_contrib_name_slice]
|
115
|
+
name_title_group = name_title_vals_index[parallel_contrib_name_slice]&.values&.first
|
116
|
+
if display_name_present
|
117
|
+
# associate type 'display' with the previous value
|
118
|
+
write_name_with_display_form(contributor, contrib_name, parallel_name_values, index, name_title_group, altrepgroup_id)
|
119
|
+
else
|
120
|
+
write_name_from_parallel(contributor, contrib_name, parallel_contrib_name, name_title_group, altrepgroup_id)
|
121
|
+
end
|
122
|
+
elsif display_name_present
|
123
|
+
# TODO: want a way to notify that we hit a problem - either notifier or HB error (issue #3751)
|
124
|
+
# OR validate for semantic correctness upon creation/update so we can't get here.
|
125
|
+
# notifier.warn("For contributor name '#{parallel_contrib_name_slice}', no contrib matching")
|
126
|
+
write_name_with_display_form(contributor, contrib_name, parallel_name_values, index, nil, altrepgroup_id)
|
127
|
+
elsif parallel_name_values[index].type == 'display'
|
128
|
+
# we assume we included this as part of a previous name
|
129
|
+
next
|
130
|
+
else
|
131
|
+
write_name_from_parallel(contributor, contrib_name, parallel_contrib_name, nil, altrepgroup_id)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_name_from_parallel(contributor, name, parallel_name, name_title_group, altrepgroup_id)
|
109
138
|
attributes = parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
110
139
|
type_attr = NAME_TYPE.fetch(contributor.type, name_title_group ? 'personal' : nil)
|
111
140
|
attributes[:type] = type_attr if type_attr
|
@@ -121,6 +150,37 @@ module Cocina
|
|
121
150
|
end
|
122
151
|
end
|
123
152
|
|
153
|
+
# rubocop:disable Metrics/ParameterLists
|
154
|
+
def write_name_with_display_form(contributor, name, parallel_name_values, index, name_title_group, altrepgroup_id)
|
155
|
+
display_type_parallel_name = display_type_parallel_name(parallel_name_values)
|
156
|
+
parallel_name = parallel_name_values[index]
|
157
|
+
return if parallel_name.blank?
|
158
|
+
|
159
|
+
attributes = if altrepgroup_id.present?
|
160
|
+
parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
161
|
+
else
|
162
|
+
name_attributes(contributor, name, nil)
|
163
|
+
end
|
164
|
+
type_attr = NAME_TYPE.fetch(contributor.type, name_title_group ? 'personal' : nil)
|
165
|
+
attributes[:type] = type_attr if type_attr
|
166
|
+
xml.name attributes do
|
167
|
+
if parallel_name.structuredValue.present?
|
168
|
+
write_structured(parallel_name)
|
169
|
+
else
|
170
|
+
write_basic(parallel_name)
|
171
|
+
end
|
172
|
+
write_display_form(display_type_parallel_name) if display_type_parallel_name.present?
|
173
|
+
write_identifier(contributor) if contributor.identifier.present?
|
174
|
+
write_note(contributor)
|
175
|
+
write_roles(contributor)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
# rubocop:enable Metrics/ParameterLists
|
179
|
+
|
180
|
+
def display_type_parallel_name(parallel_name_values)
|
181
|
+
parallel_name_values.detect { |parallel_value| parallel_value.type == 'display' }
|
182
|
+
end
|
183
|
+
|
124
184
|
def parallel_name_attributes(name, parallel_name, name_title_group, altrepgroup_id)
|
125
185
|
{
|
126
186
|
nameTitleGroup: name_title_group,
|
@@ -394,7 +394,7 @@ module Cocina
|
|
394
394
|
xml.name name_attrs do
|
395
395
|
write_name_part(subject_value)
|
396
396
|
write_display_form(display_values)
|
397
|
-
write_roles(
|
397
|
+
write_roles(subject_value.note)
|
398
398
|
write_other_notes(subject.note, 'description')
|
399
399
|
write_other_notes(subject.note, 'affiliation')
|
400
400
|
end
|
@@ -410,7 +410,7 @@ module Cocina
|
|
410
410
|
write_display_form(display_values)
|
411
411
|
write_roles(subject.note)
|
412
412
|
write_other_notes(subject.note, 'description')
|
413
|
-
write_other_notes(
|
413
|
+
write_other_notes(subject_value.note, 'affiliation')
|
414
414
|
end
|
415
415
|
write_genres(subject_value)
|
416
416
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# A request to create a DRO. This has the same general structure as a DRO but doesn't have externalIdentifier and doesn't require the access subschema. If no access subschema is provided, these values will be inherited from the AdminPolicy.
|
5
6
|
class RequestDRO < Struct
|
6
7
|
include Validatable
|
7
8
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module Cocina
|
4
4
|
module Models
|
5
|
+
# A sequence or ordering of resources within a Collection or Object.
|
5
6
|
class Sequence < Struct
|
6
7
|
attribute :members, Types::Strict::Array.of(Types::Strict::String).default([].freeze)
|
7
8
|
# The direction that a sequence of canvases should be displayed to the user
|
data/lib/cocina/models/source.rb
CHANGED
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'edtf'
|
4
|
+
require 'jsonpath'
|
5
|
+
require 'rss'
|
6
|
+
|
7
|
+
module Cocina
|
8
|
+
module Models
|
9
|
+
module Validators
|
10
|
+
# Validates that dates of known types are type-valid
|
11
|
+
class DateTimeValidator
|
12
|
+
VALIDATABLE_TYPES = %w[edtf iso8601 w3cdtf].freeze
|
13
|
+
|
14
|
+
def self.validate(_clazz, attributes)
|
15
|
+
new(attributes).validate
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(attributes)
|
19
|
+
@attributes = attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate
|
23
|
+
return unless meets_preconditions?
|
24
|
+
|
25
|
+
return if invalid_dates.empty?
|
26
|
+
|
27
|
+
raise ValidationError, "Invalid date(s) for #{druid}: #{invalid_dates}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :attributes
|
33
|
+
|
34
|
+
def meets_preconditions?
|
35
|
+
attributes.key?(:description)
|
36
|
+
end
|
37
|
+
|
38
|
+
def invalid_dates
|
39
|
+
@invalid_dates ||= validatable_dates.filter_map do |date_hash|
|
40
|
+
code = date_hash.dig('encoding', 'code')
|
41
|
+
bad_values = JsonPath.new('$..value').on(date_hash.to_json).reject do |value|
|
42
|
+
send("valid_#{code}?", value)
|
43
|
+
end
|
44
|
+
|
45
|
+
next if bad_values.empty?
|
46
|
+
|
47
|
+
[*bad_values, code]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def validatable_dates
|
52
|
+
# Why is the `uniq` needed below? Odd behavior when handling highly nested use cases:
|
53
|
+
#
|
54
|
+
# > JsonPath.new("$..date..[?(@.encoding.code =~ /#{VALIDATABLE_TYPES.join('|')}/)]").on(attributes[:description].to_json)
|
55
|
+
# > [
|
56
|
+
# {"structuredValue"=>[{"value"=>"1996", "type"=>"start"}, {"value"=>"1998", "type"=>"end"}], "encoding"=>{"code"=>"iso8601"}},
|
57
|
+
# {"structuredValue"=>[{"value"=>"1996", "type"=>"start"}, {"value"=>"1998", "type"=>"end"}], "encoding"=>{"code"=>"iso8601"}}
|
58
|
+
# ]
|
59
|
+
#
|
60
|
+
# Notice how the JSONPath expression returns the *same exact* structure twice despite only being present once in the data.
|
61
|
+
JsonPath
|
62
|
+
.new("$..date..[?(@.encoding.code =~ /#{VALIDATABLE_TYPES.join('|')}/)]")
|
63
|
+
.on(attributes[:description].to_json)
|
64
|
+
.uniq
|
65
|
+
end
|
66
|
+
|
67
|
+
def valid_edtf?(value)
|
68
|
+
Date.edtf!(value)
|
69
|
+
true
|
70
|
+
rescue StandardError
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_iso8601?(value)
|
75
|
+
DateTime.iso8601(value)
|
76
|
+
true
|
77
|
+
rescue StandardError
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def valid_w3cdtf?(value)
|
82
|
+
Time.w3cdtf(value)
|
83
|
+
true
|
84
|
+
rescue StandardError
|
85
|
+
# NOTE: the upstream W3CDTF implementation in the `rss` gem does not
|
86
|
+
# allow two patterns that should be valid per the specification:
|
87
|
+
#
|
88
|
+
# * YYYY
|
89
|
+
# * YYYY-MM
|
90
|
+
#
|
91
|
+
# So we catch the false positives from the upstream gem and allow
|
92
|
+
# these two patterns to validate
|
93
|
+
/\A\d{4}(-0[1-9]|1[0-2])?\Z/.match?(value)
|
94
|
+
end
|
95
|
+
|
96
|
+
def druid
|
97
|
+
@druid ||= attributes[:externalIdentifier]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/openapi.yml
CHANGED
@@ -779,6 +779,7 @@ components:
|
|
779
779
|
$ref: "#/components/schemas/DescriptiveValue"
|
780
780
|
DescriptiveParallelContributor:
|
781
781
|
description: Value model for multiple representations of information about the same contributor (e.g. in different languages).
|
782
|
+
deprecated: true
|
782
783
|
type: object
|
783
784
|
additionalProperties: false
|
784
785
|
properties:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocina-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.82.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: edtf
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: equivalent-xml
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: jsonpath
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: nokogiri
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,6 +156,20 @@ dependencies:
|
|
128
156
|
- - "<"
|
129
157
|
- !ruby/object:Gem::Version
|
130
158
|
version: '1.0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: rss
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
131
173
|
- !ruby/object:Gem::Dependency
|
132
174
|
name: super_diff
|
133
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -377,6 +419,7 @@ files:
|
|
377
419
|
- lib/cocina/models/location_based_access.rb
|
378
420
|
- lib/cocina/models/location_based_download_access.rb
|
379
421
|
- lib/cocina/models/mapping/error_notifier.rb
|
422
|
+
- lib/cocina/models/mapping/escape_html.rb
|
380
423
|
- lib/cocina/models/mapping/from_mods/access.rb
|
381
424
|
- lib/cocina/models/mapping/from_mods/admin_metadata.rb
|
382
425
|
- lib/cocina/models/mapping/from_mods/alt_rep_group.rb
|
@@ -461,6 +504,7 @@ files:
|
|
461
504
|
- lib/cocina/models/validators/associated_name_validator.rb
|
462
505
|
- lib/cocina/models/validators/catalog_links_validator.rb
|
463
506
|
- lib/cocina/models/validators/dark_validator.rb
|
507
|
+
- lib/cocina/models/validators/date_time_validator.rb
|
464
508
|
- lib/cocina/models/validators/description_types_validator.rb
|
465
509
|
- lib/cocina/models/validators/description_values_validator.rb
|
466
510
|
- lib/cocina/models/validators/open_api_validator.rb
|
@@ -492,7 +536,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
492
536
|
- !ruby/object:Gem::Version
|
493
537
|
version: '0'
|
494
538
|
requirements: []
|
495
|
-
rubygems_version: 3.3.
|
539
|
+
rubygems_version: 3.3.7
|
496
540
|
signing_key:
|
497
541
|
specification_version: 4
|
498
542
|
summary: Data models for the SDR
|