predicate 2.3.0 → 2.5.0

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