predicate 2.3.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/LICENSE.md +17 -19
  4. data/README.md +433 -0
  5. data/bin/g +2 -0
  6. data/lib/predicate.rb +26 -2
  7. data/lib/predicate/dsl.rb +138 -0
  8. data/lib/predicate/factory.rb +130 -33
  9. data/lib/predicate/grammar.rb +11 -2
  10. data/lib/predicate/grammar.sexp.yml +29 -0
  11. data/lib/predicate/nodes/${op_name}.rb.jeny +12 -0
  12. data/lib/predicate/nodes/and.rb +9 -0
  13. data/lib/predicate/nodes/binary_func.rb +20 -0
  14. data/lib/predicate/nodes/contradiction.rb +2 -7
  15. data/lib/predicate/nodes/dyadic_comp.rb +3 -5
  16. data/lib/predicate/nodes/empty.rb +14 -0
  17. data/lib/predicate/nodes/eq.rb +13 -6
  18. data/lib/predicate/nodes/expr.rb +9 -3
  19. data/lib/predicate/nodes/has_size.rb +14 -0
  20. data/lib/predicate/nodes/identifier.rb +1 -3
  21. data/lib/predicate/nodes/in.rb +9 -8
  22. data/lib/predicate/nodes/intersect.rb +3 -23
  23. data/lib/predicate/nodes/literal.rb +1 -3
  24. data/lib/predicate/nodes/match.rb +1 -21
  25. data/lib/predicate/nodes/nadic_bool.rb +1 -3
  26. data/lib/predicate/nodes/native.rb +1 -3
  27. data/lib/predicate/nodes/not.rb +1 -3
  28. data/lib/predicate/nodes/opaque.rb +1 -3
  29. data/lib/predicate/nodes/qualified_identifier.rb +2 -4
  30. data/lib/predicate/nodes/set_op.rb +26 -0
  31. data/lib/predicate/nodes/subset.rb +11 -0
  32. data/lib/predicate/nodes/superset.rb +11 -0
  33. data/lib/predicate/nodes/tautology.rb +6 -7
  34. data/lib/predicate/nodes/unary_func.rb +16 -0
  35. data/lib/predicate/nodes/var.rb +46 -0
  36. data/lib/predicate/processors.rb +1 -0
  37. data/lib/predicate/processors/qualifier.rb +4 -0
  38. data/lib/predicate/processors/renamer.rb +4 -0
  39. data/lib/predicate/processors/to_s.rb +28 -0
  40. data/lib/predicate/processors/unqualifier.rb +21 -0
  41. data/lib/predicate/sequel/to_sequel.rb +4 -1
  42. data/lib/predicate/sugar.rb +47 -0
  43. data/lib/predicate/version.rb +1 -1
  44. data/spec/dsl/test_dsl.rb +204 -0
  45. data/spec/dsl/test_evaluate.rb +65 -0
  46. data/spec/dsl/test_respond_to_missing.rb +35 -0
  47. data/spec/dsl/test_to_skake_case.rb +38 -0
  48. data/spec/factory/shared/a_comparison_factory_method.rb +1 -0
  49. data/spec/factory/test_${op_name}.rb.jeny +12 -0
  50. data/spec/factory/test_empty.rb +11 -0
  51. data/spec/factory/test_has_size.rb +11 -0
  52. data/spec/factory/test_match.rb +1 -0
  53. data/spec/factory/test_set_ops.rb +18 -0
  54. data/spec/factory/test_var.rb +22 -0
  55. data/spec/factory/test_vars.rb +27 -0
  56. data/spec/nodes/${op_name}.jeny/test_evaluate.rb.jeny +19 -0
  57. data/spec/nodes/empty/test_evaluate.rb +42 -0
  58. data/spec/nodes/eq/test_and.rb +6 -0
  59. data/spec/nodes/has_size/test_evaluate.rb +44 -0
  60. data/spec/nodes/qualified_identifier/test_and_split.rb +1 -1
  61. data/spec/nodes/qualified_identifier/test_free_variables.rb +1 -1
  62. data/spec/predicate/test_and_split.rb +18 -0
  63. data/spec/predicate/test_attr_split.rb +18 -0
  64. data/spec/predicate/test_bool_and.rb +11 -0
  65. data/spec/predicate/test_constant_variables.rb +24 -2
  66. data/spec/predicate/test_constants.rb +24 -0
  67. data/spec/predicate/test_evaluate.rb +205 -3
  68. data/spec/predicate/test_free_variables.rb +1 -1
  69. data/spec/predicate/test_to_hash.rb +40 -0
  70. data/spec/predicate/test_to_s.rb +37 -0
  71. data/spec/predicate/test_unqualify.rb +18 -0
  72. data/spec/sequel/test_to_sequel.rb +16 -0
  73. data/spec/shared/a_predicate.rb +30 -0
  74. data/spec/spec_helper.rb +1 -0
  75. data/spec/test_predicate.rb +68 -33
  76. data/spec/test_readme.rb +80 -0
  77. data/spec/test_sugar.rb +48 -0
  78. data/tasks/test.rake +3 -3
  79. metadata +43 -13
  80. data/spec/factory/test_between.rb +0 -12
  81. data/spec/factory/test_intersect.rb +0 -12
