cocina-models 0.71.0 → 0.73.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,456 @@
1
+ # Description types
2
+
3
+ ## Access accesscontact types
4
+ _Path: access.accessContact_
5
+ * email: Email address for a contact person or institution concerning the resource.
6
+ * repository: Institution providing access to the resource.
7
+
8
+ ## Access digitallocation types
9
+ _Path: access.digitalLocation_
10
+ * discovery: Online location for the purpose of discovering the resource.
11
+
12
+ ## Access note types
13
+ _Path: access.note_
14
+ * access restriction: Restrictions on or conditions for gaining access to the resource.
15
+ * display label: Display label for the purl.
16
+ * license: License describing allowed uses of the resource.
17
+ * use and reproduction: Information related to allowed uses of the resource in other contexts.
18
+
19
+ ## Access physicallocation types
20
+ _Path: access.physicalLocation_
21
+ * discovery: Location where a user may find the resource.
22
+ * location: Physical location of the resource, or path to the resource on a hard drive or disk.
23
+ * repository: The institution holding the resource.
24
+ * series: Archival series of the resource.
25
+ * shelf locator: Identifier or shelfmark indicating the location of the resource.
26
+
27
+ ## Adminmetadata note types
28
+ _Path: adminMetadata.note_
29
+ * record information: General information about the metadata record.
30
+ * record origin: The source of the record, such as another record transformed to generate the current record.
31
+
32
+ # Contributor types
33
+ _Path: contributor_
34
+ * conference: An event focusing on a particular topic or discipline.
35
+ * event: A time-bound occurrence.
36
+ * family: A group of individuals related by blood or personal alliance.
37
+ * organization: An institution or other corporate or collective body.
38
+ * person: An individual identity.
39
+ * unspecified others: Designator for one or more additional contributors not named individually.
40
+
41
+ ## Contributor identifier types
42
+ _Path: contributor.identifier_
43
+ * ORCID: Identifier from orcid.org.
44
+ * Wikidata: Identifier from wikidata.org.
45
+
46
+ ## Contributor name types
47
+ _Path: contributor.name_
48
+ * alternative: Additional nonpreferred form of name.
49
+ * display: Preferred form of the name for display.
50
+ * forename: First or given name or names.
51
+ * inverted full name: Name given in last name, first name order.
52
+ * pseudonym: Name used that differs from legal or primary form of name.
53
+ * surname: Last or family name.
54
+ * transliteration: Name originally in non-Latin script presented phonetically using Latin characters.
55
+
56
+ ### Contributor name part types for structured value
57
+ _Path: contributor.name.structuredValue_
58
+ * activity dates: The date or dates when someone was producing work.
59
+ * forename: First or given name or names.
60
+ * life dates: Birth and death dates, or dates when an entity was in existence.
61
+ * name: Name provided alongside additional information.
62
+ * ordinal: Indicator that the name is one in a series (e.g. Elizabeth I, Martin Luther King, Jr.).
63
+ * surname: Last or family name.
64
+ * term of address: Title or other signifier associated with name.
65
+
66
+ ### Contributor name types for grouped value (MODS legacy)
67
+ _Path: contributor.name.groupedValue_
68
+ * alternative: Additional nonpreferred form of name.
69
+ * name: Primary form of name within group of values.
70
+ * pseudonym: Name used that differs from legal or primary form of name.
71
+
72
+ ## Contributor note types
73
+ _Path: contributor.note_
74
+ * affiliation: Institution with which the contributor is associated.
75
+ * citation status: Indicator of whether the contributor should be included in the citation.
76
+ * description: Biographical information about the contributor.
77
+
78
+ # Event types
79
+ _Path: event_
80
+ * acquisition: The transferral of ownership of a resource to a repository.
81
+ * capture: A record of the resource in a fixed form at a specific time.
82
+ * collection: The addition of a resource to a set of other resources.
83
+ * copyright: The activity by which a resource may be considered subject to copyright law.
84
+ * copyright notice: An explicit statement that a resource is under copyright.
85
+ * creation: The coming into being of a resource.
86
+ * degree conferral: The institutional approval of a thesis or other resource leading to an academic degree.
87
+ * development: The creation of a print from a photographic negative or other source medium.
88
+ * distribution: The delivery of the resource to an external audience.
89
+ * generation: The creation of a resource by an automatic or natural process.
90
+ * manufacture: The physical assembly of a resource, often in multiple copies, for publication or other distribution.
91
+ * modification: A change to an existing resource.
92
+ * performance: The enactment of an artistic or cultural work for an audience, such as a play.
93
+ * presentation: The discussion of an academic or intellectual work for an audience, such as a seminar.
94
+ * production: The physical assembly of a resource not considered published, such as page proofs for a book.
95
+ * publication: The publishing or issuing of a resource.
96
+ * recording: The initial fixation to a medium of live audio and/or visual activity.
97
+ * release: Making a resource available to a broader audience.
98
+ * submission: The provision of a resource for review or evaluation.
99
+ * validity: When a resource takes effect, such as a revised train schedule.
100
+ * withdrawal: The removal of previous access to a resource, often due to its obsolescence.
101
+
102
+ ## Event date types
103
+ _Path: event.date_
104
+ * acquisition: The transferral of ownership of a resource to a repository.
105
+ * capture: A record of the resource in a fixed form at a specific time.
106
+ * collection: The addition of a resource to a set of other resources.
107
+ * copyright: The activity by which a resource may be considered subject to copyright law.
108
+ * creation: The coming into being of a resource.
109
+ * degree conferral: The institutional approval of a thesis or other resource leading to an academic degree.
110
+ * developed: The creation of a print from a photographic negative or other source medium.
111
+ * development: The creation of a print from a photographic negative or other source medium.
112
+ * distribution: The delivery of the resource to an external audience.
113
+ * generation: The creation of a resource by an automatic or natural process.
114
+ * manufacture: The physical assembly of a resource, often in multiple copies, for publication or other distribution.
115
+ * modification: A change to an existing resource.
116
+ * performance: The enactment of an artistic or cultural work for an audience, such as a play.
117
+ * presentation: The discussion of an academic or intellectual work for an audience, such as a seminar.
118
+ * production: The physical assembly of a resource not considered published, such as page proofs for a book.
119
+ * publication: The publishing or issuing of a resource.
120
+ * recording: The initial fixation to a medium of live audio and/or visual activity.
121
+ * release: Making a resource available to a broader audience.
122
+ * submission: The provision of a resource for review or evaluation.
123
+ * validity: When a resource takes effect, such as a revised train schedule.
124
+ * withdrawal: The removal of previous access to a resource, often due to its obsolescence.
125
+
126
+ ### Event date part types for structured value
127
+ _Path: event.date.structuredValue_
128
+ * start: The start date in a range.
129
+ * end: The end date in a range.
130
+
131
+ ## Event note types
132
+ _Path: event.note_
133
+ * copyright statement: A formal declaration of copyright on a resource.
134
+ * edition
135
+ * frequency: How often a resource is issued, such as monthly.
136
+ * issuance: How the resource is issued, such as serially.
137
+
138
+ # Form types
139
+ _Path: form_
140
+ * carrier
141
+ * data format
142
+ * digital original
143
+ * extent
144
+ * form
145
+ * genre
146
+ * map projection
147
+ * map scale
148
+ * material
149
+ * media
150
+ * media type
151
+ * reformatting quality
152
+ * resource type
153
+ * technique
154
+ * type
155
+
156
+ ## Form note types
157
+ _Path: form.note_
158
+ * additions
159
+ * arrangement
160
+ * binding
161
+ * codicology
162
+ * collation
163
+ * colophon
164
+ * condition
165
+ * decoNote
166
+ * decoration
167
+ * dimensions
168
+ * explicit
169
+ * foliation
170
+ * genre type
171
+ * hand note
172
+ * handNote
173
+ * incipit
174
+ * instrumentation
175
+ * layout
176
+ * material
177
+ * medium of performance
178
+ * provenance
179
+ * reassembly
180
+ * reproduction
181
+ * research
182
+ * rubric
183
+ * secfol
184
+ * second folio
185
+ * secondFolio
186
+ * unit
187
+ * writing
188
+
189
+ ## Form part types for structured value
190
+ _Path: form.structuredValue_
191
+ * type
192
+ * subtype
193
+
194
+ ## Geographic form types
195
+ _Path: geographic.form_
196
+ * data format
197
+ * media type
198
+ * type
199
+
200
+ ## Geographic subject types
201
+ _Path: geographic.subject_
202
+ * bounding box coordinates
203
+ * coverage
204
+ * point coordinates
205
+
206
+ ### Geographic subject part types for structured value
207
+ _Path: geographic.subject.structuredValue_
208
+ * east
209
+ * latitude
210
+ * longitude
211
+ * north
212
+ * south
213
+ * west
214
+
215
+ # Identifier types
216
+ _Path: identifier_
217
+ * accession number
218
+ * alternate case number
219
+ * Apis ID
220
+ * ARK
221
+ * arXiv
222
+ * case identifier
223
+ * case number
224
+ * document number
225
+ * DOI
226
+ * druid
227
+ * GTIN-14 ID
228
+ * Handle
229
+ * inventory number
230
+ * ISBN
231
+ * ISMN
232
+ * ISRC
233
+ * ISSN
234
+ * ISSN-L
235
+ * issue number
236
+ * LCCN
237
+ * local
238
+ * Local ID
239
+ * matrix number
240
+ * music plate
241
+ * music publisher
242
+ * OCLC
243
+ * PMCID
244
+ * PMID
245
+ * record id
246
+ * Senate Number
247
+ * Series
248
+ * SIRSI
249
+ * Source ID
250
+ * sourceID
251
+ * stock number
252
+ * Swets (Netherlands) ID
253
+ * UPC
254
+ * URI
255
+ * URN
256
+ * videorecording identifier
257
+ * West Mat \#
258
+ * Wikidata
259
+
260
+ # Note types
261
+ _Path: note_
262
+ * abstract
263
+ * access
264
+ * access note
265
+ * acquisition
266
+ * action
267
+ * additional physical form
268
+ * additions
269
+ * admin
270
+ * affiliation
271
+ * bibliographic
272
+ * bibliography
273
+ * biographical/historical
274
+ * biographical/historical note
275
+ * biography
276
+ * boat note
277
+ * citation/reference
278
+ * contact
279
+ * content
280
+ * content note
281
+ * content warning
282
+ * contents
283
+ * copyright
284
+ * creation/production credits
285
+ * date
286
+ * date/sequential designation
287
+ * description
288
+ * digitization
289
+ * duration
290
+ * event
291
+ * exhibitions
292
+ * funding
293
+ * general
294
+ * genre type
295
+ * geography
296
+ * host
297
+ * language
298
+ * local
299
+ * location
300
+ * medium of performance
301
+ * names
302
+ * numbering
303
+ * original location
304
+ * other relation type
305
+ * ownership
306
+ * part
307
+ * performer
308
+ * performers
309
+ * preferred citation
310
+ * provenance
311
+ * publications
312
+ * qualifications
313
+ * quote
314
+ * reassembly
315
+ * reference
316
+ * references
317
+ * related publication
318
+ * reproduction
319
+ * research
320
+ * restriction
321
+ * scope and content
322
+ * source characteristics
323
+ * source identifier
324
+ * statement of responsibility
325
+ * summary
326
+ * system details
327
+ * system requirements
328
+ * table of contents
329
+ * target audience
330
+ * technical note
331
+ * thesis
332
+ * transcript
333
+ * translation
334
+ * update
335
+ * use and reproduction
336
+ * venue
337
+ * version
338
+ * version identification
339
+ * writing
340
+
341
+ ## Note types for grouped value (MODS legacy)
342
+ _Path: note.groupedValue_
343
+ * caption
344
+ * date
345
+ * detail type
346
+ * extent unit
347
+ * list
348
+ * marker
349
+ * number
350
+ * title
351
+ * text
352
+
353
+ # Relatedresource types
354
+ _Path: relatedResource_
355
+ * has original version: An initial form of the resource.
356
+ * has other format: A version of the resource in a different physical or digital format.
357
+ * has part: A constituent unit of the resource.
358
+ * has version: A version of the resource with different intellectual content.
359
+ * in series: The name of a series of publications to which the resource belongs.
360
+ * other relation type: Resource type not otherwise described.
361
+ * part of: A larger resource to which the resource belongs, such as a collection.
362
+ * preceded by: A predecessor to the resource, such as a preceding journal title.
363
+ * referenced by: Other resources that cite the resource, such as a catalog.
364
+ * references: A resource which the resource references or cites.
365
+ * related to: A generically related resource.
366
+ * reviewed by: A review of the resource.
367
+ * succeeded by: A successor to the resource, such as a subsequent journal title.
368
+
369
+ # Subject types
370
+ _Path: subject_
371
+ * classification
372
+ * conference
373
+ * display
374
+ * event
375
+ * family
376
+ * genre
377
+ * map coordinates
378
+ * name
379
+ * occupation
380
+ * organization
381
+ * person
382
+ * place
383
+ * point coordinates
384
+ * time
385
+ * title
386
+ * topic
387
+
388
+ ## Subject note types
389
+ _Path: subject.note_
390
+ * role
391
+
392
+ ## Subject part types for structured value
393
+ _Path: subject.structuredValue_
394
+ * activity dates
395
+ * city
396
+ * conference
397
+ * continent
398
+ * country
399
+ * end
400
+ * east
401
+ * event
402
+ * display
403
+ * family
404
+ * forename
405
+ * genre
406
+ * latitude
407
+ * life dates
408
+ * longitude
409
+ * main title
410
+ * name
411
+ * north
412
+ * occupation
413
+ * ordinal
414
+ * organization
415
+ * part name
416
+ * person
417
+ * place
418
+ * south
419
+ * start
420
+ * surname
421
+ * term of address
422
+ * time
423
+ * title
424
+ * topic
425
+ * west
426
+
427
+ ### Subject note types
428
+ _Path: subject.structuredValue.note_
429
+ * role: The relation of the subject entity to the resource.
430
+
431
+ ## Subject types for grouped value (MODS legacy)
432
+ _Path: subject.groupedValue_
433
+ * uniform: Form of title in Library of Congress title authority.
434
+
435
+ # Title types
436
+ _Path: title_
437
+ * abbreviated: Abbreviated form of title for indexing or identification.
438
+ * alternative: Variant title.
439
+ * parallel: Title transcribed from the resource in multiple languages or scripts.
440
+ * supplied: Title provided by metadata creator rather than transcribed from the resource.
441
+ * translated: Title translated into another language.
442
+ * transliterated: Title transliterated from non-Latin script to Latin script.
443
+ * uniform: Form of title in Library of Congress title authority.
444
+
445
+ ## Title note types
446
+ _Path: title.note_
447
+ * associated name: A name linked to the title, such as for a name-title heading.
448
+ * nonsorting character count: The number of characters at the beginning of the string to be disregarded when sorting.
449
+
450
+ ## Title part types for structured value
451
+ _Path: title.structuredValue_
452
+ * main title: The primary part of a multipart title.
453
+ * nonsorting characters: A string at the beginning of the title to be disregarded when sorting.
454
+ * part name: The distinct name of a resource as part of a series or multivolume set.
455
+ * part number: The distinct number of a resource as part of a series or multivolume set.
456
+ * subtitle: The secondary part of a title.
@@ -28,6 +28,7 @@ module Cocina
28
28
  # rubocop:enable Style/HashEachMethods
