nxt_schema 0.1.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/Gemfile +0 -1
  4. data/Gemfile.lock +40 -42
  5. data/README.md +267 -121
  6. data/lib/nxt_schema.rb +60 -51
  7. data/lib/nxt_schema/callable.rb +21 -55
  8. data/lib/nxt_schema/dsl.rb +41 -31
  9. data/lib/nxt_schema/error.rb +4 -0
  10. data/lib/nxt_schema/errors/{error.rb → coercion_error.rb} +1 -2
  11. data/lib/nxt_schema/errors/invalid.rb +16 -0
  12. data/lib/nxt_schema/errors/invalid_options.rb +6 -0
  13. data/lib/nxt_schema/node/any_of.rb +39 -0
  14. data/lib/nxt_schema/node/base.rb +66 -267
  15. data/lib/nxt_schema/node/collection.rb +40 -56
  16. data/lib/nxt_schema/node/error_store.rb +41 -0
  17. data/lib/nxt_schema/node/errors/schema_error.rb +15 -0
  18. data/lib/nxt_schema/node/errors/validation_error.rb +15 -0
  19. data/lib/nxt_schema/node/leaf.rb +8 -36
  20. data/lib/nxt_schema/node/schema.rb +70 -103
  21. data/lib/nxt_schema/registry.rb +12 -74
  22. data/lib/nxt_schema/registry/proxy.rb +21 -0
  23. data/lib/nxt_schema/template/any_of.rb +50 -0
  24. data/lib/nxt_schema/template/base.rb +220 -0
  25. data/lib/nxt_schema/template/collection.rb +23 -0
  26. data/lib/nxt_schema/template/has_sub_nodes.rb +87 -0
  27. data/lib/nxt_schema/template/leaf.rb +13 -0
  28. data/lib/nxt_schema/template/maybe_evaluator.rb +28 -0
  29. data/lib/nxt_schema/template/on_evaluator.rb +25 -0
  30. data/lib/nxt_schema/template/schema.rb +22 -0
  31. data/lib/nxt_schema/template/sub_nodes.rb +22 -0
  32. data/lib/nxt_schema/template/type_resolver.rb +39 -0
  33. data/lib/nxt_schema/template/type_system_resolver.rb +22 -0
  34. data/lib/nxt_schema/types.rb +7 -4
  35. data/lib/nxt_schema/undefined.rb +4 -2
  36. data/lib/nxt_schema/validators/{equality.rb → equal_to.rb} +2 -2
  37. data/lib/nxt_schema/validators/error_messages.rb +42 -0
  38. data/lib/nxt_schema/{error_messages → validators/error_messages}/en.yaml +6 -5
  39. data/lib/nxt_schema/validators/{excluded.rb → excluded_in.rb} +1 -1
  40. data/lib/nxt_schema/validators/{included.rb → included_in.rb} +1 -1
  41. data/lib/nxt_schema/validators/includes.rb +1 -1
  42. data/lib/nxt_schema/validators/optional_node.rb +11 -6
  43. data/lib/nxt_schema/validators/registry.rb +1 -7
  44. data/lib/nxt_schema/{node → validators}/validate_with_proxy.rb +3 -3
  45. data/lib/nxt_schema/validators/validator.rb +2 -2
  46. data/lib/nxt_schema/version.rb +1 -1
  47. data/nxt_schema.gemspec +1 -0
  48. metadata +44 -21
  49. data/lib/nxt_schema/callable_or_value.rb +0 -72
  50. data/lib/nxt_schema/error_messages.rb +0 -40
  51. data/lib/nxt_schema/errors.rb +0 -4
  52. data/lib/nxt_schema/errors/invalid_options_error.rb +0 -5
  53. data/lib/nxt_schema/errors/schema_not_applied_error.rb +0 -5
  54. data/lib/nxt_schema/node/constructor.rb +0 -9
  55. data/lib/nxt_schema/node/default_value_evaluator.rb +0 -20
  56. data/lib/nxt_schema/node/error.rb +0 -13
  57. data/lib/nxt_schema/node/has_subnodes.rb +0 -97
  58. data/lib/nxt_schema/node/maybe_evaluator.rb +0 -23
  59. data/lib/nxt_schema/node/template_store.rb +0 -15
  60. data/lib/nxt_schema/node/type_resolver.rb +0 -24
