cocina-models 0.67.0 → 0.69.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/README.md +19 -0
  4. data/cocina-models.gemspec +4 -1
  5. data/lib/cocina/generator/generator.rb +15 -12
  6. data/lib/cocina/generator/vocab.rb +46 -42
  7. data/lib/cocina/models/access.rb +3 -3
  8. data/lib/cocina/models/admin_policy.rb +1 -1
  9. data/lib/cocina/models/{admin_policy_default_access.rb → admin_policy_access_template.rb} +4 -4
  10. data/lib/cocina/models/admin_policy_administrative.rb +1 -1
  11. data/lib/cocina/models/administrative.rb +0 -3
  12. data/lib/cocina/models/citation_only_access.rb +3 -3
  13. data/lib/cocina/models/collection.rb +5 -5
  14. data/lib/cocina/models/collection_access.rb +1 -1
  15. data/lib/cocina/models/controlled_digital_lending_access.rb +3 -3
  16. data/lib/cocina/models/dark_access.rb +3 -3
  17. data/lib/cocina/models/dro.rb +15 -15
  18. data/lib/cocina/models/dro_access.rb +4 -4
  19. data/lib/cocina/models/dro_rights_description_builder.rb +67 -0
  20. data/lib/cocina/models/embargo.rb +3 -3
  21. data/lib/cocina/models/file.rb +1 -1
  22. data/lib/cocina/models/file_access.rb +3 -3
  23. data/lib/cocina/models/file_set.rb +16 -16
  24. data/lib/cocina/models/file_set_type.rb +25 -0
  25. data/lib/cocina/models/license.rb +10 -0
  26. data/lib/cocina/models/location_based_access.rb +3 -3
  27. data/lib/cocina/models/location_based_download_access.rb +3 -3
  28. data/lib/cocina/models/object_type.rb +31 -0
  29. data/lib/cocina/models/request_admin_policy.rb +1 -1
  30. data/lib/cocina/models/request_administrative.rb +14 -0
  31. data/lib/cocina/models/request_collection.rb +6 -6
  32. data/lib/cocina/models/request_dro.rb +16 -16
  33. data/lib/cocina/models/request_file.rb +1 -1
  34. data/lib/cocina/models/request_file_set.rb +16 -16
  35. data/lib/cocina/models/rights_description_builder.rb +81 -0
  36. data/lib/cocina/models/stanford_access.rb +3 -3
  37. data/lib/cocina/models/title_builder.rb +203 -0
  38. data/lib/cocina/models/version.rb +1 -1
  39. data/lib/cocina/models/vocabulary.rb +30 -0
  40. data/lib/cocina/models/world_access.rb +3 -3
  41. data/lib/cocina/models.rb +26 -7
  42. data/lib/cocina/rspec/matchers.rb +103 -0
  43. data/lib/cocina/rspec.rb +14 -0
  44. data/openapi.yml +132 -124
  45. metadata +39 -10
  46. data/lib/cocina/models/vocab.rb +0 -162
@@ -5,22 +5,22 @@ module Cocina
5
5
  class FileSet < Struct
6
6
  include Checkable
7
7
 