@@ -38,5 +38,16 @@ class Predicate
38
38
  end
39
39
  end
40
40
 
41
+ context 'when using qualified names' do
42
+ let(:right) { Predicate.coerce(x: 2) }
43
+
44
+ it 'does not mix predicates' do
45
+ l = left.qualify(:p1)
46
+ r = right.qualify(:p2)
47
+ expect(l & r).not_to eql(l)
48
+ end
49
+
50
+ end
51
+
41
52
  end
42
53
  end
@@ -37,14 +37,36 @@ class Predicate
37
37
  describe "on an intersect with one value" do
38
38
  let(:p){ Predicate.intersect(:x, [2]) }
39
39
 
40
- # TODO: is that correct?
41
40
  it{ expect(subject).to eql([]) }
42
41
  end
43
42
 
44
43
  describe "on an intersect with a placeholder" do
45
44
  let(:p){ Predicate.intersect(:x, Predicate.placeholder) }
46
45
 
47
- # TODO: is that correct?
46
+ it{ expect(subject).to eql([]) }
47
+ end
48
+
49
+ describe "on an subset with one value" do
50
+ let(:p){ Predicate.subset(:x, [2]) }
51
+
52
+ it{ expect(subject).to eql([]) }
53
+ end
54
+
55
+ describe "on an subset with a placeholder" do
56
+ let(:p){ Predicate.subset(:x, Predicate.placeholder) }
57
+
58
+ it{ expect(subject).to eql([]) }
59
+ end
60
+
61
+ describe "on an superset with one value" do
62
+ let(:p){ Predicate.superset(:x, [2]) }
63
+
64
+ it{ expect(subject).to eql([]) }
65
+ end
66
+
67
+ describe "on an superset with a placeholder" do
68
+ let(:p){ Predicate.superset(:x, Predicate.placeholder) }
69
+
48
70
  it{ expect(subject).to eql([]) }
49
71
  end
50
72
 
@@ -119,6 +119,30 @@ class Predicate
119
119
  it{ should eq({}) }
120
120
  end
121
121
 
122
+ context "on subset" do
123
+ let(:pred){ p.subset(:x, [4,8]) }
124
+
125
+ it{ should eq({}) }
126
+ end
127
+
128
+ context "on subset with placeholder" do
129
+ let(:pred){ p.subset(:x, p.placeholder) }
130
+
131
+ it{ should eq({}) }
132
+ end
133
+
134
+ context "on superset" do
135
+ let(:pred){ p.superset(:x, [4,8]) }
136
+
137
+ it{ should eq({}) }
138
+ end
139
+
140
+ context "on superset with placeholder" do
141
+ let(:pred){ p.superset(:x, p.placeholder) }
142
+
143
+ it{ should eq({}) }
144
+ end
145
+
122
146
  context "on or (two eqs)" do
123
147
  let(:pred){ p.eq(:x, 2) | p.eq(:y, 4) }
124
148
 
@@ -40,7 +40,7 @@ class Predicate
40
40
 
41
41
  context 'on a factored predicate' do
42
42
  let(:predicate){
43
- Predicate.new(Factory.lte(:x => 2))
43
+ Predicate.lte(:x => 2)
44
44
  }
45
45
 
46
46
  describe "on x == 2" do
@@ -80,7 +80,7 @@ class Predicate
80
80
  end
81
81
  end
82
82
 
83
- context 'on an intersect predicate' do
83
+ context 'on an intersect predicate, with array literal' do
84
84
  let(:predicate){
85
85
  Predicate.intersect(:x, [8,9])
86
86
  }
@@ -98,6 +98,168 @@ class Predicate
98
98
  end
99
99
  end
100
100
 
