cocina-models 0.124.0 → 0.126.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/.circleci/config.yml +1 -1
- data/Gemfile.lock +38 -40
- data/bin/validate-data +1 -2
- data/iso15924_codes.yml +275 -0
- data/language_uri_iso639_2_codes.yml +509 -0
- data/language_uri_languages_codes.yml +487 -0
- data/lib/cocina/models/admin_policy.rb +1 -1
- data/lib/cocina/models/admin_policy_lite.rb +1 -1
- data/lib/cocina/models/admin_policy_with_metadata.rb +1 -1
- data/lib/cocina/models/collection.rb +1 -1
- data/lib/cocina/models/collection_lite.rb +1 -1
- data/lib/cocina/models/collection_with_metadata.rb +1 -1
- data/lib/cocina/models/contributor.rb +1 -1
- data/lib/cocina/models/description.rb +1 -1
- data/lib/cocina/models/descriptive_admin_metadata.rb +1 -1
- data/lib/cocina/models/descriptive_basic_value.rb +1 -1
- data/lib/cocina/models/descriptive_identifier.rb +45 -0
- data/lib/cocina/models/descriptive_parallel_event.rb +1 -1
- data/lib/cocina/models/descriptive_value.rb +1 -1
- data/lib/cocina/models/dro.rb +1 -1
- data/lib/cocina/models/dro_lite.rb +1 -1
- data/lib/cocina/models/dro_with_metadata.rb +1 -1
- data/lib/cocina/models/event.rb +1 -1
- data/lib/cocina/models/mapping/from_mods/access.rb +2 -2
- data/lib/cocina/models/related_resource.rb +1 -1
- data/lib/cocina/models/request_description.rb +1 -1
- data/lib/cocina/models/title.rb +1 -1
- data/lib/cocina/models/validators/composite_description_validator.rb +7 -1
- data/lib/cocina/models/validators/description_language_code_visitor_validator.rb +48 -0
- data/lib/cocina/models/validators/description_language_uri_visitor_validator.rb +86 -0
- data/lib/cocina/models/validators/description_name_source_code_visitor_validator.rb +45 -0
- data/lib/cocina/models/validators/description_script_visitor_validator.rb +74 -0
- data/lib/cocina/models/validators/description_subject_source_code_visitor_validator.rb +41 -0
- data/lib/cocina/models/validators/description_title_source_code_visitor_validator.rb +43 -0
- data/lib/cocina/models/version.rb +1 -1
- data/name_source_codes.yml +498 -0
- data/name_title_source_codes.yml +46 -0
- data/schema.json +109 -55
- data/subject_source_codes.yml +848 -0
- metadata +15 -2
|
@@ -28,7 +28,7 @@ module Cocina
|
|
|
28
28
|
# Additional information relevant to a related resource.
|
|
29
29
|
attribute :note, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
|
30
30
|
# Identifiers and URIs associated with the related resource.
|
|
31
|
-
attribute :identifier, Types::Strict::Array.of(
|
|
31
|
+
attribute :identifier, Types::Strict::Array.of(DescriptiveIdentifier).default([].freeze)
|
|
32
32
|
# Property model for indicating the encoding, standard, or syntax to which a value
|
|
33
33
|
# conforms (e.g. RDA).
|
|
34
34
|
attribute? :standard, Standard.optional
|
|
@@ -23,7 +23,7 @@ module Cocina
|
|
|
23
23
|
# Additional information relevant to a resource.
|
|
24
24
|
attribute :note, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
|
25
25
|
# Identifiers and URIs associated with the resource.
|
|
26
|
-
attribute :identifier, Types::Strict::Array.of(
|
|
26
|
+
attribute :identifier, Types::Strict::Array.of(DescriptiveIdentifier).default([].freeze)
|
|
27
27
|
# Terms associated with the intellectual content of the resource.
|
|
28
28
|
attribute :subject, Types::Strict::Array.of(DescriptiveValue).default([].freeze)
|
|
29
29
|
# Information about how to access digital and physical versions of the object.
|
data/lib/cocina/models/title.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Cocina
|
|
|
25
25
|
# conforms (e.g. RDA).
|
|
26
26
|
attribute? :encoding, Standard.optional
|
|
27
27
|
# Identifiers and URIs associated with the descriptive element.
|
|
28
|
-
attribute :identifier, Types::Strict::Array.of(
|
|
28
|
+
attribute :identifier, Types::Strict::Array.of(DescriptiveIdentifier).default([].freeze)
|
|
29
29
|
# Property model for indicating the vocabulary, authority, or other origin for a term,
|
|
30
30
|
# code, or identifier.
|
|
31
31
|
attribute? :source, Source.optional
|
|
@@ -9,12 +9,18 @@ module Cocina
|
|
|
9
9
|
DescriptionTypesVisitorValidator,
|
|
10
10
|
DescriptionIdentifierSourceCodeVisitorValidator,
|
|
11
11
|
DescriptionRoleSourceCodeVisitorValidator,
|
|
12
|
+
DescriptionNameSourceCodeVisitorValidator,
|
|
12
13
|
DescriptionFormResourceTypeVisitorValidator,
|
|
14
|
+
DescriptionLanguageCodeVisitorValidator,
|
|
15
|
+
DescriptionSubjectSourceCodeVisitorValidator,
|
|
13
16
|
DescriptionValuesVisitorValidator,
|
|
14
17
|
DescriptionDateTimeVisitorValidator,
|
|
15
18
|
DescriptionEventDateVisitorValidator,
|
|
16
19
|
DescriptionSubjectTemporalEncodingVisitorValidator,
|
|
17
|
-
DescriptionLocationSourceCodeVisitorValidator
|
|
20
|
+
DescriptionLocationSourceCodeVisitorValidator,
|
|
21
|
+
DescriptionTitleSourceCodeVisitorValidator,
|
|
22
|
+
DescriptionLanguageUriVisitorValidator,
|
|
23
|
+
DescriptionScriptVisitorValidator
|
|
18
24
|
].freeze
|
|
19
25
|
|
|
20
26
|
def self.validate(clazz, attributes)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cocina_display'
|
|
4
|
+
|
|
5
|
+
module Cocina
|
|
6
|
+
module Models
|
|
7
|
+
module Validators
|
|
8
|
+
# Validates language.code values against searchworks_languages from cocina_display gem plus mul, und, zxx.
|
|
9
|
+
class DescriptionLanguageCodeVisitorValidator < BaseDescriptionVisitorValidator
|
|
10
|
+
EXTRA_VALID_CODES = %w[mul und zxx].freeze
|
|
11
|
+
|
|
12
|
+
def validate!
|
|
13
|
+
return if error_paths.empty?
|
|
14
|
+
|
|
15
|
+
raise ValidationError, "Unrecognized language codes in description: #{error_paths.join(', ')}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def visit_hash(hash:, path:)
|
|
19
|
+
return unless language_path?(path)
|
|
20
|
+
|
|
21
|
+
code = hash[:code]
|
|
22
|
+
return unless code
|
|
23
|
+
|
|
24
|
+
error_paths << "#{path_to_s(path)}.code (#{code})" unless valid_codes.include?(code)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def error_paths
|
|
30
|
+
@error_paths ||= []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def language_path?(path)
|
|
34
|
+
path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'language'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# rubocop:disable Style/ClassVars
|
|
38
|
+
def valid_codes
|
|
39
|
+
@@valid_codes ||= begin
|
|
40
|
+
gem_codes = CocinaDisplay::Languages::Language.searchworks_languages.keys.to_set
|
|
41
|
+
gem_codes.merge(EXTRA_VALID_CODES)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
# rubocop:enable Style/ClassVars
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cocina
|
|
4
|
+
module Models
|
|
5
|
+
module Validators
|
|
6
|
+
# Validates language.uri and valueLanguage.uri against LC ISO 639-2, LC MARC languages,
|
|
7
|
+
# and 5 approved ISO 639-3 URIs. Both http and https are acceptable.
|
|
8
|
+
class DescriptionLanguageUriVisitorValidator < BaseDescriptionVisitorValidator
|
|
9
|
+
ISO639_3_CODES = %w[ase dnt quc skr trw tta].freeze
|
|
10
|
+
|
|
11
|
+
def visit_hash(hash:, path:)
|
|
12
|
+
return unless language_or_value_language_path?(path)
|
|
13
|
+
|
|
14
|
+
uri = hash[:uri]
|
|
15
|
+
return unless uri
|
|
16
|
+
|
|
17
|
+
error_paths << "#{path_to_s(path)}.uri (#{uri})" unless valid_uri?(uri)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def validate!
|
|
21
|
+
return if error_paths.empty?
|
|
22
|
+
|
|
23
|
+
raise ValidationError, "Invalid language URIs in description: #{error_paths.join(', ')}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def error_paths
|
|
29
|
+
@error_paths ||= []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def language_or_value_language_path?(path)
|
|
33
|
+
language_path?(path) || value_language_path?(path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def language_path?(path)
|
|
37
|
+
path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'language'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def value_language_path?(path)
|
|
41
|
+
path.length >= 1 && path[-1].to_s == 'valueLanguage'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def valid_uri?(uri)
|
|
45
|
+
normalized = uri.sub(%r{\Ahttps?://}, '')
|
|
46
|
+
valid_iso639_2_uri?(normalized) || valid_iso639_3_uri?(normalized) || valid_languages_uri?(normalized)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def valid_iso639_2_uri?(normalized)
|
|
50
|
+
match = normalized.match(%r{\Aid\.loc\.gov/vocabulary/iso639-2/(.+)\z})
|
|
51
|
+
return false unless match
|
|
52
|
+
|
|
53
|
+
valid_iso639_2_codes.include?(match[1].downcase)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def valid_iso639_3_uri?(normalized)
|
|
57
|
+
match = normalized.match(%r{\Aid\.loc\.gov/vocabulary/iso639-3/(.+)\z})
|
|
58
|
+
return false unless match
|
|
59
|
+
|
|
60
|
+
ISO639_3_CODES.include?(match[1].downcase)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def valid_languages_uri?(normalized)
|
|
64
|
+
match = normalized.match(%r{\Aid\.loc\.gov/vocabulary/languages/(.+)\z})
|
|
65
|
+
return false unless match
|
|
66
|
+
|
|
67
|
+
valid_languages_codes.include?(match[1].downcase)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# rubocop:disable Style/ClassVars
|
|
71
|
+
def valid_iso639_2_codes
|
|
72
|
+
@@valid_iso639_2_codes ||= YAML.load_file(
|
|
73
|
+
::File.expand_path('../../../../language_uri_iso639_2_codes.yml', __dir__)
|
|
74
|
+
).to_set(&:downcase)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def valid_languages_codes
|
|
78
|
+
@@valid_languages_codes ||= YAML.load_file(
|
|
79
|
+
::File.expand_path('../../../../language_uri_languages_codes.yml', __dir__)
|
|
80
|
+
).to_set(&:downcase)
|
|
81
|
+
end
|
|
82
|
+
# rubocop:enable Style/ClassVars
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cocina
|
|
4
|
+
module Models
|
|
5
|
+
module Validators
|
|
6
|
+
# Validates contributor.name.source.code values against name_source_codes.yml.
|
|
7
|
+
class DescriptionNameSourceCodeVisitorValidator < BaseDescriptionVisitorValidator
|
|
8
|
+
def validate!
|
|
9
|
+
return if error_paths.empty?
|
|
10
|
+
|
|
11
|
+
raise ValidationError, "Unrecognized name source codes in description: #{error_paths.join(', ')}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def visit_hash(hash:, path:)
|
|
15
|
+
return unless name_path?(path)
|
|
16
|
+
|
|
17
|
+
source_code = hash.dig(:source, :code)
|
|
18
|
+
return unless source_code
|
|
19
|
+
return if valid_codes.include?(source_code.downcase)
|
|
20
|
+
|
|
21
|
+
error_paths << "#{path_to_s(path)}.source.code (#{source_code})"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def error_paths
|
|
27
|
+
@error_paths ||= []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def name_path?(path)
|
|
31
|
+
path.length >= 2 &&
|
|
32
|
+
path[-1].is_a?(Integer) &&
|
|
33
|
+
path[-2].to_s == 'name' &&
|
|
34
|
+
path.any? { |part| part.to_s == 'contributor' }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# rubocop:disable Style/ClassVars
|
|
38
|
+
def valid_codes
|
|
39
|
+
@@valid_codes ||= YAML.load_file(::File.expand_path('../../../../name_source_codes.yml', __dir__)).to_set(&:downcase)
|
|
40
|
+
end
|
|
41
|
+
# rubocop:enable Style/ClassVars
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cocina
|
|
4
|
+
module Models
|
|
5
|
+
module Validators
|
|
6
|
+
# Validates language.script and valueLanguage.valueScript:
|
|
7
|
+
# - source.code, when present, must be 'iso15924' (case-insensitive)
|
|
8
|
+
# - code must be a recognized ISO 15924 script code (when source.code is 'iso15924')
|
|
9
|
+
class DescriptionScriptVisitorValidator < BaseDescriptionVisitorValidator
|
|
10
|
+
def validate!
|
|
11
|
+
raise ValidationError, "Unrecognized script source codes in description: #{source_error_paths.join(', ')}" if source_error_paths.any?
|
|
12
|
+
raise ValidationError, "Unrecognized script codes in description: #{error_paths.join(', ')}" if error_paths.any?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def visit_hash(hash:, path:)
|
|
16
|
+
if language_path?(path)
|
|
17
|
+
validate_script(hash, path, :script)
|
|
18
|
+
elsif value_language_path?(path)
|
|
19
|
+
validate_script(hash, path, :valueScript)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def error_paths
|
|
26
|
+
@error_paths ||= []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def source_error_paths
|
|
30
|
+
@source_error_paths ||= []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Matches entries in a language array (e.g., [:language, 0] or [:adminMetadata, :language, 0]).
|
|
34
|
+
# Since language is an array of objects, the last element is the integer index
|
|
35
|
+
# and the second-to-last is the key 'language'.
|
|
36
|
+
def language_path?(path)
|
|
37
|
+
path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'language'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Matches single valueLanguage objects (e.g., [:title, 0, :valueLanguage]).
|
|
41
|
+
# Since valueLanguage is a single object rather than an array, the path ends
|
|
42
|
+
# directly with 'valueLanguage'.
|
|
43
|
+
def value_language_path?(path)
|
|
44
|
+
path.present? && path[-1].to_s == 'valueLanguage'
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def validate_script(hash, path, key)
|
|
48
|
+
script = hash[key]
|
|
49
|
+
return unless script
|
|
50
|
+
|
|
51
|
+
source_code = script.dig(:source, :code)
|
|
52
|
+
return unless source_code
|
|
53
|
+
|
|
54
|
+
unless source_code.downcase == 'iso15924'
|
|
55
|
+
source_error_paths << "#{path_to_s(path)}.#{key}.source.code (#{source_code})"
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
code = script[:code]
|
|
60
|
+
return unless code
|
|
61
|
+
return if valid_script_codes.include?(code.downcase)
|
|
62
|
+
|
|
63
|
+
error_paths << "#{path_to_s(path)}.#{key}.code (#{code})"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# rubocop:disable Style/ClassVars
|
|
67
|
+
def valid_script_codes
|
|
68
|
+
@@valid_script_codes ||= YAML.load_file(::File.expand_path('../../../../iso15924_codes.yml', __dir__)).to_set
|
|
69
|
+
end
|
|
70
|
+
# rubocop:enable Style/ClassVars
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cocina
|
|
4
|
+
module Models
|
|
5
|
+
module Validators
|
|
6
|
+
# Validates subject.source.code values against subject_source_codes.yml.
|
|
7
|
+
class DescriptionSubjectSourceCodeVisitorValidator < BaseDescriptionVisitorValidator
|
|
8
|
+
def validate!
|
|
9
|
+
return if error_paths.empty?
|
|
10
|
+
|
|
11
|
+
raise ValidationError, "Unrecognized subject source codes in description: #{error_paths.join(', ')}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def visit_hash(hash:, path:)
|
|
15
|
+
return unless subject_path?(path)
|
|
16
|
+
|
|
17
|
+
source_code = hash.dig(:source, :code)
|
|
18
|
+
return unless source_code
|
|
19
|
+
|
|
20
|
+
error_paths << "#{path_to_s(path)}.source.code (#{source_code})" unless valid_codes.include?(source_code.downcase)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def error_paths
|
|
26
|
+
@error_paths ||= []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def subject_path?(path)
|
|
30
|
+
path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'subject'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# rubocop:disable Style/ClassVars
|
|
34
|
+
def valid_codes
|
|
35
|
+
@@valid_codes ||= YAML.load_file(::File.expand_path('../../../../subject_source_codes.yml', __dir__)).to_set(&:downcase)
|
|
36
|
+
end
|
|
37
|
+
# rubocop:enable Style/ClassVars
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cocina
|
|
4
|
+
module Models
|
|
5
|
+
module Validators
|
|
6
|
+
# Validates title.source.code values against name_title_source_codes.yml.
|
|
7
|
+
class DescriptionTitleSourceCodeVisitorValidator < BaseDescriptionVisitorValidator
|
|
8
|
+
def validate!
|
|
9
|
+
return if error_paths.empty?
|
|
10
|
+
|
|
11
|
+
raise ValidationError, "Unrecognized title source codes in description: #{error_paths.join(', ')}"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def visit_hash(hash:, path:)
|
|
15
|
+
return unless title_path?(path)
|
|
16
|
+
|
|
17
|
+
source_code = hash.dig(:source, :code)
|
|
18
|
+
return unless source_code
|
|
19
|
+
return if valid_codes.include?(source_code.downcase)
|
|
20
|
+
|
|
21
|
+
error_paths << "#{path_to_s(path)}.source.code (#{source_code})"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def error_paths
|
|
27
|
+
@error_paths ||= []
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def title_path?(path)
|
|
31
|
+
# Match entries in a title array (e.g., [:title, 0] or [:relatedResource, 0, :title, 0]).
|
|
32
|
+
path.length >= 2 && path[-1].is_a?(Integer) && path[-2].to_s == 'title'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# rubocop:disable Style/ClassVars
|
|
36
|
+
def valid_codes
|
|
37
|
+
@@valid_codes ||= YAML.load_file(::File.expand_path('../../../../name_title_source_codes.yml', __dir__)).to_set(&:downcase)
|
|
38
|
+
end
|
|
39
|
+
# rubocop:enable Style/ClassVars
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|