8
- TYPES = ['http://cocina.sul.stanford.edu/models/resources/audio.jsonld',
9
- 'http://cocina.sul.stanford.edu/models/resources/attachment.jsonld',
10
- 'http://cocina.sul.stanford.edu/models/resources/document.jsonld',
11
- 'http://cocina.sul.stanford.edu/models/resources/file.jsonld',
12
- 'http://cocina.sul.stanford.edu/models/resources/image.jsonld',
13
- 'http://cocina.sul.stanford.edu/models/resources/main-augmented.jsonld',
14
- 'http://cocina.sul.stanford.edu/models/resources/main-original.jsonld',
15
- 'http://cocina.sul.stanford.edu/models/resources/media.jsonld',
16
- 'http://cocina.sul.stanford.edu/models/resources/object.jsonld',
17
- 'http://cocina.sul.stanford.edu/models/resources/page.jsonld',
18
- 'http://cocina.sul.stanford.edu/models/resources/permissions.jsonld',
19
- 'http://cocina.sul.stanford.edu/models/resources/preview.jsonld',
20
- 'http://cocina.sul.stanford.edu/models/resources/supplement.jsonld',
21
- 'http://cocina.sul.stanford.edu/models/resources/3d.jsonld',
22
- 'http://cocina.sul.stanford.edu/models/resources/thumb.jsonld',
23
- 'http://cocina.sul.stanford.edu/models/resources/video.jsonld'].freeze
8
+ TYPES = ['https://cocina.sul.stanford.edu/models/resources/audio',
9
+ 'https://cocina.sul.stanford.edu/models/resources/attachment',
10
+ 'https://cocina.sul.stanford.edu/models/resources/document',
11
+ 'https://cocina.sul.stanford.edu/models/resources/file',
12
+ 'https://cocina.sul.stanford.edu/models/resources/image',
13
+ 'https://cocina.sul.stanford.edu/models/resources/main-augmented',
14
+ 'https://cocina.sul.stanford.edu/models/resources/main-original',
15
+ 'https://cocina.sul.stanford.edu/models/resources/media',
16
+ 'https://cocina.sul.stanford.edu/models/resources/object',
17
+ 'https://cocina.sul.stanford.edu/models/resources/page',
18
+ 'https://cocina.sul.stanford.edu/models/resources/permissions',
19
+ 'https://cocina.sul.stanford.edu/models/resources/preview',
20
+ 'https://cocina.sul.stanford.edu/models/resources/supplement',
21
+ 'https://cocina.sul.stanford.edu/models/resources/3d',
22
+ 'https://cocina.sul.stanford.edu/models/resources/thumb',
23
+ 'https://cocina.sul.stanford.edu/models/resources/video'].freeze
24
24
 
25
25
  # The content type of the Fileset.
26
26
  attribute :type, Types::Strict::String.enum(*FileSet::TYPES)
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ # This vocabulary defines the types of file sets
6
+ class FileSetType < Vocabulary('https://cocina.sul.stanford.edu/models/resources/')
7
+ property :'3d', method_name: :three_dimensional
8
+ property :attachment
9
+ property :audio
10
+ property :document
11
+ property :file
12
+ property :image
13
+ property :'main-augmented'
14
+ property :'main-original'
15
+ property :media
16
+ property :object
17
+ property :page
18
+ property :permissions
19
+ property :preview
20
+ property :supplement
21
+ property :thumb
22
+ property :video
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ # This vocabulary defines some of the supported licenses
6
+ class License < Vocabulary('https://cocina.sul.stanford.edu/licenses/')
7
+ property :none
8
+ end
9
+ end
10
+ end
@@ -4,11 +4,11 @@ module Cocina
4
4
  module Models
5
5
  class LocationBasedAccess < Struct
6
6
  # Access level.
7
- attribute :access, Types::Strict::String.enum('location-based')
7
+ attribute :view, Types::Strict::String.enum('location-based')
8
8
  # Download access level.
9
9
  attribute :download, Types::Strict::String.enum('location-based', 'none')
10
- # If access is "location-based", which location should have access.
11
- attribute :readLocation, Types::Strict::String.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
10
+ # If access or download is "location-based", which location should have access.
11
+ attribute :location, Types::Strict::String.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
12
12
  attribute :controlledDigitalLending, Types::Strict::Bool.enum(false).meta(omittable: true)
13
13
  end
14
14
  end
@@ -4,11 +4,11 @@ module Cocina
4
4
  module Models
5
5
  class LocationBasedDownloadAccess < Struct
6
6
  # Access level.
7
- attribute :access, Types::Strict::String.enum('stanford', 'world')
7
+ attribute :view, Types::Strict::String.enum('stanford', 'world')
8
8
  # Download access level.