101
+ context 'on an intersect predicate, with two variables' do
102
+ let(:predicate){
103
+ Predicate.intersect(:x, :y)
104
+ }
105
+
106
+ describe "on x == [2]" do
107
+ let(:scope){ { :x => [2], :y => [8,9] } }
108
+
109
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
110
+ end
111
+
112
+ describe "on x == [9,12]" do
113
+ let(:scope){ { :x => [9,12], :y => [8,9] } }
114
+
115
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
116
+ end
117
+ end
118
+
119
+ context 'on an subset predicate, with an array literal' do
120
+ let(:predicate){
121
+ Predicate.subset(:x, [8,9])
122
+ }
123
+
124
+ describe "on x == [2]" do
125
+ let(:scope){ { :x => [2] } }
126
+
127
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
128
+ end
129
+
130
+ describe "on x == []" do
131
+ let(:scope){ { :x => [] } }
132
+
133
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
134
+ end
135
+
136
+ describe "on x == [9]" do
137
+ let(:scope){ { :x => [9] } }
138
+
139
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
140
+ end
141
+
142
+ describe "on x == [8, 9]" do
143
+ let(:scope){ { :x => [8, 9] } }
144
+
145
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
146
+ end
147
+
148
+ describe "on x == [8, 9, 10]" do
149
+ let(:scope){ { :x => [8, 9, 19] } }
150
+
151
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
152
+ end
153
+ end
154
+
155
+ context 'on an subset predicate, with two variables' do
156
+ let(:predicate){
157
+ Predicate.subset(:x, :y)
158
+ }
159
+
160
+ describe "on x == [2]" do
161
+ let(:scope){ { :x => [2], :y => [8,9] } }
162
+
163
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
164
+ end
165
+
166
+ describe "on x == []" do
167
+ let(:scope){ { :x => [], :y => [8,9] } }
168
+
169
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
170
+ end
171
+
172
+ describe "on x == [9]" do
173
+ let(:scope){ { :x => [9], :y => [8,9] } }
174
+
175
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
176
+ end
177
+
178
+ describe "on x == [8, 9]" do
179
+ let(:scope){ { :x => [8, 9], :y => [8,9] } }
180
+
181
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
182
+ end
183
+
184
+ describe "on x == [8, 9, 10]" do
185
+ let(:scope){ { :x => [8, 9, 19], :y => [8,9] } }
186
+
187
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
188
+ end
189
+ end
190
+
191
+ context 'on an superset predicate, with an array literal' do
192
+ let(:predicate){
193
+ Predicate.superset(:x, [8,9])
194
+ }
195
+
196
+ describe "on x == [2]" do
197
+ let(:scope){ { :x => [2] } }
198
+
199
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
200
+ end
201
+
202
+ describe "on x == []" do
203
+ let(:scope){ { :x => [] } }
204
+
205
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
206
+ end
207
+
208
+ describe "on x == [9]" do
209
+ let(:scope){ { :x => [9] } }
210
+
211
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
212
+ end
213
+
214
+ describe "on x == [8, 9]" do
215
+ let(:scope){ { :x => [8, 9] } }
216
+
217
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
218
+ end
219
+
220
+ describe "on x == [8, 9, 10]" do
221
+ let(:scope){ { :x => [8, 9, 19] } }
222
+
223
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
224
+ end
225
+ end
226
+
227
+ context 'on an superset predicate, with two variables' do
228
+ let(:predicate){
229
+ Predicate.superset(:x, :y)
230
+ }
231
+
232
+ describe "on x == [2]" do
233
+ let(:scope){ { :x => [2], :y => [8,9] } }
234
+
235
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
236
+ end
237
+
238
+ describe "on x == []" do
239
+ let(:scope){ { :x => [], :y => [8,9] } }
240
+
241
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
242
+ end
243
+
244
+ describe "on x == [9]" do
245
+ let(:scope){ { :x => [9], :y => [8,9] } }
246
+
247
+ it{ expect(predicate.evaluate(scope)).to be_falsy }
248
+ end
249
+
250
+ describe "on x == [8, 9]" do
251
+ let(:scope){ { :x => [8, 9], :y => [8,9] } }
252
+
253
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
254
+ end
255
+
256
+ describe "on x == [8, 9, 10]" do
257
+ let(:scope){ { :x => [8, 9, 19], :y => [8,9] } }
258
+
259
+ it{ expect(predicate.evaluate(scope)).to be_truthy }
260
+ end
261
+ end
262
+
101
263
  context 'on a match against a string' do
