Class: Cocina::Models::Validators::DescriptionDateTimeVisitorValidator
- Inherits:
-
BaseDescriptionVisitorValidator
- Object
- BaseDescriptionVisitorValidator
- Cocina::Models::Validators::DescriptionDateTimeVisitorValidator
- Defined in:
- lib/cocina/models/validators/description_date_time_visitor_validator.rb
Overview
Validates that dates of known types are type-valid using the visitor pattern.
Constant Summary collapse
- VALIDATABLE_TYPES =
%w[edtf iso8601 w3cdtf].freeze
- VALID_ENCODING_CODES =
%w[edtf iso8601 marc temper w3cdtf].freeze
Instance Method Summary collapse
- #validate! ⇒ Object
-
#visit_hash(hash:, path:) ⇒ Object
rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity.
Methods inherited from BaseDescriptionVisitorValidator
#path_to_s, #visit_array, #visit_obj
Instance Method Details
#validate! ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/cocina/models/validators/description_date_time_visitor_validator.rb', line 68 def validate! errors = [] errors << "Unrecognized date encoding codes in description: #{invalid_encoding_codes.join(', ')}" if invalid_encoding_codes.any? unless invalid_groups.empty? invalid_dates = invalid_groups.filter_map do |path, values| [*values, encoding_paths[path]] unless values.empty? end errors << "Invalid date(s) in description: #{invalid_dates}" if invalid_dates.any? end raise ValidationError, errors.join('; ') if errors.any? end |
#visit_hash(hash:, path:) ⇒ Object
rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/cocina/models/validators/description_date_time_visitor_validator.rb', line 13 def visit_hash(hash:, path:) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity # Only dates nested under a `date` key are subject to validation. # For example, event.date is in scope but event.note is not. return unless in_date_path?(path) # A hash with a validatable encoding.code "owns" the encoding for its # entire subtree. For example, the outer hash below owns iso8601 for # both structuredValue children even though those children carry no # encoding themselves: # # date: [{ # structuredValue: [ # { value: '1996', type: 'start' }, # { value: '1998', type: 'end' } # ], # encoding: { code: 'iso8601' } # ← registered at path [:date, 0] # }] # # We record the path before visiting children because # CompositeDescriptionValidator calls visit_hash on a parent before # recursing into its children, so the encoding is always registered # before any child value hashes are visited. code = hash.dig(:encoding, :code) if code encoding_paths[path.dup] = code if VALIDATABLE_TYPES.include?(code) invalid_encoding_codes << "#{path_to_s(path)}.encoding.code (#{code})" unless VALID_ENCODING_CODES.include?(code) end value = hash[:value] return unless value.is_a?(String) # Resolve which encoding governs this value by finding the longest # registered encoding path that is a prefix of the current path. # Longest-prefix wins so that a more-specific inner encoding overrides # a less-specific outer one. For example, given: # # date: [{ # parallelValue: [ # { value: '1996', encoding: { code: 'edtf' } }, # path [:date,0,:parallelValue,0] # { value: '一九九六' } # path [:date,0,:parallelValue,1] # ], # encoding: { code: 'iso8601' } # path [:date,0] # }] # # The value '1996' at [:date,0,:parallelValue,0] matches both [:date,0] # (iso8601) and [:date,0,:parallelValue,0] (edtf); the longer prefix wins # and it is validated as edtf. The value '一九九六' at # [:date,0,:parallelValue,1] only matches [:date,0] (iso8601). encoding_path, code = find_encoding_for(path) return unless code invalid_groups[encoding_path] ||= [] invalid_groups[encoding_path] << value unless valid_value?(value, code) end |