9
9
  attribute :download, Types::Strict::String.enum('location-based')
10
- # If access is "location-based", which location should have access.
11
- attribute :readLocation, Types::Strict::String.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
10
+ # Which location should have download access.
11
+ attribute :location, Types::Strict::String.enum('spec', 'music', 'ars', 'art', 'hoover', 'm&m')
12
12
  attribute :controlledDigitalLending, Types::Strict::Bool.enum(false).meta(omittable: true)
13
13
  end
14
14
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ # This vocabulary defines the top level object type
6
+ class ObjectType < Vocabulary('https://cocina.sul.stanford.edu/models/')
7
+ property :'3d', method_name: :three_dimensional
8
+ property :admin_policy
9
+ property :agreement
10
+ property :book
11
+ property :collection
12
+ property :'curated-collection'
13
+ property :document
14
+ property :exhibit
15
+ property :file
16
+ property :geo
17
+ property :image
18
+ property :manuscript
19
+ property :map
20
+ property :media
21
+ property :object
22
+ property :page
23
+ property :photograph
24
+ property :series
25
+ property :track
26
+ property :'user-collection'
27
+ property :'webarchive-binary'
28
+ property :'webarchive-seed'
29
+ end
30
+ end
31
+ end
@@ -7,7 +7,7 @@ module Cocina
7
7
 
8
8
  include Checkable
9
9
 
10
- TYPES = ['http://cocina.sul.stanford.edu/models/admin_policy.jsonld'].freeze
10
+ TYPES = ['https://cocina.sul.stanford.edu/models/admin_policy'].freeze
11
11
 
12
12
  # The version of Cocina with which this object conforms.
13
13
  # example: 1.2.3
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ class RequestAdministrative < Struct
6
+ # example: druid:bc123df4567
7
+ attribute :hasAdminPolicy, Types::Strict::String
8
+ attribute :releaseTags, Types::Strict::Array.of(ReleaseTag).default([].freeze)
9
+ # Internal project this resource is a part of. This governs routing of messages about this object.
10
+ # example: Google Books
11
+ attribute :partOfProject, Types::Strict::String.meta(omittable: true)
12
+ end
13
+ end
14
+ end
@@ -7,11 +7,11 @@ module Cocina
7
7
 
8
8
  include Checkable
9
9
 
10
- TYPES = ['http://cocina.sul.stanford.edu/models/collection.jsonld',
11
- 'http://cocina.sul.stanford.edu/models/curated-collection.jsonld',
12
- 'http://cocina.sul.stanford.edu/models/user-collection.jsonld',
13
- 'http://cocina.sul.stanford.edu/models/exhibit.jsonld',
14
- 'http://cocina.sul.stanford.edu/models/series.jsonld'].freeze
10
+ TYPES = ['https://cocina.sul.stanford.edu/models/collection',
11
+ 'https://cocina.sul.stanford.edu/models/curated-collection',
12
+ 'https://cocina.sul.stanford.edu/models/user-collection',
13
+ 'https://cocina.sul.stanford.edu/models/exhibit',
14
+ 'https://cocina.sul.stanford.edu/models/series'].freeze
15
15
 
16
16
  # The version of Cocina with which this object conforms.
17
17
  # example: 1.2.3
@@ -20,7 +20,7 @@ module Cocina
20
20
  attribute :label, Types::Strict::String
21
21
  attribute :version, Types::Strict::Integer.default(1).enum(1)
22
22
  attribute(:access, CollectionAccess.default { CollectionAccess.new })
23
- attribute(:administrative, Administrative.default { Administrative.new })
23
+ attribute(:administrative, RequestAdministrative.default { RequestAdministrative.new })
24
24
  attribute :description, RequestDescription.optional.meta(omittable: true)
25
25
  attribute :identification, CollectionIdentification.optional.meta(omittable: true)
