dry-logic 0.6.1 → 1.0.6

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