29
29
 
30
30
  generate_vocab
31
+ generate_descriptive_docs
31
32
  end
32
33
 
33
34
  desc 'generate_schema SCHEMA_NAME', 'generate for SCHEMA_NAME'
@@ -45,8 +46,56 @@ module Cocina
45
46
  Vocab.generate(schemas, output_dir: options[:output])
46
47
  end
47
48
 
49
+ desc 'generate_descriptive_docs', 'generate descriptive documentation'
50
+ def generate_descriptive_docs
51
+ markdown = YAML.load_file('description_types.yml').map do |field, types|
52
+ header_markdown = field_markdown_from(field)
53
+ types_markdown = types_markdown_from(types)
54
+
55
+ <<~MARKDOWN
56
+ #{'#' * (field.count('.') + 1)} #{header_markdown}
57
+ _Path: #{field}_
58
+ #{types_markdown}
59
+ MARKDOWN
60
+ end.join("\n")
61
+
62
+ remove_file 'docs/description_types.md'
63
+ create_file 'docs/description_types.md', h1_markdown + markdown
64
+ end
65
+
48
66
  private
49
67
 
68
+ def h1_markdown
69
+ <<~MARKDOWN
70
+ # Description types
71
+
72
+ MARKDOWN
73
+ end
74
+
75
+ def field_markdown_from(field)
76
+ header = field.split('.')
77
+ .grep_v(/groupedValue|structuredValue/)
78
+ .join(' ')
79
+ .capitalize
80
+
81
+ header_suffix = if field.ends_with?('structuredValue')
82
+ 'part types for structured value'
83
+ elsif field.ends_with?('groupedValue')
84
+ 'types for grouped value (MODS legacy)'
85
+ else
86
+ 'types'
87
+ end
88
+ "#{header} #{header_suffix}"
89
+ end
90
+
91
+ def types_markdown_from(types)
92
+ types.map do |type|
93
+ " * #{type['value']}".tap do |type_value|
94
+ type_value << ": #{type['description']}" if type['description']
95
+ end
96
+ end.join("\n")
97
+ end
98
+
50
99
  def schemas