26
26
  end
@@ -7,21 +7,21 @@ module Cocina
7
7
 
8
8
  include Checkable
9
9
 
10
- TYPES = ['http://cocina.sul.stanford.edu/models/object.jsonld',
11
- 'http://cocina.sul.stanford.edu/models/3d.jsonld',
12
- 'http://cocina.sul.stanford.edu/models/agreement.jsonld',
13
- 'http://cocina.sul.stanford.edu/models/book.jsonld',
14
- 'http://cocina.sul.stanford.edu/models/document.jsonld',
15
- 'http://cocina.sul.stanford.edu/models/geo.jsonld',
16
- 'http://cocina.sul.stanford.edu/models/image.jsonld',
17
- 'http://cocina.sul.stanford.edu/models/page.jsonld',
18
- 'http://cocina.sul.stanford.edu/models/photograph.jsonld',
19
- 'http://cocina.sul.stanford.edu/models/manuscript.jsonld',
20
- 'http://cocina.sul.stanford.edu/models/map.jsonld',
21
- 'http://cocina.sul.stanford.edu/models/media.jsonld',
22
- 'http://cocina.sul.stanford.edu/models/track.jsonld',
23
- 'http://cocina.sul.stanford.edu/models/webarchive-binary.jsonld',
24
- 'http://cocina.sul.stanford.edu/models/webarchive-seed.jsonld'].freeze
10
+ TYPES = ['https://cocina.sul.stanford.edu/models/object',
11
+ 'https://cocina.sul.stanford.edu/models/3d',
12
+ 'https://cocina.sul.stanford.edu/models/agreement',
13
+ 'https://cocina.sul.stanford.edu/models/book',
14
+ 'https://cocina.sul.stanford.edu/models/document',
15
+ 'https://cocina.sul.stanford.edu/models/geo',
16
+ 'https://cocina.sul.stanford.edu/models/image',
17
+ 'https://cocina.sul.stanford.edu/models/page',
18
+ 'https://cocina.sul.stanford.edu/models/photograph',
19
+ 'https://cocina.sul.stanford.edu/models/manuscript',
20
+ 'https://cocina.sul.stanford.edu/models/map',
21
+ 'https://cocina.sul.stanford.edu/models/media',
22
+ 'https://cocina.sul.stanford.edu/models/track',
23
+ 'https://cocina.sul.stanford.edu/models/webarchive-binary',
24
+ 'https://cocina.sul.stanford.edu/models/webarchive-seed'].freeze
25
25
 
26
26
  # The version of Cocina with which this object conforms.
27
27
  # example: 1.2.3
@@ -30,7 +30,7 @@ module Cocina
30
30
  attribute :label, Types::Strict::String
31
31
  attribute :version, Types::Strict::Integer.default(1).enum(1)
32
32
  attribute :access, DROAccess.optional.meta(omittable: true)
33
- attribute(:administrative, Administrative.default { Administrative.new })
33
+ attribute(:administrative, RequestAdministrative.default { RequestAdministrative.new })
34
34
  attribute :description, RequestDescription.optional.meta(omittable: true)
35
35
  attribute(:identification, RequestIdentification.default { RequestIdentification.new })
36
36
  attribute :structural, RequestDROStructural.optional.meta(omittable: true)
@@ -5,7 +5,7 @@ module Cocina
5
5
  class RequestFile < Struct
6
6
  include Checkable
7
7
 
8
- TYPES = ['http://cocina.sul.stanford.edu/models/file.jsonld'].freeze
8
+ TYPES = ['https://cocina.sul.stanford.edu/models/file'].freeze
9
9
 
10
10
  attribute :type, Types::Strict::String.enum(*RequestFile::TYPES)
11
11
  attribute :label, Types::Strict::String
@@ -5,22 +5,22 @@ module Cocina
5
5
  class RequestFileSet < Struct
6
6
  include Checkable