data/lib/nxt_schema.rb CHANGED
@@ -1,69 +1,78 @@
1
- require "nxt_schema/version"
2
- require "pry"
3
- require "active_support/all"
1
+ require 'nxt_schema/version'
2
+ require 'active_support/all'
4
3
  require 'dry-types'
5
4
  require 'nxt_registry'
5
+ require 'nxt_init'
6
6
  require 'yaml'
7
7
 
8
- require "nxt_schema/types"
9
- require "nxt_schema/undefined"
10
- require "nxt_schema/registry"
11
- require "nxt_schema/callable"
12
- require "nxt_schema/callable_or_value"
13
- require "nxt_schema/validators/registry"
14
- require "nxt_schema/node/validate_with_proxy"
8
+ require_relative 'nxt_schema/types'
9
+ require_relative 'nxt_schema/callable'
10
+ require_relative 'nxt_schema/node'
11
+ require_relative 'nxt_schema/undefined'
12
+ require_relative 'nxt_schema/error'
13
+ require_relative 'nxt_schema/errors/invalid'
14
+ require_relative 'nxt_schema/errors/invalid_options'
15
+ require_relative 'nxt_schema/errors/coercion_error'
15
16
 
16
- require "nxt_schema/errors"
17
- require "nxt_schema/errors/error"
18
- require "nxt_schema/errors/schema_not_applied_error"
19
- require "nxt_schema/errors/invalid_options_error"
17
+ require_relative 'nxt_schema/validators/registry'
18
+ require_relative 'nxt_schema/validators/validate_with_proxy'
19
+ require_relative 'nxt_schema/validators/error_messages'
20
+ require_relative 'nxt_schema/validators/validator'
21
+ require_relative 'nxt_schema/validators/attribute'
22
+ require_relative 'nxt_schema/validators/equal_to'
23
+ require_relative 'nxt_schema/validators/optional_node'
24
+ require_relative 'nxt_schema/validators/greater_than'
25
+ require_relative 'nxt_schema/validators/greater_than_or_equal'
26
+ require_relative 'nxt_schema/validators/less_than'
27
+ require_relative 'nxt_schema/validators/less_than_or_equal'
28
+ require_relative 'nxt_schema/validators/pattern'
29
+ require_relative 'nxt_schema/validators/included_in'
30
+ require_relative 'nxt_schema/validators/includes'
31
+ require_relative 'nxt_schema/validators/excluded_in'
32
+ require_relative 'nxt_schema/validators/excludes'
33
+ require_relative 'nxt_schema/validators/query'
20
34
 
21
- require "nxt_schema/error_messages"
22
- require "nxt_schema/validators/validator"
23
- require "nxt_schema/validators/attribute"
24
- require "nxt_schema/validators/equality"
25
- require "nxt_schema/validators/optional_node"
26
- require "nxt_schema/validators/greater_than"
27
- require "nxt_schema/validators/greater_than_or_equal"
28
- require "nxt_schema/validators/less_than"
29
- require "nxt_schema/validators/less_than_or_equal"
30
- require "nxt_schema/validators/pattern"
31
- require "nxt_schema/validators/included"
32
- require "nxt_schema/validators/includes"
33
- require "nxt_schema/validators/excluded"
34
- require "nxt_schema/validators/excludes"
35
- require "nxt_schema/validators/query"
35
+ require_relative 'nxt_schema/template/on_evaluator'
36
+ require_relative 'nxt_schema/template/maybe_evaluator'
37
+ require_relative 'nxt_schema/template/type_resolver'
38
+ require_relative 'nxt_schema/template/type_system_resolver'
39
+ require_relative 'nxt_schema/template/base'
40
+ require_relative 'nxt_schema/template/sub_nodes'
41
+ require_relative 'nxt_schema/template/has_sub_nodes'
42
+ require_relative 'nxt_schema/template/any_of'
43
+ require_relative 'nxt_schema/template/collection'
44
+ require_relative 'nxt_schema/template/schema'
45
+ require_relative 'nxt_schema/template/leaf'
36
46
 