51
100
  @schemas ||= Openapi3Parser.load_file(options[:openapi]).components.schemas
52
101
  end
@@ -78,7 +127,6 @@ module Cocina
78
127
  'rights_description_builder.rb',
79
128
  'title_builder.rb',
80
129
  'validatable.rb',
81
- 'validator.rb',
82
130
  'version.rb',
83
131
  'vocabulary.rb'
84
132
  ].freeze
@@ -4,7 +4,7 @@ module Cocina
4
4
  module Models
5
5
  class AccessRole < Struct
6
6
  # Name of role
7
- attribute :name, Types::Strict::String.enum('dor-apo-creator', 'dor-apo-depositor', 'dor-apo-manager', 'dor-apo-metadata', 'dor-apo-reviewer', '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')
7
+ 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')
8
8
  attribute :members, Types::Strict::Array.of(AccessRoleMember).default([].freeze)
9
9
  end
10
10
  end
@@ -5,7 +5,9 @@ module Cocina
5
5
  class CatalogLink < Struct
6
6
  # Catalog that is the source of the linked record.
7
7
  # example: symphony
8
- attribute :catalog, Types::Strict::String
8
+ attribute :catalog, Types::Strict::String.enum('symphony', 'previous symphony')
9
+ # Only one of the catkeys should be designated for refreshing. This means that this key is the one used to pull metadata from the catalog if there is more than one key present.
10
+ attribute :refresh, Types::Strict::Bool.default(false)
9
11
  # Record identifier that is unique within the context of the linked record's catalog.
