dry-logic 1.0.5 → 1.1.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 +4 -4
- data/CHANGELOG.md +122 -26
- data/LICENSE +1 -1
- data/README.md +12 -14
- data/dry-logic.gemspec +26 -20
- data/lib/dry-logic.rb +1 -1
- data/lib/dry/logic.rb +2 -2
- data/lib/dry/logic/evaluator.rb +1 -1
- data/lib/dry/logic/operations.rb +11 -11
- data/lib/dry/logic/operations/abstract.rb +3 -3
- data/lib/dry/logic/operations/and.rb +3 -3
- data/lib/dry/logic/operations/attr.rb +1 -1
- data/lib/dry/logic/operations/binary.rb +4 -3
- data/lib/dry/logic/operations/check.rb +4 -4
- data/lib/dry/logic/operations/each.rb +2 -2
- data/lib/dry/logic/operations/implication.rb +2 -2
- data/lib/dry/logic/operations/key.rb +3 -3
- data/lib/dry/logic/operations/negation.rb +2 -2
- data/lib/dry/logic/operations/or.rb +2 -2
- data/lib/dry/logic/operations/set.rb +3 -3
- data/lib/dry/logic/operations/unary.rb +1 -1
- data/lib/dry/logic/operations/xor.rb +2 -2
- data/lib/dry/logic/operators.rb +4 -4
- data/lib/dry/logic/predicates.rb +32 -7
- data/lib/dry/logic/result.rb +2 -2
- data/lib/dry/logic/rule.rb +8 -8
- data/lib/dry/logic/rule/interface.rb +32 -37
- data/lib/dry/logic/rule/predicate.rb +3 -3
- data/lib/dry/logic/rule_compiler.rb +3 -3
- data/lib/dry/logic/version.rb +1 -1
- metadata +17 -166
- data/.codeclimate.yml +0 -12
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
- data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
- data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
- data/.github/workflows/ci.yml +0 -70
- data/.github/workflows/docsite.yml +0 -34
- data/.github/workflows/sync_configs.yml +0 -34
- data/.gitignore +0 -9
- data/.rspec +0 -4
- data/.rubocop.yml +0 -89
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -16
- data/Rakefile +0 -14
- data/benchmarks/rule_application.rb +0 -30
- data/benchmarks/setup.rb +0 -13
- data/bin/console +0 -11
- data/docsite/source/index.html.md +0 -54
- data/docsite/source/operations.html.md +0 -62
- data/docsite/source/predicates.html.md +0 -102
- data/examples/basic.rb +0 -16
- data/spec/integration/result_spec.rb +0 -61
- data/spec/integration/rule_spec.rb +0 -55
- data/spec/shared/predicates.rb +0 -59
- data/spec/shared/rule.rb +0 -74
- data/spec/spec_helper.rb +0 -30
- data/spec/support/mutant.rb +0 -11
- data/spec/unit/operations/and_spec.rb +0 -70
- data/spec/unit/operations/attr_spec.rb +0 -29
- data/spec/unit/operations/check_spec.rb +0 -51
- data/spec/unit/operations/each_spec.rb +0 -49
- data/spec/unit/operations/implication_spec.rb +0 -32
- data/spec/unit/operations/key_spec.rb +0 -135
- data/spec/unit/operations/negation_spec.rb +0 -51
- data/spec/unit/operations/or_spec.rb +0 -75
- data/spec/unit/operations/set_spec.rb +0 -43
- data/spec/unit/operations/xor_spec.rb +0 -63
- data/spec/unit/predicates/array_spec.rb +0 -43
- data/spec/unit/predicates/attr_spec.rb +0 -31
- data/spec/unit/predicates/bool_spec.rb +0 -36
- data/spec/unit/predicates/bytesize_spec.rb +0 -48
- data/spec/unit/predicates/case_spec.rb +0 -35
- data/spec/unit/predicates/date_spec.rb +0 -33
- data/spec/unit/predicates/date_time_spec.rb +0 -33
- data/spec/unit/predicates/decimal_spec.rb +0 -34
- data/spec/unit/predicates/empty_spec.rb +0 -40
- data/spec/unit/predicates/eql_spec.rb +0 -23
- data/spec/unit/predicates/even_spec.rb +0 -33
- data/spec/unit/predicates/excluded_from_spec.rb +0 -37
- data/spec/unit/predicates/excludes_spec.rb +0 -58
- data/spec/unit/predicates/false_spec.rb +0 -37
- data/spec/unit/predicates/filled_spec.rb +0 -40
- data/spec/unit/predicates/float_spec.rb +0 -33
- data/spec/unit/predicates/format_spec.rb +0 -31
- data/spec/unit/predicates/gt_spec.rb +0 -42
- data/spec/unit/predicates/gteq_spec.rb +0 -42
- data/spec/unit/predicates/hash_spec.rb +0 -40
- data/spec/unit/predicates/included_in_spec.rb +0 -37
- data/spec/unit/predicates/includes_spec.rb +0 -24
- data/spec/unit/predicates/int_spec.rb +0 -36
- data/spec/unit/predicates/key_spec.rb +0 -31
- data/spec/unit/predicates/lt_spec.rb +0 -42
- data/spec/unit/predicates/lteq_spec.rb +0 -42
- data/spec/unit/predicates/max_bytesize_spec.rb +0 -39
- data/spec/unit/predicates/max_size_spec.rb +0 -51
- data/spec/unit/predicates/min_bytesize_spec.rb +0 -39
- data/spec/unit/predicates/min_size_spec.rb +0 -51
- data/spec/unit/predicates/none_spec.rb +0 -30
- data/spec/unit/predicates/not_eql_spec.rb +0 -23
- data/spec/unit/predicates/number_spec.rb +0 -39
- data/spec/unit/predicates/odd_spec.rb +0 -33
- data/spec/unit/predicates/respond_to_spec.rb +0 -31
- data/spec/unit/predicates/size_spec.rb +0 -57
- data/spec/unit/predicates/str_spec.rb +0 -34
- data/spec/unit/predicates/time_spec.rb +0 -33
- data/spec/unit/predicates/true_spec.rb +0 -37
- data/spec/unit/predicates/type_spec.rb +0 -37
- data/spec/unit/predicates/uuid_v4_spec.rb +0 -29
- data/spec/unit/predicates_spec.rb +0 -25
- data/spec/unit/rule/predicate_spec.rb +0 -55
- data/spec/unit/rule_compiler_spec.rb +0 -129
- data/spec/unit/rule_spec.rb +0 -213
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::Predicates do
|
6
|
-
describe '#str?' do
|
7
|
-
let(:predicate_name) { :str? }
|
8
|
-
|
9
|
-
context 'when value is a string' do
|
10
|
-
let(:arguments_list) do
|
11
|
-
[
|
12
|
-
[''],
|
13
|
-
['John']
|
14
|
-
]
|
15
|
-
end
|
16
|
-
|
17
|
-
it_behaves_like 'a passing predicate'
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'with value is not a string' do
|
21
|
-
let(:arguments_list) do
|
22
|
-
[
|
23
|
-
[[]],
|
24
|
-
[{}],
|
25
|
-
[nil],
|
26
|
-
[:symbol],
|
27
|
-
[String]
|
28
|
-
]
|
29
|
-
end
|
30
|
-
|
31
|
-
it_behaves_like 'a failing predicate'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::Predicates do
|
6
|
-
describe '#time?' do
|
7
|
-
let(:predicate_name) { :time? }
|
8
|
-
|
9
|
-
context 'when value is a time' do
|
10
|
-
let(:arguments_list) do
|
11
|
-
[[Time.now]]
|
12
|
-
end
|
13
|
-
|
14
|
-
it_behaves_like 'a passing predicate'
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'with value is not an integer' do
|
18
|
-
let(:arguments_list) do
|
19
|
-
[
|
20
|
-
[''],
|
21
|
-
[[]],
|
22
|
-
[{}],
|
23
|
-
[nil],
|
24
|
-
[:symbol],
|
25
|
-
[String],
|
26
|
-
[1]
|
27
|
-
]
|
28
|
-
end
|
29
|
-
|
30
|
-
it_behaves_like 'a failing predicate'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::Predicates do
|
6
|
-
describe '#true?' do
|
7
|
-
let(:predicate_name) { :true? }
|
8
|
-
|
9
|
-
context 'when value is true' do
|
10
|
-
let(:arguments_list) do
|
11
|
-
[[true]]
|
12
|
-
end
|
13
|
-
|
14
|
-
it_behaves_like 'a passing predicate'
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'with value is not true' do
|
18
|
-
let(:arguments_list) do
|
19
|
-
[
|
20
|
-
[false],
|
21
|
-
[''],
|
22
|
-
[[]],
|
23
|
-
[{}],
|
24
|
-
[nil],
|
25
|
-
[:symbol],
|
26
|
-
[String],
|
27
|
-
[1],
|
28
|
-
[0],
|
29
|
-
['true'],
|
30
|
-
['false']
|
31
|
-
]
|
32
|
-
end
|
33
|
-
|
34
|
-
it_behaves_like 'a failing predicate'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::Predicates do
|
6
|
-
describe '#type?' do
|
7
|
-
let(:predicate_name) { :type? }
|
8
|
-
|
9
|
-
context 'when value has a correct type' do
|
10
|
-
let(:arguments_list) do
|
11
|
-
[[TrueClass, true]]
|
12
|
-
end
|
13
|
-
|
14
|
-
it_behaves_like 'a passing predicate'
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'with value is not true' do
|
18
|
-
let(:arguments_list) do
|
19
|
-
[
|
20
|
-
[TrueClass, false],
|
21
|
-
[TrueClass, ''],
|
22
|
-
[TrueClass, []],
|
23
|
-
[TrueClass, {}],
|
24
|
-
[TrueClass, nil],
|
25
|
-
[TrueClass, :symbol],
|
26
|
-
[TrueClass, String],
|
27
|
-
[TrueClass, 1],
|
28
|
-
[TrueClass, 0],
|
29
|
-
[TrueClass, 'true'],
|
30
|
-
[TrueClass, 'false']
|
31
|
-
]
|
32
|
-
end
|
33
|
-
|
34
|
-
it_behaves_like 'a failing predicate'
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::Predicates do
|
6
|
-
describe '#uuid_v4?' do
|
7
|
-
let(:predicate_name) { :uuid_v4? }
|
8
|
-
|
9
|
-
context 'when value is a valid V4 UUID' do
|
10
|
-
let(:arguments_list) do
|
11
|
-
[['f2d26c57-e07c-4416-a749-57e937930e04']]
|
12
|
-
end
|
13
|
-
|
14
|
-
it_behaves_like 'a passing predicate'
|
15
|
-
end
|
16
|
-
|
17
|
-
context 'with value is not a valid V4 UUID' do
|
18
|
-
let(:arguments_list) do
|
19
|
-
[
|
20
|
-
['f2d26c57-e07c-3416-a749-57e937930e04'], # wrong version number (3, not 4)
|
21
|
-
['20633928-6a07-11e9-a923-1681be663d3e'], # UUID V1
|
22
|
-
['not-a-uuid-at-all']
|
23
|
-
]
|
24
|
-
end
|
25
|
-
|
26
|
-
it_behaves_like 'a failing predicate'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/predicates'
|
4
|
-
|
5
|
-
RSpec.describe Predicates do
|
6
|
-
it 'can be included in another module' do
|
7
|
-
mod = Module.new { include Predicates }
|
8
|
-
|
9
|
-
expect(mod[:key?]).to be_a(Method)
|
10
|
-
end
|
11
|
-
|
12
|
-
describe '.predicate' do
|
13
|
-
it 'defines a predicate method' do
|
14
|
-
mod = Module.new {
|
15
|
-
include Predicates
|
16
|
-
|
17
|
-
predicate(:test?) do |foo|
|
18
|
-
true
|
19
|
-
end
|
20
|
-
}
|
21
|
-
|
22
|
-
expect(mod.test?('arg')).to be(true)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Rule::Predicate do
|
4
|
-
subject(:rule) { Rule::Predicate.build(predicate) }
|
5
|
-
|
6
|
-
let(:predicate) { str? }
|
7
|
-
|
8
|
-
include_context 'predicates'
|
9
|
-
|
10
|
-
it_behaves_like Rule
|
11
|
-
|
12
|
-
describe '#name' do
|
13
|
-
it 'returns predicate identifier' do
|
14
|
-
expect(rule.name).to be(:str?)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe '#to_ast' do
|
19
|
-
context 'without a result' do
|
20
|
-
it 'returns rule ast' do
|
21
|
-
expect(rule.to_ast).to eql([:predicate, [:str?, [[:input, Undefined]]]])
|
22
|
-
end
|
23
|
-
|
24
|
-
it 'returns :failure with an id' do
|
25
|
-
email = rule.with(id: :email)
|
26
|
-
|
27
|
-
expect(email.(11).to_ast).to eql([:failure, [:email, [:predicate, [:str?, [[:input, 11]]]]]])
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'with a result' do
|
32
|
-
it 'returns success' do
|
33
|
-
expect(rule.('foo')).to be_success
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'returns failure ast' do
|
37
|
-
expect(rule.(5).to_ast).to eql([:predicate, [:str?, [[:input, 5]]]])
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context 'with a zero-arity predicate' do
|
42
|
-
let(:predicate) { Module.new { def self.test?; true; end }.method(:test?) }
|
43
|
-
|
44
|
-
it 'returns ast' do
|
45
|
-
expect(rule.to_ast).to eql([:predicate, [:test?, []]])
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
describe '#to_s' do
|
51
|
-
it 'returns string representation' do
|
52
|
-
expect(rule.curry('foo').to_s).to eql('str?("foo")')
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'dry/logic/rule_compiler'
|
4
|
-
|
5
|
-
RSpec.describe Dry::Logic::RuleCompiler, '#call' do
|
6
|
-
subject(:compiler) { RuleCompiler.new(predicates) }
|
7
|
-
|
8
|
-
let(:predicates) {
|
9
|
-
{ key?: predicate,
|
10
|
-
attr?: predicate,
|
11
|
-
filled?: predicate,
|
12
|
-
gt?: predicate,
|
13
|
-
one: predicate }
|
14
|
-
}
|
15
|
-
|
16
|
-
let(:predicate) { double(:predicate, name: :test?, arity: 2).as_null_object }
|
17
|
-
|
18
|
-
let(:rule) { Rule::Predicate.build(predicate) }
|
19
|
-
let(:key_op) { Operations::Key.new(rule, name: :email) }
|
20
|
-
let(:attr_op) { Operations::Attr.new(rule, name: :email) }
|
21
|
-
let(:check_op) { Operations::Check.new(rule, keys: [:email]) }
|
22
|
-
let(:not_key_op) { Operations::Negation.new(key_op) }
|
23
|
-
let(:and_op) { key_op.curry(:email) & rule }
|
24
|
-
let(:or_op) { key_op.curry(:email) | rule }
|
25
|
-
let(:xor_op) { key_op.curry(:email) ^ rule }
|
26
|
-
let(:set_op) { Operations::Set.new(rule) }
|
27
|
-
let(:each_op) { Operations::Each.new(rule) }
|
28
|
-
|
29
|
-
it 'compiles key rules' do
|
30
|
-
ast = [[:key, [:email, [:predicate, [:filled?, [[:input, Undefined]]]]]]]
|
31
|
-
|
32
|
-
rules = compiler.(ast)
|
33
|
-
|
34
|
-
expect(rules).to eql([key_op])
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'compiles attr rules' do
|
38
|
-
ast = [[:attr, [:email, [:predicate, [:filled?, [[:input, Undefined]]]]]]]
|
39
|
-
|
40
|
-
rules = compiler.(ast)
|
41
|
-
|
42
|
-
expect(rules).to eql([attr_op])
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'compiles check rules' do
|
46
|
-
ast = [[:check, [[:email], [:predicate, [:filled?, [[:input, Undefined]]]]]]]
|
47
|
-
|
48
|
-
rules = compiler.(ast)
|
49
|
-
|
50
|
-
expect(rules).to eql([check_op])
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'compiles attr rules' do
|
54
|
-
ast = [[:attr, [:email, [:predicate, [:filled?, [[:input, Undefined]]]]]]]
|
55
|
-
|
56
|
-
rules = compiler.(ast)
|
57
|
-
|
58
|
-
expect(rules).to eql([attr_op])
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'compiles negated rules' do
|
62
|
-
ast = [[:not, [:key, [:email, [:predicate, [:filled?, [[:input, Undefined]]]]]]]]
|
63
|
-
|
64
|
-
rules = compiler.(ast)
|
65
|
-
|
66
|
-
expect(rules).to eql([not_key_op])
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'compiles and rules' do
|
70
|
-
ast = [
|
71
|
-
[
|
72
|
-
:and, [
|
73
|
-
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, Undefined]]]]]],
|
74
|
-
[:predicate, [:filled?, [[:input, Undefined]]]]
|
75
|
-
]
|
76
|
-
]
|
77
|
-
]
|
78
|
-
|
79
|
-
rules = compiler.(ast)
|
80
|
-
|
81
|
-
expect(rules).to eql([and_op])
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'compiles or rules' do
|
85
|
-
ast = [
|
86
|
-
[
|
87
|
-
:or, [
|
88
|
-
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, Undefined]]]]]],
|
89
|
-
[:predicate, [:filled?, [[:input, Undefined]]]]
|
90
|
-
]
|
91
|
-
]
|
92
|
-
]
|
93
|
-
|
94
|
-
rules = compiler.(ast)
|
95
|
-
|
96
|
-
expect(rules).to eql([or_op])
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'compiles exclusive or rules' do
|
100
|
-
ast = [
|
101
|
-
[
|
102
|
-
:xor, [
|
103
|
-
[:key, [:email, [:predicate, [:key?, [[:name, :email], [:input, Undefined]]]]]],
|
104
|
-
[:predicate, [:filled?, [[:input, Undefined]]]]
|
105
|
-
]
|
106
|
-
]
|
107
|
-
]
|
108
|
-
|
109
|
-
rules = compiler.(ast)
|
110
|
-
|
111
|
-
expect(rules).to eql([xor_op])
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'compiles set rules' do
|
115
|
-
ast = [[:set, [[:predicate, [:filled?, [[:input, nil]]]]]]]
|
116
|
-
|
117
|
-
rules = compiler.(ast)
|
118
|
-
|
119
|
-
expect(rules).to eql([set_op])
|
120
|
-
end
|
121
|
-
|
122
|
-
it 'compiles each rules' do
|
123
|
-
ast = [[:each, [:predicate, [:filled?, [[:input, nil]]]]]]
|
124
|
-
|
125
|
-
rules = compiler.(ast)
|
126
|
-
|
127
|
-
expect(rules).to eql([each_op])
|
128
|
-
end
|
129
|
-
end
|
data/spec/unit/rule_spec.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
RSpec.describe Dry::Logic::Rule do
|
4
|
-
subject(:rule) { Rule.build(predicate, **options) }
|
5
|
-
|
6
|
-
let(:predicate) { -> { true } }
|
7
|
-
let(:options) { {} }
|
8
|
-
|
9
|
-
let(:schema) do
|
10
|
-
Class.new do
|
11
|
-
define_method(:class, Kernel.instance_method(:class))
|
12
|
-
|
13
|
-
def method_missing(m, *)
|
14
|
-
if m.to_s.end_with?('?')
|
15
|
-
self.class.new
|
16
|
-
else
|
17
|
-
super
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_proc
|
22
|
-
-> value { value }
|
23
|
-
end
|
24
|
-
|
25
|
-
def arity
|
26
|
-
1
|
27
|
-
end
|
28
|
-
|
29
|
-
def parameters
|
30
|
-
[[:req, :value]]
|
31
|
-
end
|
32
|
-
end.new
|
33
|
-
end
|
34
|
-
|
35
|
-
it_behaves_like Dry::Logic::Rule
|
36
|
-
|
37
|
-
describe '.new' do
|
38
|
-
it 'accepts an :id' do
|
39
|
-
expect(Rule.build(predicate, id: :check_num).id).to be(:check_num)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe 'with a function returning truthy value' do
|
44
|
-
it 'is successful for valid input' do
|
45
|
-
expect(Rule.build(-> val { val }).('true')).to be_success
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'is not successful for invalid input' do
|
49
|
-
expect(Rule.build(-> val { val }).(nil)).to be_failure
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe '#ast' do
|
54
|
-
it 'returns predicate node with :id' do
|
55
|
-
expect(Rule.build(-> value { true }).with(id: :email?).ast('oops')).to eql(
|
56
|
-
[:predicate, [:email?, [[:value, 'oops']]]]
|
57
|
-
)
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'returns predicate node with undefined args' do
|
61
|
-
expect(Rule.build(-> value { true }).with(id: :email?).ast).to eql(
|
62
|
-
[:predicate, [:email?, [[:value, Undefined]]]]
|
63
|
-
)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
describe '#type' do
|
68
|
-
it 'returns rule type' do
|
69
|
-
expect(rule.type).to be(:rule)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe '#bind' do
|
74
|
-
let(:bound) { rule.with(id: :bound).bind(object) }
|
75
|
-
|
76
|
-
context 'with an unbound method' do
|
77
|
-
let(:predicate) { klass.instance_method(:test?) }
|
78
|
-
let(:klass) { Class.new { def test?; true; end } }
|
79
|
-
let(:object) { klass.new }
|
80
|
-
|
81
|
-
it 'returns a new rule with its predicate bound to a specific object' do
|
82
|
-
expect(bound.()).to be_success
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'carries id' do
|
86
|
-
expect(bound.id).to be(:bound)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'with an arbitrary block' do
|
91
|
-
let(:predicate) { -> value { value == expected } }
|
92
|
-
let(:object) { Class.new { def expected; 'test'; end }.new }
|
93
|
-
|
94
|
-
it 'returns a new with its predicate executed in the context of the provided object' do
|
95
|
-
expect(bound.('test')).to be_success
|
96
|
-
expect(bound.('oops')).to be_failure
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'carries id' do
|
100
|
-
expect(bound.id).to be(:bound)
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'stores arity' do
|
104
|
-
expect(bound.options[:arity]).to be(rule.arity)
|
105
|
-
end
|
106
|
-
|
107
|
-
it 'stores parameters' do
|
108
|
-
expect(bound.options[:parameters]).to eql(rule.parameters)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context 'with a schema instance' do
|
113
|
-
let(:object) { schema }
|
114
|
-
let(:predicate) { schema }
|
115
|
-
|
116
|
-
it 'returns a new with its predicate executed in the context of the provided object' do
|
117
|
-
expect(bound.(true)).to be_success
|
118
|
-
expect(bound.(false)).to be_failure
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
describe '#eval_args' do
|
124
|
-
context 'with an unbound method' do
|
125
|
-
let(:options) { { args: [1, klass.instance_method(:num), :foo], arity: 3 } }
|
126
|
-
let(:klass) { Class.new { def num; 7; end } }
|
127
|
-
let(:object) { klass.new }
|
128
|
-
|
129
|
-
it 'evaluates args in the context of the provided object' do
|
130
|
-
expect(rule.eval_args(object).args).to eql([1, 7, :foo])
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
context 'with a schema instance' do
|
135
|
-
let(:options) { { args: [1, schema, :foo], arity: 3 } }
|
136
|
-
let(:object) { Object.new }
|
137
|
-
|
138
|
-
it 'returns a new with its predicate executed in the context of the provided object' do
|
139
|
-
expect(rule.eval_args(object).args).to eql([1, schema, :foo])
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
describe 'arity specialization' do
|
145
|
-
describe '0-arity rule' do
|
146
|
-
let(:options) { { args: [1], arity: 1 } }
|
147
|
-
let(:predicate) { :odd?.to_proc }
|
148
|
-
|
149
|
-
it 'generates interface with the right arity' do
|
150
|
-
expect(rule.method(:call).arity).to be_zero
|
151
|
-
expect(rule.method(:[]).arity).to be_zero
|
152
|
-
expect(rule[]).to be(true)
|
153
|
-
expect(rule.()).to be_success
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
describe '1-arity rule' do
|
158
|
-
let(:options) { { args: [1], arity: 2 } }
|
159
|
-
let(:predicate) { -> a, b { a + b } }
|
160
|
-
|
161
|
-
it 'generates interface with the right arity' do
|
162
|
-
expect(rule.method(:call).arity).to be(1)
|
163
|
-
expect(rule.method(:[]).arity).to be(1)
|
164
|
-
expect(rule[10]).to be(11)
|
165
|
-
expect(rule.(1)).to be_success
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
describe 'currying' do
|
170
|
-
let(:options) { { args: [], arity: 2 } }
|
171
|
-
let(:predicate) { -> a, b { a + b } }
|
172
|
-
let(:rule) { super().curry(1) }
|
173
|
-
|
174
|
-
it 'generates correct arity on currying' do
|
175
|
-
expect(rule.method(:call).arity).to be(1)
|
176
|
-
expect(rule.method(:[]).arity).to be(1)
|
177
|
-
expect(rule[10]).to be(11)
|
178
|
-
expect(rule.(1)).to be_success
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
describe 'arbitrary arity' do
|
183
|
-
let(:arity) { rand(1..20) }
|
184
|
-
let(:curried) { rand(arity) }
|
185
|
-
|
186
|
-
let(:options) { { args: [1] * curried, arity: arity } }
|
187
|
-
let(:predicate) { double(:predicate) }
|
188
|
-
|
189
|
-
it 'generates correct arity' do
|
190
|
-
expect(rule.method(:call).arity).to be(arity - curried)
|
191
|
-
expect(rule.method(:[]).arity).to be(arity - curried)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
describe '-1 arity' do
|
196
|
-
let(:options) { { args: [], arity: -1 } }
|
197
|
-
|
198
|
-
it 'accepts variable number of arguments' do
|
199
|
-
expect(rule.method(:call).arity).to be(-1)
|
200
|
-
expect(rule.method(:[]).arity).to be(-1)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
describe 'constants' do
|
205
|
-
let(:options) { { args: [], arity: 0 } }
|
206
|
-
|
207
|
-
it 'accepts variable number of arguments' do
|
208
|
-
expect(rule.method(:call).arity).to be(-1)
|
209
|
-
expect(rule.method(:[]).arity).to be(-1)
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|