37
- require "nxt_schema/node"
38
- require "nxt_schema/node/type_resolver"
39
- require "nxt_schema/node/maybe_evaluator"
40
- require "nxt_schema/node/default_value_evaluator"
41
- require "nxt_schema/node/base"
42
- require "nxt_schema/node/error"
43
- require "nxt_schema/node/has_subnodes"
44
- require "nxt_schema/node/template_store"
45
- require "nxt_schema/node/schema"
46
- require "nxt_schema/node/collection"
47
- require "nxt_schema/node/leaf"
48
- require "nxt_schema/dsl"
47
+ require_relative 'nxt_schema/node/errors/schema_error'
48
+ require_relative 'nxt_schema/node/errors/validation_error'
49
+ require_relative 'nxt_schema/node/error_store'
50
+ require_relative 'nxt_schema/node/base'
51
+ require_relative 'nxt_schema/node/any_of'
52
+ require_relative 'nxt_schema/node/leaf'
53
+ require_relative 'nxt_schema/node/collection'
54
+ require_relative 'nxt_schema/node/schema'
55
+ require_relative 'nxt_schema/dsl'
56
+ require_relative 'nxt_schema/registry/proxy'
57
+ require_relative 'nxt_schema/registry'
49
58
 
50
59
  module NxtSchema
51
- def register_validator(validator, *keys)
52
- keys.each do |key|
53
- NxtSchema::Validators::Registry::VALIDATORS.register(key, validator)
54
- end
60
+ extend Dsl
61
+
62
+ def register_error_messages(*paths)
63
+ Validators::ErrorMessages.load(paths)
55
64
  end
56
65
 
57
- def register_type(key, type)
58
- NxtSchema::Types.const_set(key.to_s, type)
66
+ def register_validator(validator, *keys)
67
+ keys.each { |key| NxtSchema::Validators::REGISTRY.register(key, validator) }
59
68
  end
60
69
 
61
- def register_error_messages(*paths)
62
- ErrorMessages.load(paths)
70
+ def register_type(key, type)
71
+ NxtSchema::Types.registry(:types).register(key, type)
63
72
  end
64
73
 
65
74
  # Load default messages
66
- ErrorMessages.load
75
+ Validators::ErrorMessages.load
67
76
 
68
- module_function :register_validator, :register_type, :register_error_messages
77
+ module_function :register_error_messages, :register_validator, :register_type
69
78
  end
@@ -1,74 +1,40 @@
1
1
  module NxtSchema
2
2
  class Callable
3
- def initialize(callee)
4
- @callee = callee
5
-
6
- if callee.is_a?(Symbol)
7
- self.type = :method
8
- elsif callee.respond_to?(:call)
9
- self.type = :proc
10
- self.context = callee.binding
11
- else
12
- raise ArgumentError, "Callee is nor symbol nor a proc: #{callee}"
13
- end
14
- end
15
-
16
- def bind!(execution_context)
17
- self.context = execution_context
18
- ensure_context_not_missing
19
- self
3
+ def initialize(callable, target = nil, *args)
4
+ @callable = callable
5
+ @target = target
6
+ @args = args
20
7
  end
21
8
 
22
- def bind(execution_context = nil)
23
- return self if context
9
+ def call
10
+ return callable if value?
11
+ return callable.call(*args_from_arity) if proc?
24
12
 
25
- self.context = execution_context
26
- ensure_context_not_missing
27
- self
13
+ target.send(callable, *args_from_arity)
28
14
  end
29
15
 
30
- # NOTE: Currently we only allow arguments! Not keyword args or **options
31
- # If we would allow **options and we would pass a hash as the only argument it would
32
- # automatically be parsed as the options!
33
- def call(*args)
34
- ensure_context_not_missing
35
-
36
- args = args.take(arity)
16
+ def method?
17
+ @method ||= callable.class.in?([Symbol, String]) && target.respond_to?(callable)
18
+ end
37
19
 
38
- if method?
39
- context.send(callee, *args)
40
- else
41
- context.instance_exec(*args, &callee)
42
- end
20
+ def proc?
21
+ @proc ||= callable.respond_to?(:call)
43
22
  end
44
23
 
45
- def arity
46
- if proc?
47
- callee.arity
48
- elsif method?
49
- method = context.send(:method, callee)
50
- method.arity
51
- else
52
- raise ArgumentError, "Can't resolve arity from #{callee}"
53
- end
24
+ def value?
25
+ !method? && !proc?
54
26
  end
55
27
 
56
28
  private
57
29
 
58
- def proc?
59
- type == :proc
60
- end
30
+ attr_reader :callable, :target, :args
61
31
 
62
- def method?
63
- type == :method
32
+ def arity
33
+ proc? ? callable.arity : 0
64
34
  end
65
35
 