10
12
  # example: 11403803
11
13
  attribute :catalogRecordId, Types::Strict::String
@@ -46,8 +46,7 @@ module Cocina
46
46
  result = if cocina_title.value
47
47
  cocina_title.value
48
48
  elsif cocina_title.structuredValue.present?
49
- title_from_structured_values(cocina_title.structuredValue,
50
- non_sorting_char_count(cocina_title))
49
+ title_from_structured_values(cocina_title)
51
50
  elsif cocina_title.parallelValue.present?
52
51
  return build(cocina_title.parallelValue)
53
52
  end
@@ -104,20 +103,17 @@ module Cocina
104
103
  # rubocop:disable Metrics/PerceivedComplexity
105
104
  # rubocop:disable Metrics/MethodLength
106
105
  # rubocop:disable Metrics/AbcSize
107
- # @param [Array<Cocina::Models::StructuredValue>] structured_values - the pieces of a structuredValue
108
- # @param [Integer] the length of the non_sorting_characters
106
+ # @param [Cocina::Models::Title] title with structured values
109
107
  # @return [String] the title value from combining the pieces of the structured_values by type and order
110
108
  # with desired punctuation per specs
111
- def title_from_structured_values(structured_values, non_sorting_char_count)
109
+ def title_from_structured_values(title)
112
110
  structured_title = ''
