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.
- checksums.yaml +7 -0
- data/.rubocop.yml +94 -0
- data/Gemfile +18 -0
- data/README.md +770 -0
- data/bench/calibrational.rb +89 -0
- data/bench/comparative.rb +197 -0
- data/bench/functional.rb +107 -0
- data/bench/profile.rb +45 -0
- data/lib/lite/validation/adapters/interfaces/dry.rb +29 -0
- data/lib/lite/validation/error.rb +9 -0
- data/lib/lite/validation/result/abstract/disputable.rb +17 -0
- data/lib/lite/validation/result/abstract/failure.rb +19 -0
- data/lib/lite/validation/result/abstract/refutable.rb +21 -0
- data/lib/lite/validation/result/abstract/success.rb +25 -0
- data/lib/lite/validation/result/abstract.rb +16 -0
- data/lib/lite/validation/structured_error/record.rb +36 -0
- data/lib/lite/validation/structured_error.rb +23 -0
- data/lib/lite/validation/validator/adapters/errors/default.rb +24 -0
- data/lib/lite/validation/validator/adapters/interfaces/default.rb +32 -0
- data/lib/lite/validation/validator/adapters/interfaces/dry.rb +17 -0
- data/lib/lite/validation/validator/adapters/predicates/dry/adapter.rb +50 -0
- data/lib/lite/validation/validator/adapters/predicates/dry/builder.rb +46 -0
- data/lib/lite/validation/validator/adapters/predicates/dry/engine.rb +32 -0
- data/lib/lite/validation/validator/adapters/predicates/dry.rb +3 -0
- data/lib/lite/validation/validator/coordinator/builder.rb +92 -0
- data/lib/lite/validation/validator/coordinator/default.rb +32 -0
- data/lib/lite/validation/validator/coordinator/errors/builder.rb +56 -0
- data/lib/lite/validation/validator/coordinator/errors/dry.rb +29 -0
- data/lib/lite/validation/validator/coordinator/errors/flat.rb +46 -0
- data/lib/lite/validation/validator/coordinator/errors/hierarchical.rb +29 -0
- data/lib/lite/validation/validator/coordinator/instance.rb +30 -0
- data/lib/lite/validation/validator/coordinator.rb +12 -0
- data/lib/lite/validation/validator/helpers/path.rb +49 -0
- data/lib/lite/validation/validator/node/abstract/branch.rb +21 -0
- data/lib/lite/validation/validator/node/abstract/instance.rb +43 -0
- data/lib/lite/validation/validator/node/abstract/leaf.rb +35 -0
- data/lib/lite/validation/validator/node/abstract.rb +25 -0
- data/lib/lite/validation/validator/node/child.rb +44 -0
- data/lib/lite/validation/validator/node/implementation/apply_ruling.rb +44 -0
- data/lib/lite/validation/validator/node/implementation/dig.rb +38 -0
- data/lib/lite/validation/validator/node/implementation/helpers/call_foreign.rb +31 -0
- data/lib/lite/validation/validator/node/implementation/helpers/with_result.rb +23 -0
- data/lib/lite/validation/validator/node/implementation/helpers/yield_strategy.rb +83 -0
- data/lib/lite/validation/validator/node/implementation/helpers/yield_validator.rb +49 -0
- data/lib/lite/validation/validator/node/implementation/identity.rb +90 -0
- data/lib/lite/validation/validator/node/implementation/iteration/iterator.rb +102 -0
- data/lib/lite/validation/validator/node/implementation/iteration.rb +46 -0
- data/lib/lite/validation/validator/node/implementation/navigation.rb +43 -0
- data/lib/lite/validation/validator/node/implementation/predication.rb +61 -0
- data/lib/lite/validation/validator/node/implementation/scoping/evaluator.rb +43 -0
- data/lib/lite/validation/validator/node/implementation/scoping.rb +43 -0
- data/lib/lite/validation/validator/node/implementation/validation.rb +64 -0
- data/lib/lite/validation/validator/node/implementation/wrap.rb +26 -0
- data/lib/lite/validation/validator/node/root.rb +60 -0
- data/lib/lite/validation/validator/node/suspended.rb +33 -0
- data/lib/lite/validation/validator/node.rb +12 -0
- data/lib/lite/validation/validator/option/none.rb +43 -0
- data/lib/lite/validation/validator/option/some/abstract.rb +29 -0
- data/lib/lite/validation/validator/option/some/complex/registry/abstract.rb +67 -0
- data/lib/lite/validation/validator/option/some/complex/registry/node.rb +47 -0
- data/lib/lite/validation/validator/option/some/complex/registry/root.rb +31 -0
- data/lib/lite/validation/validator/option/some/complex/registry.rb +32 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/abstract/iterable.rb +31 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/abstract/non_iterable.rb +27 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/abstract.rb +35 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/array.rb +41 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/hash.rb +40 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/object.rb +34 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers/tuple.rb +47 -0
- data/lib/lite/validation/validator/option/some/complex/wrappers.rb +5 -0
- data/lib/lite/validation/validator/option/some/complex.rb +24 -0
- data/lib/lite/validation/validator/option/some/dig.rb +34 -0
- data/lib/lite/validation/validator/option/some/simple.rb +23 -0
- data/lib/lite/validation/validator/option/some/singular.rb +29 -0
- data/lib/lite/validation/validator/option/some.rb +20 -0
- data/lib/lite/validation/validator/option.rb +20 -0
- data/lib/lite/validation/validator/predicate/abstract/variants.rb +23 -0
- data/lib/lite/validation/validator/predicate/foreign/adapter/input/single.rb +21 -0
- data/lib/lite/validation/validator/predicate/foreign/adapter/input/tuple.rb +21 -0
- data/lib/lite/validation/validator/predicate/foreign/adapter/input.rb +28 -0
- data/lib/lite/validation/validator/predicate/foreign/adapter/ruling/instance.rb +37 -0
- data/lib/lite/validation/validator/predicate/foreign/adapter/ruling.rb +26 -0
- data/lib/lite/validation/validator/predicate/foreign/engine.rb +27 -0
- data/lib/lite/validation/validator/predicate/foreign/variant.rb +33 -0
- data/lib/lite/validation/validator/predicate/foreign/variants.rb +46 -0
- data/lib/lite/validation/validator/predicate/native/builder.rb +72 -0
- data/lib/lite/validation/validator/predicate/native/definite.rb +19 -0
- data/lib/lite/validation/validator/predicate/native/instance.rb +41 -0
- data/lib/lite/validation/validator/predicate/native/optional.rb +34 -0
- data/lib/lite/validation/validator/predicate/registry.rb +47 -0
- data/lib/lite/validation/validator/predicate.rb +17 -0
- data/lib/lite/validation/validator/result/abstract/failure.rb +21 -0
- data/lib/lite/validation/validator/result/abstract/instance.rb +18 -0
- data/lib/lite/validation/validator/result/abstract/success.rb +17 -0
- data/lib/lite/validation/validator/result/abstract.rb +29 -0
- data/lib/lite/validation/validator/result/committed.rb +75 -0
- data/lib/lite/validation/validator/result/disputable/hash.rb +17 -0
- data/lib/lite/validation/validator/result/disputable/instance.rb +43 -0
- data/lib/lite/validation/validator/result/disputable/iterable/array.rb +23 -0
- data/lib/lite/validation/validator/result/disputable/iterable.rb +17 -0
- data/lib/lite/validation/validator/result/disputable/navigable.rb +35 -0
- data/lib/lite/validation/validator/result/disputable.rb +14 -0
- data/lib/lite/validation/validator/result/disputed/abstract/hash.rb +32 -0
- data/lib/lite/validation/validator/result/disputed/abstract/instance.rb +26 -0
- data/lib/lite/validation/validator/result/disputed/iterable/array.rb +42 -0
- data/lib/lite/validation/validator/result/disputed/iterable/hash.rb +38 -0
- data/lib/lite/validation/validator/result/disputed/iterable.rb +20 -0
- data/lib/lite/validation/validator/result/disputed/navigable.rb +59 -0
- data/lib/lite/validation/validator/result/disputed.rb +17 -0
- data/lib/lite/validation/validator/result/refuted.rb +78 -0
- data/lib/lite/validation/validator/result/valid/abstract/collect.rb +42 -0
- data/lib/lite/validation/validator/result/valid/abstract/commit.rb +25 -0
- data/lib/lite/validation/validator/result/valid/abstract/instance.rb +23 -0
- data/lib/lite/validation/validator/result/valid/iterable/array/abstract.rb +24 -0
- data/lib/lite/validation/validator/result/valid/iterable/array/tuples.rb +64 -0
- data/lib/lite/validation/validator/result/valid/iterable/array/values.rb +42 -0
- data/lib/lite/validation/validator/result/valid/iterable/hash.rb +46 -0
- data/lib/lite/validation/validator/result/valid/iterable.rb +33 -0
- data/lib/lite/validation/validator/result/valid/navigable.rb +68 -0
- data/lib/lite/validation/validator/result/valid.rb +21 -0
- data/lib/lite/validation/validator/result.rb +15 -0
- data/lib/lite/validation/validator/ruling/abstract/invalid.rb +59 -0
- data/lib/lite/validation/validator/ruling/abstract/valid.rb +23 -0
- data/lib/lite/validation/validator/ruling/abstract.rb +12 -0
- data/lib/lite/validation/validator/ruling/commit.rb +17 -0
- data/lib/lite/validation/validator/ruling/dispute.rb +21 -0
- data/lib/lite/validation/validator/ruling/invalidate.rb +32 -0
- data/lib/lite/validation/validator/ruling/pass.rb +19 -0
- data/lib/lite/validation/validator/ruling/refute.rb +21 -0
- data/lib/lite/validation/validator/ruling.rb +53 -0
- data/lib/lite/validation/validator/state/instance.rb +46 -0
- data/lib/lite/validation/validator/state/merge_strategy.rb +50 -0
- data/lib/lite/validation/validator/state/unwrap_strategy.rb +31 -0
- data/lib/lite/validation/validator/state.rb +21 -0
- data/lib/lite/validation/validator.rb +15 -0
- data/lib/lite/validation/version.rb +9 -0
- data/lib/lite/validation.rb +8 -0
- metadata +196 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../helpers/with_result'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Node
|
|
9
|
+
module Implementation
|
|
10
|
+
module Scoping
|
|
11
|
+
class Evaluator
|
|
12
|
+
def self.instance(validator, path)
|
|
13
|
+
new(validator, [path].freeze)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private_class_method :new
|
|
17
|
+
|
|
18
|
+
def initialize(validator, paths)
|
|
19
|
+
@validator = validator
|
|
20
|
+
@paths = paths
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def and(path, &block)
|
|
24
|
+
compound = self.class.send(:new, validator, [*paths, path])
|
|
25
|
+
return compound if block.nil?
|
|
26
|
+
|
|
27
|
+
compound.call(&block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :validator, :paths
|
|
31
|
+
|
|
32
|
+
def call(&block)
|
|
33
|
+
return validator unless paths.all? { validator.result.success_at?(*_1) }
|
|
34
|
+
|
|
35
|
+
Helpers::WithResult.with_result(validator, validator.send(:wrap, &block))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helpers/yield_validator'
|
|
4
|
+
require_relative 'scoping/evaluator'
|
|
5
|
+
require_relative 'dig'
|
|
6
|
+
require_relative 'wrap'
|
|
7
|
+
|
|
8
|
+
module Lite
|
|
9
|
+
module Validation
|
|
10
|
+
module Validator
|
|
11
|
+
module Node
|
|
12
|
+
module Implementation
|
|
13
|
+
module Scoping
|
|
14
|
+
include Dig
|
|
15
|
+
include Wrap
|
|
16
|
+
|
|
17
|
+
def critical(error_generator, &block)
|
|
18
|
+
dig do |_option, result|
|
|
19
|
+
child = child(nil, result, state: state.critical(self, error_generator))
|
|
20
|
+
updated, _meta = Helpers::YieldValidator.yield_validator(child, block)
|
|
21
|
+
updated.refuted? ? updated.with(fall_through: false) : updated
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def with_context(context, &block)
|
|
26
|
+
if block
|
|
27
|
+
result, _meta = wrap(state: state.with(context: context), &block)
|
|
28
|
+
Helpers::WithResult.with_result(self, result)
|
|
29
|
+
else
|
|
30
|
+
with(state: state.with(context: context))
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def with_valid(*path, &block)
|
|
35
|
+
with_valid = Evaluator.instance(self, path)
|
|
36
|
+
block.nil? ? with_valid : with_valid.call(&block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'dig'
|
|
4
|
+
require_relative '../suspended'
|
|
5
|
+
require_relative 'helpers/call_foreign'
|
|
6
|
+
require_relative 'helpers/yield_strategy'
|
|
7
|
+
|
|
8
|
+
module Lite
|
|
9
|
+
module Validation
|
|
10
|
+
module Validator
|
|
11
|
+
module Node
|
|
12
|
+
module Implementation
|
|
13
|
+
module Validation
|
|
14
|
+
# rubocop:disable Metrics/ParameterLists
|
|
15
|
+
def self.validate(coordinator, to_yield, result, context, commit, block)
|
|
16
|
+
updated = Helpers::CallForeign.call_foreign(result, coordinator) do
|
|
17
|
+
ruling = block.call(to_yield, context)
|
|
18
|
+
Validator::Ruling.apply(ruling.nil? ? Validator::Ruling::Pass() : ruling, result, coordinator)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
case commit
|
|
22
|
+
when false then updated
|
|
23
|
+
when true then updated.commit(to_yield)
|
|
24
|
+
else raise Error::Fatal, "Invalid commit argument. Expected boolean, got: #{commit.inspect}"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
# rubocop:enable Metrics/ParameterLists
|
|
28
|
+
|
|
29
|
+
def validate?(*path, from: nil, commit: false, &block)
|
|
30
|
+
from = Validator::Helpers::Path.expand_path(from || path, [])
|
|
31
|
+
return Suspended.new(:validate!, self, path, from, commit) if block.nil?
|
|
32
|
+
|
|
33
|
+
validate!(path, from, :skip, commit, block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate(*path, from: nil, commit: false, &block)
|
|
37
|
+
from = Validator::Helpers::Path.expand_path(from || path, [])
|
|
38
|
+
validate!(path, from, :refute, commit, block)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def validate!(path, from, strategy, commit, block)
|
|
44
|
+
strategy = Helpers::YieldStrategy.to_yield(strategy)
|
|
45
|
+
dig!(path, from) do |option, result|
|
|
46
|
+
updated = strategy.block_parameters(self, option, result) do |to_yield|
|
|
47
|
+
Validation.validate(
|
|
48
|
+
coordinator,
|
|
49
|
+
to_yield,
|
|
50
|
+
result,
|
|
51
|
+
context,
|
|
52
|
+
commit,
|
|
53
|
+
block
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
merge_strategy.transform_result(updated, self, path)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helpers/call_foreign'
|
|
4
|
+
require_relative 'helpers/yield_validator'
|
|
5
|
+
|
|
6
|
+
module Lite
|
|
7
|
+
module Validation
|
|
8
|
+
module Validator
|
|
9
|
+
module Node
|
|
10
|
+
module Implementation
|
|
11
|
+
module Wrap
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def wrap(**opts, &block)
|
|
15
|
+
Helpers::CallForeign.call_foreign(result, coordinator) do
|
|
16
|
+
proxy = opts.empty? ? self : child(nil, result, **opts)
|
|
17
|
+
|
|
18
|
+
Helpers::YieldValidator.ensure_valid_result!(proxy, block.call(proxy)).result
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../result'
|
|
4
|
+
require_relative '../state'
|
|
5
|
+
require_relative '../option'
|
|
6
|
+
|
|
7
|
+
require_relative 'abstract/instance'
|
|
8
|
+
require_relative 'abstract/leaf'
|
|
9
|
+
require_relative 'abstract/branch'
|
|
10
|
+
require_relative 'child'
|
|
11
|
+
|
|
12
|
+
module Lite
|
|
13
|
+
module Validation
|
|
14
|
+
module Validator
|
|
15
|
+
module Node
|
|
16
|
+
module Root
|
|
17
|
+
include Child::Parent
|
|
18
|
+
|
|
19
|
+
def self.initial(bare_value, coordinator, context: nil)
|
|
20
|
+
Leaf.instance(
|
|
21
|
+
nil,
|
|
22
|
+
[Node::Implementation::Identity.intent_id],
|
|
23
|
+
Option.some(bare_value),
|
|
24
|
+
Result.valid,
|
|
25
|
+
State.initial(coordinator, context: context)
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_result(coordinator: self.coordinator)
|
|
30
|
+
result.success? ? to_success(coordinator) : to_failure(coordinator)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def inspect
|
|
34
|
+
"#<Root::#{super}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def to_success(coordinator)
|
|
40
|
+
coordinator.success(result.success.some? ? result.success.value : option.value)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def to_failure(coordinator)
|
|
44
|
+
coordinator.failure(result.to_failure(coordinator))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class Leaf < Abstract::Instance
|
|
48
|
+
include Abstract::Leaf
|
|
49
|
+
include Root
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Branch < Abstract::Instance
|
|
53
|
+
include Abstract::Branch
|
|
54
|
+
include Root
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lite
|
|
4
|
+
module Validation
|
|
5
|
+
module Validator
|
|
6
|
+
module Node
|
|
7
|
+
class Suspended
|
|
8
|
+
def initialize(action, node, path, from, *args)
|
|
9
|
+
@action = action
|
|
10
|
+
@node = node
|
|
11
|
+
@path = path
|
|
12
|
+
@from = from
|
|
13
|
+
@args = args
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :action, :node, :path, :from, :args
|
|
17
|
+
|
|
18
|
+
def option(&block)
|
|
19
|
+
node.send(action, path, from, :yield_option, *args, block)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def some(&block)
|
|
23
|
+
node.send(action, path, from, :skip, *args, block)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def some_or_nil(&block)
|
|
27
|
+
node.send(action, path, from, :nullify, *args, block)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Lite
|
|
4
|
+
module Validation
|
|
5
|
+
module Validator
|
|
6
|
+
module Option
|
|
7
|
+
module None
|
|
8
|
+
def self.some?
|
|
9
|
+
false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.none?
|
|
13
|
+
true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.dig(*_path)
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.to_complex
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.some_or_nil
|
|
25
|
+
Option.some(nil)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.to_option(coordinator)
|
|
29
|
+
coordinator.none
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.iterable?
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.inspect
|
|
37
|
+
'#<Option::None>'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lite/data'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
class Abstract
|
|
11
|
+
Lite::Data.define(self, args: %i[value])
|
|
12
|
+
|
|
13
|
+
def some?
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def none?
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def inspect
|
|
22
|
+
"#<Option::Some value of #{value.class.name}>"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lite/data'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
module Complex
|
|
11
|
+
module Registry
|
|
12
|
+
class Abstract
|
|
13
|
+
Lite::Data.define(self, args: [:children])
|
|
14
|
+
|
|
15
|
+
private_class_method :new
|
|
16
|
+
|
|
17
|
+
def self.insert(node, children)
|
|
18
|
+
updated = insert_as_child(node, children)
|
|
19
|
+
return updated if updated
|
|
20
|
+
|
|
21
|
+
insert_as_parent(node, children)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.insert_as_child(node, children)
|
|
25
|
+
parents, unrelated = children.partition { |child| child.key >= node.key }
|
|
26
|
+
|
|
27
|
+
case parents.length
|
|
28
|
+
when 0 then nil
|
|
29
|
+
when 1
|
|
30
|
+
[parents.first.insert(node), *unrelated]
|
|
31
|
+
else
|
|
32
|
+
raise Error, "Multiple parents found for #{node.key}: #{parents.map(&:key).join(', ')}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.insert_as_parent(node, children)
|
|
37
|
+
descendants, unrelated = children.partition { |child| child.key < node.key }
|
|
38
|
+
|
|
39
|
+
if descendants.empty?
|
|
40
|
+
[*unrelated, node]
|
|
41
|
+
else
|
|
42
|
+
[node.with(children: descendants.freeze), *unrelated]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def insert(node)
|
|
47
|
+
with(children: self.class.insert(node, children).freeze)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def wrapper_for(key)
|
|
51
|
+
provider = children.find { |candidate| candidate.key >= key }
|
|
52
|
+
provider&.wrapper_for(key)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def describe_children(severity)
|
|
58
|
+
children.flat_map { |child| child.describe(severity) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'abstract'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
module Complex
|
|
11
|
+
module Registry
|
|
12
|
+
class Node < Abstract
|
|
13
|
+
Lite::Data.define(self, args: %i[key wrapper])
|
|
14
|
+
|
|
15
|
+
def self.instance(key, wrapper, children: [])
|
|
16
|
+
raise Error, "Key must be a class, got: #{key.inpect}" unless key.is_a?(Class)
|
|
17
|
+
|
|
18
|
+
new key, wrapper, children.freeze
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private_class_method :new
|
|
22
|
+
|
|
23
|
+
def insert(node)
|
|
24
|
+
if key > node.key
|
|
25
|
+
super
|
|
26
|
+
elsif key == node.key
|
|
27
|
+
with(wrapper: node.wrapper)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def wrapper_for(key)
|
|
32
|
+
return if self.key < key
|
|
33
|
+
|
|
34
|
+
super || wrapper
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def describe(severity)
|
|
38
|
+
["#{'--' * severity}#{key}:#{wrapper.class.name}", *describe_children(severity + 1)]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'abstract'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
module Complex
|
|
11
|
+
module Registry
|
|
12
|
+
class Root < Abstract
|
|
13
|
+
def self.instance(children: [])
|
|
14
|
+
new children.freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def insert(key, wrapper)
|
|
18
|
+
super(Node.instance(key, wrapper))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def describe
|
|
22
|
+
describe_children(0).join("\n")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'registry/root'
|
|
4
|
+
require_relative 'registry/node'
|
|
5
|
+
|
|
6
|
+
module Lite
|
|
7
|
+
module Validation
|
|
8
|
+
module Validator
|
|
9
|
+
module Option
|
|
10
|
+
module Some
|
|
11
|
+
module Complex
|
|
12
|
+
module Registry
|
|
13
|
+
def self.register(key, wrapper)
|
|
14
|
+
@root = root.insert(key, wrapper)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.root
|
|
18
|
+
@root ||= Root.instance
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.wrapper_for(key)
|
|
22
|
+
root.wrapper_for(key)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private_constant :Abstract, :Root, :Node
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../abstract'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
module Complex
|
|
11
|
+
module Wrappers
|
|
12
|
+
module Abstract
|
|
13
|
+
class Iterable < Some::Abstract
|
|
14
|
+
include Abstract
|
|
15
|
+
|
|
16
|
+
def iterable?
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def reduce
|
|
21
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../abstract'
|
|
4
|
+
|
|
5
|
+
module Lite
|
|
6
|
+
module Validation
|
|
7
|
+
module Validator
|
|
8
|
+
module Option
|
|
9
|
+
module Some
|
|
10
|
+
module Complex
|
|
11
|
+
module Wrappers
|
|
12
|
+
module Abstract
|
|
13
|
+
class NonIterable < Some::Abstract
|
|
14
|
+
include Abstract
|
|
15
|
+
|
|
16
|
+
def iterable?
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../../../../error'
|
|
4
|
+
require_relative '../../abstract'
|
|
5
|
+
require_relative '../registry'
|
|
6
|
+
|
|
7
|
+
module Lite
|
|
8
|
+
module Validation
|
|
9
|
+
module Validator
|
|
10
|
+
module Option
|
|
11
|
+
module Some
|
|
12
|
+
module Complex
|
|
13
|
+
module Wrappers
|
|
14
|
+
module Abstract
|
|
15
|
+
class InvalidAccess < Error::Fatal; end
|
|
16
|
+
|
|
17
|
+
def fetch(_key)
|
|
18
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def iterable?
|
|
22
|
+
raise NotImplementedError, "#{self.class.name}##{__method__} unimplemented"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_complex
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'abstract/iterable'
|
|
4
|
+
require_relative '../../singular'
|
|
5
|
+
|
|
6
|
+
module Lite
|
|
7
|
+
module Validation
|
|
8
|
+
module Validator
|
|
9
|
+
module Option
|
|
10
|
+
module Some
|
|
11
|
+
module Complex
|
|
12
|
+
module Wrappers
|
|
13
|
+
class Array < Abstract::Iterable
|
|
14
|
+
include Singular
|
|
15
|
+
|
|
16
|
+
def fetch(index)
|
|
17
|
+
raise InvalidAccess, "Invalid index to array: #{index}" unless index.is_a?(Integer)
|
|
18
|
+
return Option.none unless (0...value.length).include?(index)
|
|
19
|
+
|
|
20
|
+
Option.some(value[index])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def reduce(initial_state, &block)
|
|
24
|
+
value.lazy
|
|
25
|
+
.each_with_index
|
|
26
|
+
.reduce(initial_state, &block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inspect
|
|
30
|
+
"#<Option::Some::Array length=#{value.length}>"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Registry.register(::Array, self)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|