66
- def ensure_context_not_missing
67
- return if context
68
-
69
- raise ArgumentError, "Missing context: #{context}"
36
+ def args_from_arity
37
+ @args_from_arity ||= ([target] + args).take(arity)
70
38
  end
71
-
72
- attr_accessor :context, :callee, :type
73
39
  end
74
- end
40
+ end
@@ -1,38 +1,48 @@
1
1
  module NxtSchema
2
- def schema(name = :root, **options, &block)
3
- Node::Schema.new(name: name, parent_node: nil, **options, &block)
4
- end
2
+ module Dsl
3
+ DEFAULT_OPTIONS = { type_system: NxtSchema::Types }.freeze
5
4
 
6
- def collection(name = :roots, **options, &block)
7
- Node::Collection.new(name: name, parent_node: nil, **options, &block)
8
- end
5
+ def collection(name = :root, type: NxtSchema::Template::Collection::DEFAULT_TYPE, **options, &block)
6
+ NxtSchema::Template::Collection.new(
7
+ name: name,
8
+ type: type,
9
+ parent_node: nil,
10
+ **DEFAULT_OPTIONS.merge(options),
11
+ &block
12
+ )
13
+ end
9
14
 
10
- def params(name = :root, **options, &block)
11
- Node::Schema.new(
12
- name: name,
13
- parent_node: nil,
14
- **options.merge(
15
- type_system: NxtSchema::Types::Params,
16
- ).reverse_merge(transform_keys: :to_sym),
17
- &block
18
- )
19
- end
15
+ alias nodes collection
20
16
 
21
- def json(name = :root, **options, &block)
22
- Node::Schema.new(
23
- name: name,
24
- parent_node: nil,
25
- **options.merge(
26
- type_system: NxtSchema::Types::JSON,
27
- ).reverse_merge(transform_keys: :to_sym),
28
- &block
29
- )
30
- end
17
+ def schema(name = :roots, type: NxtSchema::Template::Schema::DEFAULT_TYPE, **options, &block)
18
+ NxtSchema::Template::Schema.new(
19
+ name: name,
20
+ type: type,
21
+ parent_node: nil,
22
+ **DEFAULT_OPTIONS.merge(options),
23
+ &block
24
+ )
25
+ end
26
+
27
+ def any_of(name = :roots, **options, &block)
28
+ NxtSchema::Template::AnyOf.new(
29
+ name: name,
30
+ parent_node: nil,
31
+ **DEFAULT_OPTIONS.merge(options),
32
+ &block
33
+ )
34
+ end
31
35
 
32
- alias_method :node, :schema
33
- alias_method :root, :schema
34
- alias_method :nodes, :collection
35
- alias_method :roots, :collection
36
+ # schema root with NxtSchema::Types::Params type system
36
37
 
37
- module_function :root, :roots, :node, :nodes, :collection, :schema, :params
38
+ def params(name = :params, type: NxtSchema::Template::Schema::DEFAULT_TYPE, **options, &block)
39
+ NxtSchema::Template::Schema.new(
40
+ name: name,
41
+ type: type,
42
+ parent_node: nil,
43
+ **options.merge(type_system: NxtSchema::Types::Params),
44
+ &block
45
+ )
46
+ end
47
+ end
38
48
  end
@@ -0,0 +1,4 @@
1
+ module NxtSchema
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -1,7 +1,6 @@
1
1
  module NxtSchema
2
2
  module Errors
3
- class Error < StandardError
4
-
3
+ class CoercionError < Error
5
4
  end
6
5
  end
7
6
  end
@@ -0,0 +1,16 @@
1
+ module NxtSchema
2
+ module Errors
3
+ class Invalid < NxtSchema::Error
4
+ def initialize(node)
5
+ @node = node
6
+ super(build_message)
7
+ end
8
+
9
+ attr_reader :node
10
+
11
+ def build_message
12
+ node.errors
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ module NxtSchema
2
+ module Errors
3
+ class InvalidOptions < NxtSchema::Error
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,39 @@
1
+ module NxtSchema
2
+ module Node
3
+ class AnyOf < Node::Base
4
+ def valid?
5
+ valid_node.present?
6
+ end
7
+
8
+ def call
9
+ child_nodes.map(&:call)
10
+
11
+ if valid?
12
+ self.output = valid_node.output
13
+ else
14
+ child_nodes.each do |node|
15
+ merge_errors(node)
16
+ end
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ private
23
+
24
+ delegate :[], to: :child_nodes
25
+
26
+ def valid_node
27
+ child_nodes.find(&:valid?)
28
+ end
29
+
30
+ def child_nodes
31
+ @child_nodes ||= nodes.map { |node| node.build_node(input: input, context: context, parent: self) }
32
+ end
33
+
34
+ def nodes
35
+ @nodes ||= node.sub_nodes.values
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,317 +1,116 @@
1
1
  module NxtSchema