113
111
  part_name_number = ''
114
112
  # combine pieces of the cocina structuredValue into a single title
115
- structured_values.each do |structured_value|
113
+ title.structuredValue.each do |structured_value|
116
114
  # There can be a structuredValue inside a structuredValue. For example,
117
115
  # a uniform title where both the name and the title have internal StructuredValue
118
- if structured_value.structuredValue.present?
119
- return title_from_structured_values(structured_value.structuredValue, non_sorting_char_count)
120
- end
116
+ return title_from_structured_values(structured_value) if structured_value.structuredValue.present?
121
117
 
122
118
  value = structured_value.value&.strip
123
119
  next unless value
@@ -125,8 +121,7 @@ module Cocina
125
121
  # additional types: name, uniform ...
126
122
  case structured_value.type&.downcase
127
123
  when 'nonsorting characters'
128
- non_sorting_size = [non_sorting_char_count - (value&.size || 0), 0].max
129
- non_sort_value = "#{value}#{' ' * non_sorting_size}"
124
+ non_sort_value = "#{value}#{non_sorting_padding(title, value)}"
130
125
  structured_title = if structured_title.present?
131
126
  "#{structured_title}#{non_sort_value}"
132
127
  else
@@ -134,7 +129,7 @@ module Cocina
134
129
  end