7
7
 
8
- TYPES = ['http://cocina.sul.stanford.edu/models/resources/audio.jsonld',
9
- 'http://cocina.sul.stanford.edu/models/resources/attachment.jsonld',
10
- 'http://cocina.sul.stanford.edu/models/resources/document.jsonld',
11
- 'http://cocina.sul.stanford.edu/models/resources/file.jsonld',
12
- 'http://cocina.sul.stanford.edu/models/resources/image.jsonld',
13
- 'http://cocina.sul.stanford.edu/models/resources/main-augmented.jsonld',
14
- 'http://cocina.sul.stanford.edu/models/resources/main-original.jsonld',
15
- 'http://cocina.sul.stanford.edu/models/resources/media.jsonld',
16
- 'http://cocina.sul.stanford.edu/models/resources/object.jsonld',
17
- 'http://cocina.sul.stanford.edu/models/resources/page.jsonld',
18
- 'http://cocina.sul.stanford.edu/models/resources/permissions.jsonld',
19
- 'http://cocina.sul.stanford.edu/models/resources/preview.jsonld',
20
- 'http://cocina.sul.stanford.edu/models/resources/supplement.jsonld',
21
- 'http://cocina.sul.stanford.edu/models/resources/3d.jsonld',
22
- 'http://cocina.sul.stanford.edu/models/resources/thumb.jsonld',
23
- 'http://cocina.sul.stanford.edu/models/resources/video.jsonld'].freeze
8
+ TYPES = ['https://cocina.sul.stanford.edu/models/resources/audio',
9
+ 'https://cocina.sul.stanford.edu/models/resources/attachment',
10
+ 'https://cocina.sul.stanford.edu/models/resources/document',
11
+ 'https://cocina.sul.stanford.edu/models/resources/file',
12
+ 'https://cocina.sul.stanford.edu/models/resources/image',
13
+ 'https://cocina.sul.stanford.edu/models/resources/main-augmented',
14
+ 'https://cocina.sul.stanford.edu/models/resources/main-original',
15
+ 'https://cocina.sul.stanford.edu/models/resources/media',
16
+ 'https://cocina.sul.stanford.edu/models/resources/object',
17
+ 'https://cocina.sul.stanford.edu/models/resources/page',
18
+ 'https://cocina.sul.stanford.edu/models/resources/permissions',
19
+ 'https://cocina.sul.stanford.edu/models/resources/preview',
20
+ 'https://cocina.sul.stanford.edu/models/resources/supplement',
21
+ 'https://cocina.sul.stanford.edu/models/resources/3d',
22
+ 'https://cocina.sul.stanford.edu/models/resources/thumb',
23
+ 'https://cocina.sul.stanford.edu/models/resources/video'].freeze
24
24
 
25
25
  attribute :type, Types::Strict::String.enum(*RequestFileSet::TYPES)