102
264
  let(:predicate){
103
265
  Predicate.match(:x, "12")
@@ -228,12 +390,52 @@ class Predicate
228
390
 
229
391
  context 'has a call alias' do
230
392
  let(:predicate){
231
- Predicate.new(Factory.gte(:x => 0))
393
+ Predicate.gte(:x => 0)
232
394
  }
233
395
 
234
396
  let(:scope){ { x: 2 } }
235
397
 
236
398
  it{ expect(predicate.call(scope)).to be(true) }
237
399
  end
400
+
401
+ context "on a var, build with a dotted string" do
402
+ let(:predicate){
403
+ Predicate.var("x.0.y")
404
+ }
405
+
406
+ let(:scope){ { x: [{ y: 2 }] } }
407
+
408
+ it{ expect(predicate.call(scope)).to eql(2) }
409
+ end
410
+
411
+ context "on a var, build with a . string" do
412
+ let(:predicate){
413
+ Predicate.var(".")
414
+ }
415
+
416
+ let(:scope){ { x: [{ y: 2 }] } }
417
+
418
+ it{ expect(predicate.call(scope)).to be(scope) }
419
+ end
420
+
421
+ context "on a var, build with an array for terms" do
422
+ let(:predicate){
423
+ Predicate.var([:x, 0, :y])
424
+ }
425
+
426
+ let(:scope){ { x: [{ y: 2 }] } }
427
+
428
+ it{ expect(predicate.call(scope)).to eql(2) }
429
+ end
430
+
431
+ context "on a var, build with an empty array" do
432
+ let(:predicate){
433
+ Predicate.var([])
434
+ }
435
+
436
+ let(:scope){ { x: [{ y: 2 }] } }
437
+
438
+ it{ expect(predicate.call(scope)).to be(scope) }
439
+ end
238
440
  end
239
441
  end
@@ -9,6 +9,6 @@ class Predicate
9
9
 
10
10
  it{ should eq([:x]) }
11
11
  end
12
-
12
+
13
13
  end
14
14
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ class Predicate
3
+ describe Predicate, "to_hash" do
4
+
5
+ let(:p){ Predicate }
6
+
7
+ subject{ predicate.to_hash }
8
+
9
+ context "tautology" do
10
+ let(:predicate){ Predicate.tautology }
11
+
12
+ it{ expect(subject).to eql({}) }
13
+ end
14
+
15
+ context "contradiction" do
16
+ let(:predicate){ Predicate.contradiction }
17
+
18
+ it{ expect{ subject }.to raise_error(ArgumentError) }
19
+ end
20
+
21
+ context "eq" do
22
+ let(:predicate){ Predicate.eq(:x, 2) }
23
+
24
+ it{ expect(subject).to eql(x: 2) }
25
+ end
26
+
27
+ context "in" do
28
+ let(:predicate){ Predicate.in(:x, [2,3]) }
29
+
30
+ it{ expect(subject).to eql(x: [2,3]) }
31
+ end
32
+
33
+ context "and" do
34
+ let(:predicate){ Predicate.eq(:x, 3) & Predicate.in(:y, [2,3]) }
35
+
36
+ it{ expect(subject).to eql(x: 3, y: [2,3]) }
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ class Predicate
3
+ describe Predicate, "to_s" do
4
+
5
+ let(:p){ Predicate }
6
+
7
+ subject{ predicate.to_s }
8
+
9
+ context "tautology" do
10
+ let(:predicate){ Predicate.tautology }
11
+
12
+ it{ expect(subject).to eql("true") }
13
+ end
14
+
15
+ context "contradiction" do
16
+ let(:predicate){ Predicate.contradiction }
17
+
18
+ it{ expect(subject).to eql("false") }
19
+ end
20
+
21
+ context "var" do
22
+ let(:predicate){ Predicate.var("x.y") }
23
+
24
+ it{ expect(subject).to eql("dig(x.y)") }
25
+ end
26
+
27
+ context "var when used in another predicate" do
28
+ let(:predicate){
29
+ v = Predicate.var("x.y")
30
+ Predicate.eq(v, 6)
31
+ }
32
+
33
+ it{ expect(subject).to eql("dig(x.y) == 6") }
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ class Predicate
3
+ describe Predicate, "unqualify" do
4
+
5
+ let(:p){ Predicate }
6
+
7
+ subject{ predicate.unqualify }
8
+
9
+ context 'when the predicate is not qualified' do
10
+ let(:predicate){ p.in(:x, [2]) & p.eq(:y, :z) }
11
+
12
+ it 'works as expected' do
13
+ expect(subject).to eql(predicate)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -164,6 +164,22 @@ class Predicate
164
164
  end
165
165
  end
166
166
 
167
+ context 'subset' do
168
+ let(:predicate) { Predicate.subset(:x, [8, 9]) }
169
+
170
+ it 'raises an error' do
171
+ expect { subject }.to raise_error(NotSupportedError)
172
+ end
173
+ end
174
+
175
+ context 'superset' do
176
+ let(:predicate) { Predicate.superset(:x, [8, 9]) }
177
+
178
+ it 'raises an error' do
179
+ expect { subject }.to raise_error(NotSupportedError)
180
+ end
181
+ end
182
+
167
183
  context 'match' do
168
184
  let(:predicate) { Predicate.match(left, right, options) }
169
185
  let(:options){ nil }