2
2
  module Node
3
3
  class Base
4
- def initialize(name: name_from_index, type:, parent_node:, **options, &block)
5
- @name = name
6
- @parent_node = parent_node
7
- @options = options
8
- @type_system = resolve_type_system
9
- @additional_keys_strategy = resolve_additional_keys_strategy
10
- @type = type
11
- @schema_errors_key = options.fetch(:schema_errors_key, :itself)
12
- @validations = []
13
- @level = parent_node ? parent_node.level + 1 : 0
14
- @all_nodes = parent_node ? (parent_node.all_nodes || {}) : {}
15
- @is_root = parent_node.nil?
16
- @root = parent_node.nil? ? self : parent_node.root
17
- @errors = {}
18
- @context = nil
19
- @applied = false
20
- @input = nil
21
- @value = NxtSchema::Undefined.new
22
- @locale = options.fetch(:locale) { parent_node&.locale || 'en' }
4
+ def initialize(node:, input: Undefined.new, parent:, context:, error_key:)
5
+ @node = node
6
+ @input = input
7
+ @parent = parent
8
+ @output = nil
9
+ @error_key = error_key
10
+ @context = context || parent&.context
11
+ @coerced = false
12
+ @coerced_nodes = parent&.coerced_nodes || []
13
+ @is_root = parent.nil?
14
+ @root = parent.nil? ? self : parent.root
15
+ @errors = ErrorStore.new(self)
16
+ @locale = node.options.fetch(:locale) { parent&.locale || 'en' }.to_s
23
17
 
24
- # Note that it is not possible to use present? on an instance of NxtSchema::Schema since it inherits from Hash
25
- evaluate_block(block) if block_given?
18
+ @index = error_key
19
+ resolve_error_key(error_key)
26
20
  end
27
21
 
28
- attr_accessor :name,
29
- :parent_node,
30
- :options,
31
- :type,
32
- :schema_errors,
33
- :namespace,
34
- :errors,
35
- :validations,
36
- :schema_errors_key,
37
- :level,
38
- :validation_errors,
39
- :all_nodes,
40
- :value,
41
- :type_system,
42
- :root,
43
- :context,
44
- :applied,
45
- :input,
46
- :additional_keys_strategy,
47
- :locale
22
+ attr_accessor :output, :node, :input
23
+ attr_reader :parent, :context, :error_key, :coerced, :coerced_nodes, :root, :errors, :locale, :index
48
24
 
49
-
50
- alias_method :types, :type_system
51
-
52
- def parent(level = 1)
53
- level.times.inject(self) { |acc| acc.parent_node }
54
- end
55
-
56
- alias_method :up, :parent
57
-
58
- def default(default_value, &block)
59
- options.merge!(default: default_value)
60
- evaluate_block(block) if block_given?
61
- self
25
+ def call
26
+ raise NotImplementedError, 'Implement this in our sub class'
62
27
  end
63
28
 
64
- def meta(value = NxtSchema::Undefined.new)
65
- if value.is_a?(NxtSchema::Undefined)
66
- @meta
67
- else
68
- @meta = value
69
- self
70
- end
71
- end
72
-
73
- def value_or_default_value(value)
74
- if !value && options.key?(:default)
75
- DefaultValueEvaluator.new(self, options.fetch(:default)).call
76
- else
77
- value
78
- end
79
- end
29
+ delegate :name, :options, to: :node
80
30
 
81
- def maybe(maybe_value, &block)
82
- options.merge!(maybe: maybe_value)
83
- evaluate_block(block) if block_given?
84
- self
31
+ def root?
32
+ @is_root
85
33
  end
86
34
 
87
- def optional(optional_value, &block)
88
- raise ArgumentError, 'Optional nodes can only exist within schemas' unless parent.is_a?(NxtSchema::Node::Schema)
89
-
90
- options.merge!(optional: optional_value)
91
- evaluate_block(block) if block_given?
92
- self
35
+ def valid?
36
+ errors.empty?
93
37
  end
94
38
 