26
26
  attribute :label, Types::Strict::String
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ # RightsDescriptionBuilder
6
+ class RightsDescriptionBuilder
7
+ # @param [Cocina::Models::AdminPolicy, Cocina::Models::DRO] cocina_object
8
+ def self.build(cocina_object)
9
+ new(cocina_object).build
10
+ end
11
+
12
+ def initialize(cocina_object)
13
+ @cocina = cocina_object
14
+ end
15
+
16
+ # This is set up to work for APOs, but this method is to be overridden on sub classes
17
+ # @return [Cocina::Models::AdminPolicyDefaultAccess]
18
+ def object_access
19
+ @object_access ||= cocina.administrative.accessTemplate
20
+ end
21
+
22
+ def build
23
+ return 'controlled digital lending' if object_access.controlledDigitalLending
24
+
25
+ return ['dark'] if object_access.view == 'dark'
26
+
27
+ object_level_access
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :cocina
33
+
34
+ # rubocop:disable Metrics/MethodLength
35
+ def object_level_access
36
+ case object_access.view
37
+ when 'citation-only'
38
+ ['citation']
39
+ when 'world'
40
+ world_object_access
41
+ when 'location-based'
42
+ case object_access.download
43
+ when 'none'
44
+ ["location: #{object_access.location} (no-download)"]
45
+ else
46
+ ["location: #{object_access.location}"]
47
+ end
48
+ when 'stanford'
49
+ stanford_object_access
50
+ end
51
+ end
52
+ # rubocop:enable Metrics/MethodLength
53
+
54
+ def stanford_object_access
55
+ case object_access.download
56
+ when 'none'
57
+ ['stanford (no-download)']
58
+ when 'location-based'
59
+ # this is an odd case we might want to move away from. See https://github.com/sul-dlss/cocina-models/issues/258
60
+ ['stanford (no-download)', "location: #{object_access.location}"]
61
+ else
62
+ ['stanford']
63
+ end
64
+ end
65
+
66
+ def world_object_access
67
+ case object_access.download
68
+ when 'stanford'
69
+ ['stanford', 'world (no-download)']
70
+ when 'none'
71
+ ['world (no-download)']
72
+ when 'world'
73
+ ['world']
74
+ when 'location-based'
75
+ # this is an odd case we might want to move away from. See https://github.com/sul-dlss/cocina-models/issues/258
76
+ ['world (no-download)', "location: #{object_access.location}"]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -4,11 +4,11 @@ module Cocina
4
4
  module Models
5
5
  class StanfordAccess < Struct
6
6
  # Access level.
7
- attribute :access, Types::Strict::String.enum('stanford')
7
+ attribute :view, Types::Strict::String.enum('stanford')
8
8
  # Download access level.
9
9
  attribute :download, Types::Strict::String.enum('stanford')
10
- # If access is "location-based", which location should have access.
11
- attribute :readLocation, Types::Strict::String.optional.enum('').meta(omittable: true)
10
+ # Not used for this access type, must be null.
11
+ attribute :location, Types::Strict::String.optional.enum('').meta(omittable: true)
12
12
  attribute :controlledDigitalLending, Types::Strict::Bool.enum(false).meta(omittable: true)
13
13
  end
