lite-validation 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +94 -0
  3. data/Gemfile +18 -0
  4. data/README.md +770 -0
  5. data/bench/calibrational.rb +89 -0
  6. data/bench/comparative.rb +197 -0
  7. data/bench/functional.rb +107 -0
  8. data/bench/profile.rb +45 -0
  9. data/lib/lite/validation/adapters/interfaces/dry.rb +29 -0
  10. data/lib/lite/validation/error.rb +9 -0
  11. data/lib/lite/validation/result/abstract/disputable.rb +17 -0
  12. data/lib/lite/validation/result/abstract/failure.rb +19 -0
  13. data/lib/lite/validation/result/abstract/refutable.rb +21 -0
  14. data/lib/lite/validation/result/abstract/success.rb +25 -0
  15. data/lib/lite/validation/result/abstract.rb +16 -0
  16. data/lib/lite/validation/structured_error/record.rb +36 -0
  17. data/lib/lite/validation/structured_error.rb +23 -0
  18. data/lib/lite/validation/validator/adapters/errors/default.rb +24 -0
  19. data/lib/lite/validation/validator/adapters/interfaces/default.rb +32 -0
  20. data/lib/lite/validation/validator/adapters/interfaces/dry.rb +17 -0
  21. data/lib/lite/validation/validator/adapters/predicates/dry/adapter.rb +50 -0
  22. data/lib/lite/validation/validator/adapters/predicates/dry/builder.rb +46 -0
  23. data/lib/lite/validation/validator/adapters/predicates/dry/engine.rb +32 -0
  24. data/lib/lite/validation/validator/adapters/predicates/dry.rb +3 -0
  25. data/lib/lite/validation/validator/coordinator/builder.rb +92 -0
  26. data/lib/lite/validation/validator/coordinator/default.rb +32 -0
  27. data/lib/lite/validation/validator/coordinator/errors/builder.rb +56 -0
  28. data/lib/lite/validation/validator/coordinator/errors/dry.rb +29 -0
  29. data/lib/lite/validation/validator/coordinator/errors/flat.rb +46 -0
  30. data/lib/lite/validation/validator/coordinator/errors/hierarchical.rb +29 -0
  31. data/lib/lite/validation/validator/coordinator/instance.rb +30 -0
  32. data/lib/lite/validation/validator/coordinator.rb +12 -0
  33. data/lib/lite/validation/validator/helpers/path.rb +49 -0
  34. data/lib/lite/validation/validator/node/abstract/branch.rb +21 -0
  35. data/lib/lite/validation/validator/node/abstract/instance.rb +43 -0
  36. data/lib/lite/validation/validator/node/abstract/leaf.rb +35 -0
  37. data/lib/lite/validation/validator/node/abstract.rb +25 -0
  38. data/lib/lite/validation/validator/node/child.rb +44 -0
  39. data/lib/lite/validation/validator/node/implementation/apply_ruling.rb +44 -0
  40. data/lib/lite/validation/validator/node/implementation/dig.rb +38 -0
  41. data/lib/lite/validation/validator/node/implementation/helpers/call_foreign.rb +31 -0
  42. data/lib/lite/validation/validator/node/implementation/helpers/with_result.rb +23 -0
  43. data/lib/lite/validation/validator/node/implementation/helpers/yield_strategy.rb +83 -0
  44. data/lib/lite/validation/validator/node/implementation/helpers/yield_validator.rb +49 -0
  45. data/lib/lite/validation/validator/node/implementation/identity.rb +90 -0
  46. data/lib/lite/validation/validator/node/implementation/iteration/iterator.rb +102 -0
  47. data/lib/lite/validation/validator/node/implementation/iteration.rb +46 -0
  48. data/lib/lite/validation/validator/node/implementation/navigation.rb +43 -0
  49. data/lib/lite/validation/validator/node/implementation/predication.rb +61 -0
  50. data/lib/lite/validation/validator/node/implementation/scoping/evaluator.rb +43 -0
  51. data/lib/lite/validation/validator/node/implementation/scoping.rb +43 -0
  52. data/lib/lite/validation/validator/node/implementation/validation.rb +64 -0
  53. data/lib/lite/validation/validator/node/implementation/wrap.rb +26 -0
  54. data/lib/lite/validation/validator/node/root.rb +60 -0
  55. data/lib/lite/validation/validator/node/suspended.rb +33 -0
  56. data/lib/lite/validation/validator/node.rb +12 -0
  57. data/lib/lite/validation/validator/option/none.rb +43 -0
  58. data/lib/lite/validation/validator/option/some/abstract.rb +29 -0
  59. data/lib/lite/validation/validator/option/some/complex/registry/abstract.rb +67 -0
  60. data/lib/lite/validation/validator/option/some/complex/registry/node.rb +47 -0
  61. data/lib/lite/validation/validator/option/some/complex/registry/root.rb +31 -0
  62. data/lib/lite/validation/validator/option/some/complex/registry.rb +32 -0
  63. data/lib/lite/validation/validator/option/some/complex/wrappers/abstract/iterable.rb +31 -0
  64. data/lib/lite/validation/validator/option/some/complex/wrappers/abstract/non_iterable.rb +27 -0
  65. data/lib/lite/validation/validator/option/some/complex/wrappers/abstract.rb +35 -0
  66. data/lib/lite/validation/validator/option/some/complex/wrappers/array.rb +41 -0
  67. data/lib/lite/validation/validator/option/some/complex/wrappers/hash.rb +40 -0
  68. data/lib/lite/validation/validator/option/some/complex/wrappers/object.rb +34 -0
  69. data/lib/lite/validation/validator/option/some/complex/wrappers/tuple.rb +47 -0
  70. data/lib/lite/validation/validator/option/some/complex/wrappers.rb +5 -0
  71. data/lib/lite/validation/validator/option/some/complex.rb +24 -0
  72. data/lib/lite/validation/validator/option/some/dig.rb +34 -0
  73. data/lib/lite/validation/validator/option/some/simple.rb +23 -0
  74. data/lib/lite/validation/validator/option/some/singular.rb +29 -0
  75. data/lib/lite/validation/validator/option/some.rb +20 -0
  76. data/lib/lite/validation/validator/option.rb +20 -0
  77. data/lib/lite/validation/validator/predicate/abstract/variants.rb +23 -0
  78. data/lib/lite/validation/validator/predicate/foreign/adapter/input/single.rb +21 -0
  79. data/lib/lite/validation/validator/predicate/foreign/adapter/input/tuple.rb +21 -0
  80. data/lib/lite/validation/validator/predicate/foreign/adapter/input.rb +28 -0
  81. data/lib/lite/validation/validator/predicate/foreign/adapter/ruling/instance.rb +37 -0
  82. data/lib/lite/validation/validator/predicate/foreign/adapter/ruling.rb +26 -0
  83. data/lib/lite/validation/validator/predicate/foreign/engine.rb +27 -0
  84. data/lib/lite/validation/validator/predicate/foreign/variant.rb +33 -0
  85. data/lib/lite/validation/validator/predicate/foreign/variants.rb +46 -0
  86. data/lib/lite/validation/validator/predicate/native/builder.rb +72 -0
  87. data/lib/lite/validation/validator/predicate/native/definite.rb +19 -0
  88. data/lib/lite/validation/validator/predicate/native/instance.rb +41 -0
  89. data/lib/lite/validation/validator/predicate/native/optional.rb +34 -0
  90. data/lib/lite/validation/validator/predicate/registry.rb +47 -0
  91. data/lib/lite/validation/validator/predicate.rb +17 -0
  92. data/lib/lite/validation/validator/result/abstract/failure.rb +21 -0
  93. data/lib/lite/validation/validator/result/abstract/instance.rb +18 -0
  94. data/lib/lite/validation/validator/result/abstract/success.rb +17 -0
  95. data/lib/lite/validation/validator/result/abstract.rb +29 -0
  96. data/lib/lite/validation/validator/result/committed.rb +75 -0
  97. data/lib/lite/validation/validator/result/disputable/hash.rb +17 -0
  98. data/lib/lite/validation/validator/result/disputable/instance.rb +43 -0
  99. data/lib/lite/validation/validator/result/disputable/iterable/array.rb +23 -0
  100. data/lib/lite/validation/validator/result/disputable/iterable.rb +17 -0
  101. data/lib/lite/validation/validator/result/disputable/navigable.rb +35 -0
  102. data/lib/lite/validation/validator/result/disputable.rb +14 -0
  103. data/lib/lite/validation/validator/result/disputed/abstract/hash.rb +32 -0
  104. data/lib/lite/validation/validator/result/disputed/abstract/instance.rb +26 -0
  105. data/lib/lite/validation/validator/result/disputed/iterable/array.rb +42 -0
  106. data/lib/lite/validation/validator/result/disputed/iterable/hash.rb +38 -0
  107. data/lib/lite/validation/validator/result/disputed/iterable.rb +20 -0
  108. data/lib/lite/validation/validator/result/disputed/navigable.rb +59 -0
  109. data/lib/lite/validation/validator/result/disputed.rb +17 -0
  110. data/lib/lite/validation/validator/result/refuted.rb +78 -0
  111. data/lib/lite/validation/validator/result/valid/abstract/collect.rb +42 -0
  112. data/lib/lite/validation/validator/result/valid/abstract/commit.rb +25 -0
  113. data/lib/lite/validation/validator/result/valid/abstract/instance.rb +23 -0
  114. data/lib/lite/validation/validator/result/valid/iterable/array/abstract.rb +24 -0
  115. data/lib/lite/validation/validator/result/valid/iterable/array/tuples.rb +64 -0
  116. data/lib/lite/validation/validator/result/valid/iterable/array/values.rb +42 -0
  117. data/lib/lite/validation/validator/result/valid/iterable/hash.rb +46 -0
  118. data/lib/lite/validation/validator/result/valid/iterable.rb +33 -0
  119. data/lib/lite/validation/validator/result/valid/navigable.rb +68 -0
  120. data/lib/lite/validation/validator/result/valid.rb +21 -0
  121. data/lib/lite/validation/validator/result.rb +15 -0
  122. data/lib/lite/validation/validator/ruling/abstract/invalid.rb +59 -0
  123. data/lib/lite/validation/validator/ruling/abstract/valid.rb +23 -0
  124. data/lib/lite/validation/validator/ruling/abstract.rb +12 -0
  125. data/lib/lite/validation/validator/ruling/commit.rb +17 -0
  126. data/lib/lite/validation/validator/ruling/dispute.rb +21 -0
  127. data/lib/lite/validation/validator/ruling/invalidate.rb +32 -0
  128. data/lib/lite/validation/validator/ruling/pass.rb +19 -0
  129. data/lib/lite/validation/validator/ruling/refute.rb +21 -0
  130. data/lib/lite/validation/validator/ruling.rb +53 -0
  131. data/lib/lite/validation/validator/state/instance.rb +46 -0
  132. data/lib/lite/validation/validator/state/merge_strategy.rb +50 -0
  133. data/lib/lite/validation/validator/state/unwrap_strategy.rb +31 -0
  134. data/lib/lite/validation/validator/state.rb +21 -0
  135. data/lib/lite/validation/validator.rb +15 -0
  136. data/lib/lite/validation/version.rb +9 -0
  137. data/lib/lite/validation.rb +8 -0
  138. metadata +196 -0
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'implementation/apply_ruling'
4
+ require_relative 'implementation/navigation'
5
+ require_relative 'implementation/identity'
6
+ require_relative 'implementation/iteration'
7
+ require_relative 'implementation/predication'
8
+ require_relative 'implementation/scoping'
9
+
10
+ module Lite
11
+ module Validation
12
+ module Validator
13
+ module Node
14
+ module Abstract
15
+ include Implementation::Identity
16
+ include Implementation::ApplyRuling
17
+ include Implementation::Navigation
18
+ include Implementation::Iteration
19
+ include Implementation::Predication
20
+ include Implementation::Scoping
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'abstract/instance'
4
+ require_relative 'abstract/leaf'
5
+ require_relative 'abstract/branch'
6
+ require_relative '../result'
7
+ require_relative '../state'
8
+ require_relative '../option'
9
+
10
+ module Lite
11
+ module Validation
12
+ module Validator
13
+ module Node
14
+ module Child
15
+ module Parent
16
+ def child(path, result, option: self.option, state: self.state)
17
+ if path.nil? || path.empty?
18
+ self.class::Leaf.instance(parent, self.path, option, result, state)
19
+ else
20
+ Child::Leaf.instance(self, path, option, result, state)
21
+ end
22
+ end
23
+ end
24
+
25
+ include Parent
26
+
27
+ def inspect
28
+ "#<Child::#{super}"
29
+ end
30
+
31
+ class Leaf < Abstract::Instance
32
+ include Abstract::Leaf
33
+ include Child
34
+ end
35
+
36
+ class Branch < Abstract::Instance
37
+ include Abstract::Branch
38
+ include Child
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../ruling'
4
+ require_relative 'helpers/with_result'
5
+
6
+ module Lite
7
+ module Validation
8
+ module Validator
9
+ module Node
10
+ module Implementation
11
+ module ApplyRuling
12
+ include Ruling::Constructors
13
+
14
+ def commit(value)
15
+ ApplyRuling.apply_ruling(self, Commit(value))
16
+ end
17
+
18
+ def auto_commit(as:)
19
+ Helpers::WithResult.with_result(self, result.auto_commit(as: as))
20
+ end
21
+
22
+ def dispute(error, at: nil, **opts)
23
+ ApplyRuling.apply_ruling(self, Dispute(error, **opts), path: at)
24
+ end
25
+
26
+ def refute(error, at: nil, **opts)
27
+ ApplyRuling.apply_ruling(self, Refute(error, **opts), path: at)
28
+ end
29
+
30
+ def self.apply_ruling(validator, ruling, path: nil)
31
+ return validator if ruling.is_a?(Ruling::Pass)
32
+
33
+ updated, _meta = validator.result.navigate(*path) do |result|
34
+ applied = Ruling.apply(ruling, result, validator.coordinator)
35
+ validator.merge_strategy.transform_result(applied, validator, path)
36
+ end
37
+ Helpers::WithResult.with_result(validator, updated)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../helpers/path'
4
+ require_relative '../../option/some/complex/wrappers/abstract'
5
+ require_relative 'helpers/with_result'
6
+
7
+ module Lite
8
+ module Validation
9
+ module Validator
10
+ module Node
11
+ module Implementation
12
+ module Dig
13
+ protected
14
+
15
+ def dig(*path, from: nil, &block)
16
+ from = Validator::Helpers::Path.expand_path(from || path, [])
17
+ dig!(path, from, &block)
18
+ end
19
+
20
+ private
21
+
22
+ def dig!(path, from, &block)
23
+ updated, _meta = result.navigate(*path) do |local|
24
+ option = self.option.dig(from)
25
+
26
+ block.call(option, local)
27
+ rescue Option::Some::Complex::Wrappers::Abstract::InvalidAccess => e
28
+ local.refute(coordinator.internal_error(:invalid_access, message: e.message))
29
+ end
30
+
31
+ Helpers::WithResult.with_result(self, updated)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Validation
5
+ module Validator
6
+ module Node
7
+ module Implementation
8
+ module Helpers
9
+ module CallForeign
10
+ def self.call_foreign(result, coordinator, &block)
11
+ block.call
12
+ rescue Error::Fatal
13
+ raise
14
+ rescue StandardError => e
15
+ rescue_execution_error(result, coordinator, e)
16
+ end
17
+
18
+ def self.rescue_execution_error(result, coordinator, error)
19
+ error = coordinator.internal_error(
20
+ :execution_error,
21
+ message: error.message, data: { error_class: error.class.name }
22
+ )
23
+ result.refute(error)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../ruling'
4
+
5
+ module Lite
6
+ module Validation
7
+ module Validator
8
+ module Node
9
+ module Implementation
10
+ module Helpers
11
+ module WithResult
12
+ def self.with_result(validator, result)
13
+ return validator if result.equal?(validator.result)
14
+
15
+ validator.send(:with, result: result)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../../error'
4
+
5
+ module Lite
6
+ module Validation
7
+ module Validator
8
+ module Node
9
+ module Implementation
10
+ module Helpers
11
+ module YieldStrategy
12
+ module Skip
13
+ def self.child_parameters(validator, option, result, &block)
14
+ maybe_yield(nil, option, result) { block.call(option, validator.send(:state).value_definite) }
15
+ end
16
+
17
+ def self.block_parameters(_validator, option, result, &block)
18
+ maybe_yield(nil, option, result) { block.call(option.unwrap) }
19
+ end
20
+
21
+ def self.maybe_yield(_, option, result, &block)
22
+ option.some? ? block.call : result
23
+ end
24
+ end
25
+
26
+ module Nullify
27
+ def self.child_parameters(_validator, option, _result, &block)
28
+ block.call(option.some_or_nil)
29
+ end
30
+
31
+ def self.block_parameters(_validator, option, _result, &block)
32
+ block.call(option.some_or_nil.unwrap)
33
+ end
34
+ end
35
+
36
+ module Refute
37
+ def self.child_parameters(validator, option, result, &block)
38
+ maybe_yield(validator, option, result) { block.call(option, validator.send(:state).value_definite) }
39
+ end
40
+
41
+ def self.block_parameters(validator, option, result, &block)
42
+ maybe_yield(validator, option, result) { block.call(option.unwrap) }
43
+ end
44
+
45
+ def self.maybe_yield(validator, option, result, &block)
46
+ option.some? ? block.call : result.refute(validator.coordinator.internal_error(:value_missing))
47
+ end
48
+ end
49
+
50
+ module YieldOption
51
+ def self.child_parameters(validator, option, _result, &block)
52
+ block.call(option, validator.send(:state).value_optional)
53
+ end
54
+
55
+ def self.block_parameters(validator, option, _result, &block)
56
+ block.call(option.to_option(validator.coordinator))
57
+ end
58
+ end
59
+
60
+ def self.to_yield(strategy)
61
+ case strategy
62
+ when :skip then Skip
63
+ when :nullify then Nullify
64
+ when :refute then Refute
65
+ when :yield_option then YieldOption
66
+ else raise Error::Fatal, "Unexpected missing yield strategy: #{strategy}"
67
+ end
68
+ end
69
+
70
+ def self.to_iterate(strategy)
71
+ case strategy
72
+ when :skip then Skip
73
+ when :refute then Refute
74
+ else raise Error::Fatal, "Unexpected missing iteration strategy: #{strategy}"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'call_foreign'
4
+
5
+ module Lite
6
+ module Validation
7
+ module Validator
8
+ module Node
9
+ module Implementation
10
+ module Helpers
11
+ module YieldValidator
12
+ def self.yield_child(parent, child, block)
13
+ updated, meta = yield_validator(child, block)
14
+ transformed = parent.merge_strategy.transform_result(updated, parent, child.path)
15
+ [transformed, meta]
16
+ end
17
+
18
+ def self.yield_validator(validator, block)
19
+ validator = ensure_valid_result!(validator, block.call(validator))
20
+
21
+ [validator.result, validator.context]
22
+ rescue Error::Fatal
23
+ raise
24
+ rescue StandardError => e
25
+ [
26
+ CallForeign.rescue_execution_error(validator.result, validator.coordinator, e),
27
+ validator.context
28
+ ]
29
+ end
30
+
31
+ def self.ensure_valid_result!(origin, result)
32
+ ensure_validator!(result)
33
+ Implementation::Identity.ensure_identical! origin, result
34
+
35
+ result
36
+ end
37
+
38
+ def self.ensure_validator!(candidate)
39
+ return if candidate.is_a?(Abstract::Instance)
40
+
41
+ raise Error::Fatal, "Validator expected, got: #{candidate.inspect}"
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Validation
5
+ module Validator
6
+ module Node
7
+ module Implementation
8
+ module Identity
9
+ def self.intent_id
10
+ @count ||= 0
11
+ @count += 1
12
+ end
13
+
14
+ def self.ensure_identical!(origin, current)
15
+ return if origin.identical?(current)
16
+
17
+ raise_error!(origin, current, origin.send(:intent) == current.send(:intent) ? :origin : :intent)
18
+ end
19
+
20
+ def self.raise_error!(origin, current, key)
21
+ full = key == :intent
22
+ message = "Not the #{key}: #{origin.send(:display_path, full)} <> #{current.send(:display_path, full)}"
23
+ raise Error::Fatal, message
24
+ end
25
+
26
+ def self.display_path(full_path)
27
+ case full_path
28
+ when Array then "[#{full_path.map { |element| display_path_element(element) }.join(',')}]"
29
+ else full_path
30
+ end
31
+ end
32
+
33
+ def self.display_path_element(element)
34
+ case element
35
+ when Array then "(#{element.map { |path| display_path(path) }.join(',')})"
36
+ else element
37
+ end
38
+ end
39
+
40
+ def identical?(other)
41
+ return true if other.equal?(self)
42
+ return false unless other.path == path
43
+ return true if other.parent.equal?(parent)
44
+ return false if other.root? || root?
45
+
46
+ other.parent.identical?(parent)
47
+ end
48
+
49
+ def key
50
+ path.last
51
+ end
52
+
53
+ def full_path(full)
54
+ if root?
55
+ full ? path : []
56
+ else
57
+ parent.full_path(full) + path
58
+ end
59
+ end
60
+
61
+ def display_path(full)
62
+ Identity.display_path(full_path(full))
63
+ end
64
+
65
+ def path_to(ancestor)
66
+ trace(ancestor, trace: [])
67
+ end
68
+
69
+ def intent
70
+ root? ? path.first : parent.intent
71
+ end
72
+
73
+ def root?
74
+ parent.nil?
75
+ end
76
+
77
+ protected
78
+
79
+ def trace(ancestor, trace: [])
80
+ return trace if ancestor.identical?(self)
81
+ return if root?
82
+
83
+ parent.trace(ancestor, trace: path + trace)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../helpers/yield_strategy'
4
+ require_relative '../helpers/yield_validator'
5
+ require_relative '../validation'
6
+
7
+ module Lite
8
+ module Validation
9
+ module Validator
10
+ module Node
11
+ module Implementation
12
+ module Iteration
13
+ class Iterator
14
+ def self.iterate(node, path, from, strategy, commit, &block)
15
+ node.send(:dig!, path, from) do |option, result|
16
+ Helpers::YieldStrategy
17
+ .to_iterate(strategy)
18
+ .maybe_yield(node, option, result) do
19
+ complex = option.to_complex
20
+ next result.refute(node.coordinator.internal_error(:not_iterable)) unless complex.iterable?
21
+
22
+ iterable = node.child(path, result.iterable(commit: commit), option: complex)
23
+ updated, meta = reduce(iterable, block)
24
+
25
+ [node.merge_strategy.transform_result(updated.navigable, node, path), meta]
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.reduce(iterable, block)
31
+ iterable.send(:option).reduce([iterable.result, iterable.context]) do |(result, context), (value, key)|
32
+ break [result, context] if result.refuted?
33
+
34
+ result.navigate(key) do |key_result|
35
+ block.call(iterable, key, value, key_result, context)
36
+ end
37
+ end
38
+ end
39
+
40
+ def initialize(parent, path, from, strategy, commit)
41
+ @parent = parent
42
+ @path = path
43
+ @from = from
44
+ @strategy = strategy
45
+ @commit = commit
46
+ freeze
47
+ end
48
+
49
+ def validate(commit: false, &block)
50
+ self.class.iterate(
51
+ parent,
52
+ path,
53
+ from,
54
+ strategy,
55
+ self.commit
56
+ ) do |iterable, _key, value, result, context|
57
+ updated = Validation.validate(
58
+ iterable.coordinator,
59
+ value,
60
+ result,
61
+ context,
62
+ commit,
63
+ block
64
+ )
65
+ [updated, context]
66
+ end
67
+ end
68
+
69
+ def satisfy(using: nil, severity: :dispute, commit: false, &block)
70
+ predicate = Predication
71
+ .resolve_predicate(using, from, parent.context, block)
72
+ .definite
73
+ .send(severity)
74
+
75
+ self.class.iterate(
76
+ parent,
77
+ path,
78
+ from,
79
+ strategy,
80
+ self.commit
81
+ ) do |iterable, _key, value, result, context|
82
+ Validation.validate(
83
+ iterable.coordinator,
84
+ value,
85
+ result,
86
+ context,
87
+ commit,
88
+ predicate
89
+ )
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ attr_reader :parent, :path, :from, :strategy, :commit
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dig'
4
+ require_relative 'helpers/yield_validator'
5
+ require_relative 'helpers/yield_strategy'
6
+ require_relative 'iteration/iterator'
7
+
8
+ module Lite
9
+ module Validation
10
+ module Validator
11
+ module Node
12
+ module Implementation
13
+ module Iteration
14
+ include Dig
15
+
16
+ def each_at?(*path, from: nil, commit: false, &block)
17
+ each_at!(path, from, :skip, commit, block)
18
+ end
19
+
20
+ def each_at(*path, from: nil, commit: false, &block)
21
+ each_at!(path, from, :refute, commit, block)
22
+ end
23
+
24
+ private
25
+
26
+ def each_at!(path, from, strategy, commit, block)
27
+ from = Validator::Helpers::Path.expand_path(from || path, [])
28
+ return Iterator.new(self, path, from, strategy, commit) if block.nil?
29
+
30
+ Iterator.iterate(self, path, from, strategy, commit) do |iterable, key, value, result, context|
31
+ child = iterable.child(
32
+ [key],
33
+ result,
34
+ option: Option.some(value),
35
+ state: iterable.send(:state).with(context: context).value_definite
36
+ )
37
+
38
+ Helpers::YieldValidator.yield_child(iterable, child, block)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dig'
4
+ require_relative 'helpers/yield_validator'
5
+ require_relative '../suspended'
6
+ require_relative 'helpers/yield_strategy'
7
+
8
+ module Lite
9
+ module Validation
10
+ module Validator
11
+ module Node
12
+ module Implementation
13
+ module Navigation
14
+ include Dig
15
+
16
+ def at?(*path, from: nil, &block)
17
+ return Suspended.new(:at!, self, path, from) if block.nil?
18
+
19
+ at!(path, from, :skip, block)
20
+ end
21
+
22
+ def at(*path, from: nil, &block)
23
+ at!(path, from, :refute, block)
24
+ end
25
+
26
+ private
27
+
28
+ def at!(path, from, strategy, block)
29
+ dig(*path, from: from) do |option, result|
30
+ strategy = Helpers::YieldStrategy.to_yield(strategy)
31
+ strategy.child_parameters(self, option, result) do |to_yield, child_state|
32
+ child = child(path, result, option: to_yield, state: child_state)
33
+
34
+ Helpers::YieldValidator.yield_child(self, child, block)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validation'
4
+ require_relative '../../predicate/registry'
5
+
6
+ module Lite
7
+ module Validation
8
+ module Validator
9
+ module Node
10
+ module Implementation
11
+ module Predication
12
+ include Validation
13
+
14
+ def self.resolve_predicate(using, path, context, block)
15
+ object = call_builder_block(using, path, context, block)
16
+ fetch_predicate(object)
17
+ end
18
+
19
+ def self.call_builder_block(using, path, context, block)
20
+ case using
21
+ when nil then block.call(context)
22
+ else
23
+ engine = Predicate::Registry.engine(using)
24
+ engine.build_contextual(path, context, &block)
25
+ end
26
+ end
27
+
28
+ def self.fetch_predicate(object)
29
+ case object
30
+ when Symbol then Predicate::Registry.predicate(object)
31
+ when Predicate::Abstract::Variants then object
32
+ else raise Error::Fatal, "Unexpected predicate object: #{object.inspect}"
33
+ end
34
+ end
35
+
36
+ def satisfy?(*path, from: nil, using: nil, severity: :dispute, commit: false, &block)
37
+ return Suspended.new(:satisfy!, self, path, from, using, severity, commit) if block.nil?
38
+
39
+ satisfy!(path, from, :skip, using, severity, commit, block)
40
+ end
41
+
42
+ def satisfy(*path, from: nil, using: nil, severity: :dispute, commit: false, &block)
43
+ satisfy!(path, from, :refute, using, severity, commit, block)
44
+ end
45
+
46
+ private
47
+
48
+ # rubocop:disable Metrics/ParameterLists
49
+ def satisfy!(path, from, strategy, using, severity, commit, block)
50
+ from = Validator::Helpers::Path.expand_path(from || path, [])
51
+ predicate = Predication.resolve_predicate(using, from, context, block)
52
+ variant = strategy == :yield_option ? predicate.optional : predicate.definite
53
+ validate!(path, from, strategy, commit, variant.send(severity))
54
+ end
55
+ # rubocop:enable Metrics/ParameterLists
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end