95
- def presence(presence_value, &block)
96
- raise ArgumentError, 'Present nodes can only exist within schemas' unless parent.is_a?(NxtSchema::Node::Schema)
97
-
98
- options.merge!(presence: presence_value)
99
- evaluate_block(block) if block_given?
100
- self
39
+ def add_error(error)
40
+ errors.add_validation_error(message: error)
101
41
  end
102
42
 
103
- def presence?
104
- @presence ||= begin
105
- presence_option = options[:presence]
106
-
107
- options[:presence] = if presence_option.respond_to?(:call)
108
- Callable.new(presence_option).call(self, value)
109
- else
110
- presence_option
111
- end
112
- end
43
+ def add_schema_error(error)
44
+ errors.add_schema_error(message: error)
113
45
  end
114
46
 
115
- def validate(key, *args, &block)
116
- if key.is_a?(Symbol)
117
- validator = validator(key, *args)
118
- elsif key.respond_to?(:call)
119
- validator = key
120
- else
121
- raise ArgumentError, "Don't know how to resolve validator from: #{key}"
122
- end
123
-
124
- add_validators(validator)
125
- evaluate_block(block) if block_given?
126
- self
47
+ def merge_errors(node)
48
+ errors.merge_errors(node)
127
49
  end
128
50
 
129
- def add_error(error, index = schema_errors_key)
130
- validation_errors[index] ||= []
131
- validation_errors[index] << error
132
- false
133
- end
51
+ def run_validations
52
+ return false unless coerced?
134
53
 
135
- def validate_all_nodes
136
- sorted_nodes = all_nodes.values.sort do |node, other_node|
137
- [node.level, (!node.leaf?).to_s] <=> [other_node.level, (!other_node.leaf?).to_s]
54
+ node.validations.each do |validation|
55
+ args = [self, input]
56
+ validation.call(*args.take(validation.arity))
138
57
  end
139
-
140
- # we have to start from the bottom, leafs before others on the same level
141
- sorted_nodes.reverse_each(&:apply_validations)
142
58
  end
143
59
 
144
- def apply_validations
145
- # We don't run validations in case there are schema errors
146
- # to avoid weird errors
147
- # First reject empty schema_errors
148
- schema_errors.reject! { |_, v| v.empty? }
149
-
150
- # TODO: Is this correct? - Do not apply validations when maybe criteria applies?
151
- unless schema_errors[schema_errors_key]&.any? && !maybe_criteria_applies?(value)
152
- build_validations
60
+ def up(levels = 1)
61
+ 0.upto(levels - 1).inject(self) do |acc, _|
62
+ parent = acc.send(:parent)
63
+ break acc unless parent
153
64
 
154
- validations.each do |validation|
155
- args = [self, value]
156
- validation.call(*args.take(validation.arity))
157
- end
65
+ parent
158
66
  end
159
-
160
- if self.is_a?(NxtSchema::Node::Collection) && value.respond_to?(:each)
161
- value.each_with_index do |item, index|
162
- validation_errors[index]&.reject! { |_, v| v.empty? }
163
- end
164
- end
165
-
166
- validation_errors.reject! { |_, v| v.empty? }
167
-
168
- self
169
- end
170
-
171
- def build_validations
172
- validations_from_options = Array(options.fetch(:validate, []))
173
- self.validations = validations_from_options
174
- end
175
-
176
- def schema_errors?
177
- schema_errors.reject! { |_, v| v.empty? }
178
- schema_errors.any?
179
- end
180
-
181
- def validation_errors?
182
- validation_errors.reject! { |_, v| v.empty? }
183
- validation_errors.any?
184
- end
185
-
186
- def root?
187
- @is_root
188
- end
189
-
190
- def leaf?
191
- false
192
- end
193
-
194
- def valid?
195
- raise SchemaNotAppliedError, 'Schema was not applied yet' unless applied?
196
-
197
- validation_errors.empty?
198
- end
199
-
200
- def add_validators(validator)
201
- options[:validate] ||= []
202
- options[:validate] = Array(options.fetch(:validate, []))
203
- options[:validate] << validator
204
- end
205
-
206
- def validator(key, *args)
207
- Validators::Registry::VALIDATORS.resolve(key).new(*args).build
208
- end
209
-
210
- def validate_with(&block)
211
- add_validators(
212
- ->(node) { NxtSchema::Node::ValidateWithProxy.new(node).validate(&block) }
213
- )
214
67
  end
215
68
 