14
14
  end
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ # TitleBuilder selects the prefered title from the cocina object for solr indexing
6
+ # rubocop:disable Metrics/ClassLength
7
+ class TitleBuilder
8
+ # @param [Cocina::Models::D*] cocina_object is the object to extract the title for
9
+ # @param [Symbol] strategy ":first" is the strategy for selection when primary or display title are missing
10
+ # @param [Boolean] add_punctuation determines if the title should be formmated with punctuation
11
+ # @return [String] the title value for Solr
12
+ def self.build(cocina_object, strategy: :first, add_punctuation: true)
13
+ new(cocina_object, strategy: strategy, add_punctuation: add_punctuation).build_title
14
+ end
15
+
16
+ def initialize(cocina_object, strategy:, add_punctuation:)
17
+ @cocina_object = cocina_object
18
+ @strategy = strategy
19
+ @add_punctuation = add_punctuation
20
+ end
21
+
22
+ # @return [String] the title value for Solr
23
+ def build_title
24
+ @titles = cocina_object.description.title
25
+
26
+ cocina_title = primary_title || untyped_title
27
+ cocina_title = other_title if cocina_title.blank?
28
+
29
+ if strategy == :first
30
+ extract_title(cocina_title)
31
+ else
32
+ cocina_title.map { |one| extract_title(one) }
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :cocina_object, :strategy, :titles
39
+
40
+ def extract_title(cocina_title)
41
+ result = if cocina_title.value
42
+ cocina_title.value
43
+ elsif cocina_title.structuredValue.present?
44
+ title_from_structured_values(cocina_title.structuredValue,
45
+ non_sorting_char_count(cocina_title))
46
+ elsif cocina_title.parallelValue.present?
47
+ return cocina_title.parallelValue.first.value
48
+ end
49
+ remove_trailing_punctuation(result.strip) if result.present?
50
+ end
51
+
52
+ def add_punctuation?
53
+ @add_punctuation
54
+ end
55
+
56
+ # @return [Cocina::Models::Title, nil] title that has status=primary
57
+ def primary_title
58
+ primary_title = titles.find do |title|
59
+ title.status == 'primary'
60
+ end
61
+ return primary_title if primary_title.present?
62
+
63
+ # NOTE: structuredValues would only have status primary assigned as a sibling, not as an attribute
64
+
65
+ titles.find do |title|
66
+ title.parallelValue&.find do |parallel_title|
67
+ parallel_title.status == 'primary'
68
+ end
69
+ end
70
+ end
71
+
72
+ def untyped_title
73
+ method = strategy == :first ? :find : :select
74
+ untyped_title_for(titles.public_send(method))
75
+ end
76
+
77
+ # @return [Array[Cocina::Models::Title]] first title that has no type attribute
78
+ def untyped_title_for(titles)
79
+ titles.each do |title|
80
+ if title.parallelValue.present?
81
+ untyped_title_for(title.parallelValue)
82
+ else
83
+ title.type.nil? || title.type == 'title'
84
+ end
85
+ end
86
+ end
87
+
88
+ # This handles 'main title', 'uniform' or 'translated'
89
+ def other_title
90
+ if strategy == :first
91
+ titles.first
92
+ else
93
+ titles
94
+ end
95
+ end
96
+
97
+ # rubocop:disable Metrics/BlockLength
98
+ # rubocop:disable Metrics/CyclomaticComplexity
99
+ # rubocop:disable Metrics/PerceivedComplexity
100
+ # rubocop:disable Metrics/MethodLength
101
+ # rubocop:disable Metrics/AbcSize
102
+ # @param [Array<Cocina::Models::StructuredValue>] structured_values - the pieces of a structuredValue
103
+ # @param [Integer] the length of the non_sorting_characters
104
+ # @return [String] the title value from combining the pieces of the structured_values by type and order
105
+ # with desired punctuation per specs
106
+ def title_from_structured_values(structured_values, non_sorting_char_count)
107
+ structured_title = ''
108
+ part_name_number = ''
109
+ # combine pieces of the cocina structuredValue into a single title
110
+ structured_values.each do |structured_value|
111
+ # There can be a structuredValue inside a structuredValue. For example,
112
+ # a uniform title where both the name and the title have internal StructuredValue
113
+ if structured_value.structuredValue.present?
114
+ return title_from_structured_values(structured_value.structuredValue, non_sorting_char_count)
115
+ end
116
+
117
+ value = structured_value.value&.strip
118
+ next unless value
119
+
120
+ # additional types: name, uniform ...
121
+ case structured_value.type&.downcase
122
+ when 'nonsorting characters'
123
+ non_sorting_size = [non_sorting_char_count - (value&.size || 0), 0].max
124
+ non_sort_value = "#{value}#{' ' * non_sorting_size}"
125
+ structured_title = if structured_title.present?
126
+ "#{structured_title}#{non_sort_value}"
127
+ else
128
+ non_sort_value
129
+ end
130
+ when 'part name', 'part number'
131
+ if part_name_number.blank?
132
+ part_name_number = part_name_number(structured_values)
133
+ structured_title = if !add_punctuation?
134
+ [structured_title, part_name_number].join(' ')
135
+ elsif structured_title.present?
136
+ "#{structured_title.sub(/[ .,]*$/, '')}. #{part_name_number}. "
137
+ else
138
+ "#{part_name_number}. "
139
+ end
140
+ end
141
+ when 'main title', 'title'
142
+ structured_title = "#{structured_title}#{value}"
143
+ when 'subtitle'
144
+ # subtitle is preceded by space colon space, unless it is at the beginning of the title string
145
+ structured_title = if !add_punctuation?
146
+ [structured_title, value].join(' ')
147
+ elsif structured_title.present?
148
+ "#{structured_title.sub(/[. :]+$/, '')} : #{value.sub(/^:/, '').strip}"
149
+ else
150
+ structured_title = value.sub(/^:/, '').strip
151
+ end
152
+ end
153
+ end
154
+ structured_title
155
+ end
156
+ # rubocop:enable Metrics/AbcSize
157
+ # rubocop:enable Metrics/MethodLength
158
+ # rubocop:enable Metrics/BlockLength
159
+ # rubocop:enable Metrics/CyclomaticComplexity
160
+ # rubocop:enable Metrics/PerceivedComplexity
161
+
162
+ def remove_trailing_punctuation(title)
163
+ title.sub(%r{[ .,;:/\\]+$}, '')
164
+ end
165
+
166
+ def non_sorting_char_count(title)
167
+ non_sort_note = title.note&.find { |note| note.type&.downcase == 'nonsorting character count' }
168
+ return 0 unless non_sort_note
169
+
170
+ non_sort_note.value.to_i
171
+ end
172
+
173
+ # combine part name and part number:
174
+ # respect order of occurrence
175
+ # separated from each other by comma space
176
+ def part_name_number(structured_values)
177
+ title_from_part = ''
178
+ structured_values.each do |structured_value|
179
+ case structured_value.type&.downcase
180
+ when 'part name', 'part number'
181
+ value = structured_value.value&.strip
182
+ next unless value
183
+
184
+ title_from_part = append_part_to_title(title_from_part, value)
185
+
186
+ end
187
+ end
188
+ title_from_part
189
+ end
190
+
191
+ def append_part_to_title(title_from_part, value)
192
+ if !add_punctuation?
193
+ [title_from_part, value].select(&:presence).join(' ')
194
+ elsif title_from_part.strip.present?
195
+ "#{title_from_part.sub(/[ .,]*$/, '')}, #{value}"
196
+ else
197
+ value
198
+ end
199
+ end
200
+ end
201
+ # rubocop:enable Metrics/ClassLength
202
+ end
203
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cocina
4
4
  module Models
