dry-logic 0.6.1 → 1.0.6

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 (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