216
69
  private
217
70
 
218
- def register_node(context)
219
- return if all_nodes.key?(object_id)
71
+ attr_writer :coerced, :root
220
72
 
221
- self.context = context
222
- all_nodes[object_id] = self
223
- end
73
+ def coerce_input
74
+ output = input.is_a?(Undefined) && node.omnipresent? ? input : node.type.call(input)
75
+ self.output = output
224
76
 
225
- def applied?
226
- @applied
77
+ rescue Dry::Types::CoercionError, NxtSchema::Errors::CoercionError => error
78
+ add_schema_error(error.message)
227
79
  end
228
80
 
229
- def mark_as_applied
230
- self.applied = true
81
+ def apply_on_evaluators
82
+ node.on_evaluators.each { |evaluator| evaluator.call(input, self, context) { |result| self.input = result } }
231
83
  end
232
84
 
233
- def add_schema_error(error, index = schema_errors_key)
234
- schema_errors[index] ||= []
235
- schema_errors[index] << error
85
+ def maybe_evaluator_applies?
86
+ @maybe_evaluator_applies ||= node.maybe_evaluators.inject(false) do |acc, evaluator|
87
+ result = (acc || evaluator.call(input, self, context))
236
88
 
237
- add_error(error, index)
238
- end
239
-
240
- def maybe_criteria_applies?(value)
241
- @maybe_criteria_applies ||= begin
242
- options.key?(:maybe) && MaybeEvaluator.new(self, options.fetch(:maybe), value).call
243
- end
244
- end
245
-
246
- def self_without_empty_schema_errors
247
- schema_errors.reject! { |_, v| v.empty? }
248
- validate_all_nodes if root?
249
- self.errors = flat_validation_errors(validation_errors, name)
250
- self
251
- end
252
-
253
- def flat_validation_errors(errors, namespace, acc = {})
254
- errors.each_with_object(acc) do |(key, val), acc|
255
- current_namespace = [namespace, key].reject { |namespace| namespace == schema_errors_key }.compact.join('.')
256
-
257
- if val.is_a?(::Hash)
258
- flat_validation_errors(val, current_namespace, acc)
259
- else
260
- acc[current_namespace] ||= []
261
- acc[current_namespace] += Array(val)
262
- end
263
- end
264
- end
265
-
266
- def name_from_index
267
- if parent_node
268
- if parent_node.is_a?(NxtSchema::Node::Collection)
269
- size + 1
89
+ if result
90
+ self.output = input
91
+ break true
270
92
  else
271
- raise ArgumentError, "Nodes with parent_node: #{parent_node} cannot be anonymous"
93
+ false
272
94
  end
273
- else
274
- :root
275
95
  end
276
96
  end
277
97
 
278
- def evaluate_block(block)
279
- if block.arity.zero?
280
- instance_exec(&block)
281
- else
282
- evaluator_args = [self, value]
283
- block.call(*evaluator_args.take(block.arity))
284
- end
285
- end
286
-
287
- def resolve_type_system
288
- type_system = options.fetch(:type_system) { parent_node&.type_system }
98
+ def register_as_coerced_when_no_errors
99
+ return unless valid?
289
100
 
290
- self.type_system = if type_system.is_a?(Module)
291
- type_system
292
- elsif type_system.is_a?(Symbol) || type_system.is_a?(String)
293
- "NxtSchema::Types::#{type_system.to_s.classify}".constantize
294
- else
295
- NxtSchema::Types
296
- end
101
+ self.coerced = true
102
+ coerced_nodes << self
297
103
  end
298
104
 
299
- def resolve_additional_keys_strategy
300
- options.fetch(:additional_keys) { parent_node&.send(:resolve_additional_keys_strategy) || :ignore }
301
- end
302
-
303
- def type_resolver
304
- @type_resolver ||= begin
305
- if root?
306
- TypeResolver.new
307
- else
308
- raise NoMethodError, 'type_resolver is only available on root node'
309
- end
310
- end
105
+ def resolve_error_key(key)
106
+ parts = [parent&.error_key].compact
107
+ parts << (key.present? ? "#{node.name}[#{key}]" : node.name)
108
+ @error_key = parts.join('.')
311
109
  end
312
110
 
313
- def coerce_value(value)
314
- type[value]
111
+ def coerced?(&block)
112
+ block.call(self) if @coerced && block_given?
113
+ @coerced
315
114
  end
316
115
  end
317
116
  end