5
- VERSION = '0.67.0'
5
+ VERSION = '0.69.0'
6
6
  end
7
7
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ class Vocabulary
6
+ # @private
7
+ # Disabled this cop because we want @@uri to be inheritable.
8
+ # rubocop:disable Style/ClassVars
9
+ def self.create(uri)
10
+ @@uri = uri
11
+ self
12
+ end
13
+ # rubocop:enable Style/ClassVars
14
+
15
+ def self.to_s
16
+ @@uri
17
+ end
18
+
19
+ def self.property(name, method_name: name.to_s.underscore.to_sym)
20
+ uri = [to_s, name].join
21
+ properties[name] = uri
22
+ (class << self; self; end).send(:define_method, method_name) { uri }
23
+ end
24
+
25
+ def self.properties
26
+ @properties ||= {}
27
+ end
28
+ end
29
+ end
30
+ end
@@ -4,11 +4,11 @@ module Cocina
4
4
  module Models
5
5
  class WorldAccess < Struct
6
6
  # Access level.
7
- attribute :access, Types::Strict::String.enum('world')
7
+ attribute :view, Types::Strict::String.enum('world')
8
8
  # Download access level.
9
9
  attribute :download, Types::Strict::String.enum('none', 'stanford', 'world')
10
- # If access is "location-based", which location should have access.
11
- attribute :readLocation, Types::Strict::String.optional.enum('').meta(omittable: true)
10
+ # Not used for this access type, must be null.
11
+ attribute :location, Types::Strict::String.optional.enum('').meta(omittable: true)
12
12
  attribute :controlledDigitalLending, Types::Strict::Bool.enum(false).meta(omittable: true)
13
13
  end
14
14
  end