Completed
Push — master ( 308a2c...1aebdd )
by Fike
01:00
created

Validator.illegal_value?   A

Complexity

Conditions 4

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.2
cc 4
1
# frozen_string_literal: true
2
3
require_relative '../../mixin/reflection'
4
require_relative '../../mixin/errors'
5
6
module AMA
7
  module Entity
8
    class Mapper
9
      module Handler
10
        module Attribute
11
          # Default validator for single attribute
12
          class Validator
13
            INSTANCE = new
14
15
            # @param [Object] value Attribute value
16
            # @param [AMA::Entity::Mapper::Type::Attribute] attribute
17
            # @param [AMA::Entity::Mapper::Context] _context
18
            # @return [Array<String>] Single violation, list of violations
19
            def validate(value, attribute, _context)
20
              violation = validate_internal(value, attribute)
21
              violation.nil? ? [] : [violation]
22
            end
23
24
            private
25
26
            def validate_internal(value, attribute)
27
              if illegal_nil?(value, attribute)
28
                return "Attribute #{attribute} could not be nil"
29
              end
30
              if invalid_type?(value, attribute)
31
                return "Provided value doesn't conform to " \
32
                  "any of attribute #{attribute} types (#{attribute.types})"
33
              end
34
              return unless illegal_value?(value, attribute)
35
              "Provided value doesn't match default value (#{value})" \
36
                " or any of allowed values (#{attribute.values})"
37
            end
38
39
            # @param [Object] value Attribute value
40
            # @param [AMA::Entity::Mapper::Type::Attribute] attribute
41
            # @return [TrueClass, FalseClass]
42
            def illegal_nil?(value, attribute)
43
              return false unless value.nil? && !attribute.nullable
44
              attribute.types.none? { |type| type.instance?(value) }
45
            end
46
47
            # @param [Object] value Attribute value
48
            # @param [AMA::Entity::Mapper::Type::Attribute] attribute
49
            # @return [TrueClass, FalseClass]
50
            def invalid_type?(value, attribute)
51
              attribute.types.all? do |type|
52
                !type.respond_to?(:instance?) || !type.instance?(value)
53
              end
54
            end
55
56
            # @param [Object] value Attribute value
57
            # @param [AMA::Entity::Mapper::Type::Attribute] attribute
58
            # @return [TrueClass, FalseClass]
59
            def illegal_value?(value, attribute)
60
              return false if value == attribute.default
61
              return false if attribute.values.empty? || attribute.values.nil?
62
              !attribute.values.include?(value)
63
            end
64
65
            class << self
66
              include Mixin::Reflection
67
68
              # @param [Validator] validator
69
              # @return [Validator]
70 View Code Duplication
              def wrap(validator)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
71
                handler = handler_factory(validator, INSTANCE)
72
                description = "Safety wrapper for #{validator}"
73
                wrapper = method_object(:validate, to_s: description, &handler)
74
                wrapper.singleton_class.instance_eval do
75
                  include Mixin::Errors
76
                end
77
                wrapper
78
              end
79
80
              private
81
82
              # @param [Validator] validator
83
              # @param [Validator] fallback
84
              # @return [Proc]
85
              def handler_factory(validator, fallback)
86
                lambda do |val, attr, ctx|
87
                  begin
88
                    validator.validate(val, attr, ctx) do |v, a, c|
89
                      fallback.validate(v, a, c)
90
                    end
91
                  rescue StandardError => e
92
                    raise_if_internal(e)
93
                    message = "Unexpected error from validator #{validator}"
94
                    signature = '(value, attribute, context)'
95
                    options = { parent: e, context: ctx, signature: signature }
96
                    compliance_error(message, **options)
97
                  end
98
                end
99
              end
100
            end
101
          end
102
        end
103
      end
104
    end
105
  end
106
end
107