135
130
  when 'part name', 'part number'
136
131
  if part_name_number.blank?
137
- part_name_number = part_name_number(structured_values)
132
+ part_name_number = part_name_number(title.structuredValue)
138
133
  structured_title = if !add_punctuation?
139
134
  [structured_title, part_name_number].join(' ')
140
135
  elsif structured_title.present?
@@ -168,11 +163,16 @@ module Cocina
168
163
  title.sub(%r{[ .,;:/\\]+$}, '')
169
164
  end
170
165
 
171
- def non_sorting_char_count(title)
166
+ def non_sorting_padding(title, non_sorting_value)
172
167
  non_sort_note = title.note&.find { |note| note.type&.downcase == 'nonsorting character count' }
173
- return 0 unless non_sort_note
174
-
175
- non_sort_note.value.to_i
168
+ if non_sort_note
169
+ padding_count = [non_sort_note.value.to_i - non_sorting_value.length, 0].max
170
+ ' ' * padding_count
171
+ elsif ['\'', '-'].include?(non_sorting_value.last)
172
+ ''
173
+ else
174
+ ' '
175
+ end
176
176
  end
177
177
 
178
178
  # combine part name and part number:
@@ -8,7 +8,7 @@ module Cocina
8
8
 
9
9
  class_methods do
10
10
  def new(attributes = default_attributes, safe = false, validate = true, &block)
11
- Validator.validate(self, attributes.with_indifferent_access) if validate && name
11
+ Validators::Validator.validate(self, attributes.with_indifferent_access) if validate
12
12
  super(attributes, safe, &block)
13
13
  end
14
14
  end
@@ -16,7 +16,7 @@ module Cocina
16
16
  def new(*args)
17
17
  validate = args.first.delete(:validate) if args.present?
18
18
  new_model = super(*args)
19
- Validator.validate(new_model.class, new_model.to_h) if (validate || validate.nil?) && self.class.name
19
+ Validators::Validator.validate(new_model.class, new_model.to_h) if validate || validate.nil?
20
20
  new_model
21
21
  end
22
22
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Validators
6
+ # Validates that only a single CatalogLink has refresh set to true
7
+ class CatalogLinksValidator
8
+ MAX_REFRESH_CATALOG_LINKS = 1
9
+
10
+ def self.validate(clazz, attributes)
11
+ new(clazz, attributes).validate
12
+ end
13
+
14
+ def initialize(clazz, attributes)
15
+ @clazz = clazz
16
+ @attributes = attributes
17
+ end
18
+
19
+ def validate
20
+ return unless meets_preconditions?
21
+
22
+ return if refresh_catalog_links.length <= MAX_REFRESH_CATALOG_LINKS
23
+
24
+ raise ValidationError, "Multiple catalog links have 'refresh' property set to true " \
25
+ "(only one allowed) #{refresh_catalog_links}"
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :clazz, :attributes
31
+
32
+ def meets_preconditions?
33
+ (dro? || collection?) && Array(attributes.dig(:identification, :catalogLinks)).any?
34
+ end
35
+
36
+ def refresh_catalog_links
37
+ attributes.dig(:identification, :catalogLinks).select { |catalog_link| catalog_link[:refresh] }
38
+ end
39
+
40
+ def dro?
41
+ (clazz::TYPES & DRO::TYPES).any?
42
+ rescue NameError
43
+ false
44
+ end
45
+
46
+ def collection?
47
+ (clazz::TYPES & Collection::TYPES).any?
48
+ rescue NameError
49
+ false
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end