predicate 2.3.3 → 2.7.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.
- checksums.yaml +5 -5
- data/Gemfile +4 -0
- data/LICENSE.md +17 -19
- data/README.md +467 -0
- data/bin/g +2 -0
- data/lib/predicate/dsl.rb +138 -0
- data/lib/predicate/factory.rb +149 -40
- data/lib/predicate/grammar.rb +11 -2
- data/lib/predicate/grammar.sexp.yml +31 -0
- data/lib/predicate/nodes/${op_name}.rb.jeny +12 -0
- data/lib/predicate/nodes/and.rb +9 -0
- data/lib/predicate/nodes/binary_func.rb +20 -0
- data/lib/predicate/nodes/contradiction.rb +2 -7
- data/lib/predicate/nodes/dyadic_comp.rb +1 -3
- data/lib/predicate/nodes/empty.rb +14 -0
- data/lib/predicate/nodes/eq.rb +11 -3
- data/lib/predicate/nodes/expr.rb +5 -3
- data/lib/predicate/nodes/has_size.rb +14 -0
- data/lib/predicate/nodes/identifier.rb +1 -3
- data/lib/predicate/nodes/in.rb +7 -6
- data/lib/predicate/nodes/intersect.rb +3 -23
- data/lib/predicate/nodes/literal.rb +1 -3
- data/lib/predicate/nodes/match.rb +1 -21
- data/lib/predicate/nodes/nadic_bool.rb +1 -3
- data/lib/predicate/nodes/native.rb +1 -3
- data/lib/predicate/nodes/not.rb +1 -3
- data/lib/predicate/nodes/opaque.rb +1 -3
- data/lib/predicate/nodes/qualified_identifier.rb +1 -3
- data/lib/predicate/nodes/set_op.rb +26 -0
- data/lib/predicate/nodes/subset.rb +11 -0
- data/lib/predicate/nodes/superset.rb +11 -0
- data/lib/predicate/nodes/tautology.rb +6 -7
- data/lib/predicate/nodes/unary_func.rb +16 -0
- data/lib/predicate/nodes/var.rb +46 -0
- data/lib/predicate/postgres/ext/factory.rb +28 -0
- data/lib/predicate/postgres/ext/to_sequel.rb +26 -0
- data/lib/predicate/postgres/ext.rb +2 -0
- data/lib/predicate/postgres/pg_array/empty.rb +10 -0
- data/lib/predicate/postgres/pg_array/literal.rb +10 -0
- data/lib/predicate/postgres/pg_array/overlaps.rb +10 -0
- data/lib/predicate/postgres/pg_array.rb +25 -0
- data/lib/predicate/postgres/rewriter.rb +49 -0
- data/lib/predicate/postgres.rb +4 -0
- data/lib/predicate/processors/qualifier.rb +4 -0
- data/lib/predicate/processors/renamer.rb +4 -0
- data/lib/predicate/processors/to_s.rb +28 -0
- data/lib/predicate/processors/unqualifier.rb +4 -0
- data/lib/predicate/sequel/to_sequel.rb +5 -1
- data/lib/predicate/sugar.rb +47 -0
- data/lib/predicate/version.rb +2 -2
- data/lib/predicate.rb +28 -4
- data/spec/dsl/test_dsl.rb +204 -0
- data/spec/dsl/test_evaluate.rb +65 -0
- data/spec/dsl/test_respond_to_missing.rb +35 -0
- data/spec/dsl/test_to_skake_case.rb +38 -0
- data/spec/factory/shared/a_comparison_factory_method.rb +1 -0
- data/spec/factory/test_${op_name}.rb.jeny +12 -0
- data/spec/factory/test_comp.rb +28 -5
- data/spec/factory/test_empty.rb +11 -0
- data/spec/factory/test_has_size.rb +11 -0
- data/spec/factory/test_match.rb +1 -0
- data/spec/factory/test_set_ops.rb +18 -0
- data/spec/factory/test_var.rb +22 -0
- data/spec/factory/test_vars.rb +27 -0
- data/spec/nodes/${op_name}.jeny/test_evaluate.rb.jeny +19 -0
- data/spec/nodes/empty/test_evaluate.rb +42 -0
- data/spec/nodes/has_size/test_evaluate.rb +44 -0
- data/spec/postgres/test_factory.rb +48 -0
- data/spec/postgres/test_to_postgres.rb +41 -0
- data/spec/postgres/test_to_sequel.rb +44 -0
- data/spec/predicate/test_and_split.rb +18 -0
- data/spec/predicate/test_attr_split.rb +18 -0
- data/spec/predicate/test_constant_variables.rb +24 -2
- data/spec/predicate/test_constants.rb +24 -0
- data/spec/predicate/test_evaluate.rb +205 -3
- data/spec/predicate/test_free_variables.rb +1 -1
- data/spec/predicate/test_to_hash.rb +40 -0
- data/spec/predicate/test_to_s.rb +37 -0
- data/spec/sequel/test_to_sequel.rb +26 -10
- data/spec/shared/a_predicate.rb +34 -0
- data/spec/spec_helper.rb +18 -1
- data/spec/test_predicate.rb +78 -33
- data/spec/test_readme.rb +80 -0
- data/spec/test_sugar.rb +48 -0
- data/tasks/test.rake +3 -3
- metadata +67 -11
- data/spec/factory/test_between.rb +0 -12
- data/spec/factory/test_intersect.rb +0 -12
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class Predicate
|
|
4
|
+
describe Dsl do
|
|
5
|
+
subject{
|
|
6
|
+
Predicate::Dsl.new(:x).instance_eval(&bl)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
context 'when used on a comparison op' do
|
|
10
|
+
context 'curried' do
|
|
11
|
+
let(:bl){
|
|
12
|
+
Proc.new{ eq(6) }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
it { expect(subject).to eq(Predicate.eq(:x, 6))}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'non curried' do
|
|
19
|
+
let(:bl){
|
|
20
|
+
Proc.new{ eq(:y, 6) }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
it { expect(subject).to eq(Predicate.eq(:y, 6))}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'when used on between' do
|
|
28
|
+
context 'curried' do
|
|
29
|
+
let(:bl){
|
|
30
|
+
Proc.new{ between(2, 6) }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
it { expect(subject).to eq(Predicate.gte(:x, 2) & Predicate.lte(:x, 6))}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'non curried' do
|
|
37
|
+
let(:bl){
|
|
38
|
+
Proc.new{ between(:y, 2, 6) }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
it { expect(subject).to eq(Predicate.gte(:y, 2) & Predicate.lte(:y, 6))}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context 'when used on a sugar op' do
|
|
46
|
+
context 'curried' do
|
|
47
|
+
let(:bl){
|
|
48
|
+
Proc.new{ min_size(6) }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
it { expect(subject).to eq(Predicate.has_size(:x, Range.new(6, nil)))}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context 'curried' do
|
|
55
|
+
let(:bl){
|
|
56
|
+
Proc.new{ min_size(:y, 6) }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
it { expect(subject).to eq(Predicate.has_size(:y, Range.new(6, nil)))}
|
|
60
|
+
end
|
|
61
|
+
end if RUBY_VERSION >= "2.6"
|
|
62
|
+
|
|
63
|
+
context 'when used on match' do
|
|
64
|
+
context 'curryied' do
|
|
65
|
+
let(:bl){
|
|
66
|
+
Proc.new{ match(/a-z/) }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
it { expect(subject).to eq(Predicate.match(:x, /a-z/, {}))}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context 'curryied with options' do
|
|
73
|
+
let(:bl){
|
|
74
|
+
Proc.new{ match(/a-z/, {case_sensitive: false}) }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
it { expect(subject).to eq(Predicate.match(:x, /a-z/, {case_sensitive: false}))}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'non curried' do
|
|
81
|
+
let(:bl){
|
|
82
|
+
Proc.new{ match(:y, /a-z/) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
it { expect(subject).to eq(Predicate.match(:y, /a-z/, {}))}
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context 'non curried with options' do
|
|
89
|
+
let(:bl){
|
|
90
|
+
Proc.new{ match(:y, /a-z/, {case_sensitive: false}) }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
it { expect(subject).to eq(Predicate.match(:y, /a-z/, {case_sensitive: false}))}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'when used on size' do
|
|
98
|
+
context 'curried' do
|
|
99
|
+
let(:bl){
|
|
100
|
+
Proc.new{ size(1..10) }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
it { expect(subject).to eq(Predicate.has_size(:x, 1..10))}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context 'curried with Integer' do
|
|
107
|
+
let(:bl){
|
|
108
|
+
Proc.new{ size(10) }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
it { expect(subject).to eq(Predicate.has_size(:x, 10))}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
context 'not curried' do
|
|
115
|
+
let(:bl){
|
|
116
|
+
Proc.new{ size(:y, 1..10) }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
it { expect(subject).to eq(Predicate.has_size(:y, 1..10))}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context 'not curried with Integer' do
|
|
123
|
+
let(:bl){
|
|
124
|
+
Proc.new{ size(:y, 10) }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
it { expect(subject).to eq(Predicate.has_size(:y, 10))}
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context 'when used with some camelCase' do
|
|
132
|
+
context 'curried' do
|
|
133
|
+
let(:bl){
|
|
134
|
+
Proc.new{ hasSize(1..10) }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
it { expect(subject).to eq(Predicate.has_size(:x, 1..10))}
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
context 'when used with full text versions and their negation' do
|
|
142
|
+
context 'less_than' do
|
|
143
|
+
let(:bl){
|
|
144
|
+
Proc.new{ less_than(:x, 1) }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
it { expect(subject).to eq(Predicate.lt(:x, 1))}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context 'lessThan' do
|
|
151
|
+
let(:bl){
|
|
152
|
+
Proc.new{ lessThan(:x, 1) }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
it { expect(subject).to eq(Predicate.lt(:x, 1))}
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
context 'notLessThan' do
|
|
159
|
+
let(:bl){
|
|
160
|
+
Proc.new{ notLessThan(:x, 1) }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
it { expect(subject).to eq(!Predicate.lt(:x, 1))}
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context 'when used with a negated form' do
|
|
168
|
+
context 'curried' do
|
|
169
|
+
let(:bl){
|
|
170
|
+
Proc.new{ notSize(1..10) }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
it { expect(subject).to eq(!Predicate.has_size(:x, 1..10))}
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
context 'when used on null' do
|
|
178
|
+
context 'curried' do
|
|
179
|
+
let(:bl){
|
|
180
|
+
Proc.new{ null() }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
it { expect(subject).to eq(Predicate.eq(:x, nil))}
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context 'negated' do
|
|
187
|
+
let(:bl){
|
|
188
|
+
Proc.new{ notNull() }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
it { expect(subject).to eq(Predicate.neq(:x, nil))}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
context 'not curried' do
|
|
195
|
+
let(:bl){
|
|
196
|
+
Proc.new{ null(:y) }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
it { expect(subject).to eq(Predicate.eq(:y, nil))}
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Predicate in a curried form' do
|
|
4
|
+
|
|
5
|
+
context 'with a variable name' do
|
|
6
|
+
it 'supports tautology, contradiction' do
|
|
7
|
+
p = Predicate.currying(:x){ tautology }
|
|
8
|
+
expect(p).to eql(Predicate.tautology)
|
|
9
|
+
|
|
10
|
+
p = Predicate.currying(:x){ contradiction }
|
|
11
|
+
expect(p).to eql(Predicate.contradiction)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'support comparison operators' do
|
|
15
|
+
p = Predicate.currying(:x){
|
|
16
|
+
gt(0) & lt(12)
|
|
17
|
+
}
|
|
18
|
+
expect(p.evaluate(x: 0)).to be_falsy
|
|
19
|
+
expect(p.evaluate(x: 1)).to be_truthy
|
|
20
|
+
expect(p.evaluate(x: 11)).to be_truthy
|
|
21
|
+
expect(p.evaluate(x: 12)).to be_falsy
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'supports between shortcut' do
|
|
25
|
+
p = Predicate.currying(:x){
|
|
26
|
+
between(1, 11)
|
|
27
|
+
}
|
|
28
|
+
expect(p.evaluate(x: 0)).to be_falsy
|
|
29
|
+
expect(p.evaluate(x: 1)).to be_truthy
|
|
30
|
+
expect(p.evaluate(x: 11)).to be_truthy
|
|
31
|
+
expect(p.evaluate(x: 12)).to be_falsy
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'supports match' do
|
|
35
|
+
p = Predicate.currying(:x){
|
|
36
|
+
match(/a/)
|
|
37
|
+
}
|
|
38
|
+
expect(p.evaluate(x: "zzz")).to be_falsy
|
|
39
|
+
expect(p.evaluate(x: "abc")).to be_truthy
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context 'without a variable name' do
|
|
44
|
+
it 'applies to the object passed' do
|
|
45
|
+
p = Predicate.currying{
|
|
46
|
+
gt(0) & lt(12)
|
|
47
|
+
}
|
|
48
|
+
expect(p.evaluate(0)).to be_falsy
|
|
49
|
+
expect(p.evaluate(1)).to be_truthy
|
|
50
|
+
expect(p.evaluate(11)).to be_truthy
|
|
51
|
+
expect(p.evaluate(12)).to be_falsy
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context 'on shortcuts' do
|
|
56
|
+
it 'applies to the object passed' do
|
|
57
|
+
p = Predicate.currying{
|
|
58
|
+
min_size(5)
|
|
59
|
+
}
|
|
60
|
+
expect(p.evaluate("1")).to be_falsy
|
|
61
|
+
expect(p.evaluate("013456789")).to be_truthy
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class Predicate
|
|
4
|
+
describe Dsl do
|
|
5
|
+
subject{
|
|
6
|
+
Predicate::Dsl.new(:x)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
it 'respond to negated forms' do
|
|
10
|
+
[
|
|
11
|
+
:notSize,
|
|
12
|
+
:notEq
|
|
13
|
+
].each do |m|
|
|
14
|
+
expect(subject.send(:respond_to_missing?, m)).to eql(true)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'respond to camelCased forms' do
|
|
19
|
+
[
|
|
20
|
+
:hasSize
|
|
21
|
+
].each do |m|
|
|
22
|
+
expect(subject.send(:respond_to_missing?, m)).to eql(true)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'respond false otherwise' do
|
|
27
|
+
[
|
|
28
|
+
:nosuchone,
|
|
29
|
+
:notsuchone
|
|
30
|
+
].each do |m|
|
|
31
|
+
expect(subject.send(:respond_to_missing?, m)).to eql(false)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class Predicate
|
|
4
|
+
describe Dsl, "to_snake_case" do
|
|
5
|
+
|
|
6
|
+
def snake(str)
|
|
7
|
+
Dsl.new.__send__(:to_snake_case, str)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'works on snake case already' do
|
|
11
|
+
{
|
|
12
|
+
"snake" => "snake",
|
|
13
|
+
"snake_case" => "snake_case"
|
|
14
|
+
}.each_pair do |k,v|
|
|
15
|
+
expect(snake(k)).to eq(v)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'works on camelCase already' do
|
|
20
|
+
{
|
|
21
|
+
"camelCase" => "camel_case",
|
|
22
|
+
"theCamelCase" => "the_camel_case"
|
|
23
|
+
}.each_pair do |k,v|
|
|
24
|
+
expect(snake(k)).to eq(v)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'works on PascalCase already' do
|
|
29
|
+
{
|
|
30
|
+
"PascalCase" => "pascal_case",
|
|
31
|
+
"ThePascalCase" => "the_pascal_case"
|
|
32
|
+
}.each_pair do |k,v|
|
|
33
|
+
expect(snake(k)).to eq(v)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#jeny(predicate)
|
|
2
|
+
require_relative 'shared/a_predicate_ast_node'
|
|
3
|
+
class Predicate
|
|
4
|
+
describe Factory, '${op_name}' do
|
|
5
|
+
include Factory
|
|
6
|
+
subject{ ${op_name}(TODO) }
|
|
7
|
+
|
|
8
|
+
it_should_behave_like "a predicate AST node"
|
|
9
|
+
|
|
10
|
+
it{ should be_a(${OpName}) }
|
|
11
|
+
end
|
|
12
|
+
end
|
data/spec/factory/test_comp.rb
CHANGED
|
@@ -10,7 +10,15 @@ class Predicate
|
|
|
10
10
|
it{ should eq(Factory.tautology) }
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
context "when the hash is
|
|
13
|
+
context "when the hash is a singelton" do
|
|
14
|
+
let(:h){ {:x => 12} }
|
|
15
|
+
|
|
16
|
+
it_should_behave_like "a predicate AST node"
|
|
17
|
+
it{ should be_a(Eq) }
|
|
18
|
+
it{ should eq([:eq, [:identifier, :x], [:literal, 12]]) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "when the hash is not singleton" do
|
|
14
22
|
let(:h){ {:x => 12, :y => :z} }
|
|
15
23
|
let(:expected){
|
|
16
24
|
[:and,
|
|
@@ -23,12 +31,27 @@ class Predicate
|
|
|
23
31
|
it{ should eq(expected) }
|
|
24
32
|
end
|
|
25
33
|
|
|
26
|
-
context "when the
|
|
27
|
-
let(:
|
|
34
|
+
context "when the value is a Regexp" do
|
|
35
|
+
let(:rx){ /[a-z]+/ }
|
|
36
|
+
let(:h){ {:x => /[a-z]+/} }
|
|
28
37
|
|
|
29
38
|
it_should_behave_like "a predicate AST node"
|
|
30
|
-
it{ should be_a(
|
|
31
|
-
it{ should eq([:
|
|
39
|
+
it{ should be_a(Match) }
|
|
40
|
+
it{ should eq([:match, [:identifier, :x], [:literal, rx]]) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "when the hash mixes value types" do
|
|
44
|
+
let(:rx){ /[a-z]+/ }
|
|
45
|
+
let(:h){ {:x => 12, :y => rx} }
|
|
46
|
+
let(:expected){
|
|
47
|
+
[:and,
|
|
48
|
+
[:eq, [:identifier, :x], [:literal, 12]],
|
|
49
|
+
[:match, [:identifier, :y], [:literal, rx]]]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
it_should_behave_like "a predicate AST node"
|
|
53
|
+
it{ should be_a(And) }
|
|
54
|
+
it{ should eq(expected) }
|
|
32
55
|
end
|
|
33
56
|
|
|
34
57
|
end
|
data/spec/factory/test_match.rb
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative "shared/a_comparison_factory_method"
|
|
2
|
+
class Predicate
|
|
3
|
+
[
|
|
4
|
+
[ :intersect, Intersect ],
|
|
5
|
+
[ :subset, Subset ],
|
|
6
|
+
[ :superset, Superset ],
|
|
7
|
+
].each do |op_name, op_class|
|
|
8
|
+
describe Factory, op_name do
|
|
9
|
+
|
|
10
|
+
subject{ Factory.send(op_name, :x, [2, 3]) }
|
|
11
|
+
|
|
12
|
+
it{ should be_a(op_class) }
|
|
13
|
+
|
|
14
|
+
it{ should eq([op_name, [:identifier, :x], [:literal, [2, 3]]]) }
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
|
2
|
+
class Predicate
|
|
3
|
+
describe Factory, 'var' do
|
|
4
|
+
include Factory
|
|
5
|
+
|
|
6
|
+
context 'when used with a string' do
|
|
7
|
+
subject{ var("a.b.c", :dig) }
|
|
8
|
+
|
|
9
|
+
it_should_behave_like "a predicate AST node"
|
|
10
|
+
it{ should be_a(Var) }
|
|
11
|
+
it{ should eql([:var, "a.b.c", :dig]) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context 'when used with an array' do
|
|
15
|
+
subject{ var([:a, :b, :c], :dig) }
|
|
16
|
+
|
|
17
|
+
it_should_behave_like "a predicate AST node"
|
|
18
|
+
it{ should be_a(Var) }
|
|
19
|
+
it{ should eql([:var, [:a, :b, :c], :dig]) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative 'shared/a_predicate_ast_node'
|
|
2
|
+
class Predicate
|
|
3
|
+
describe Factory, 'vars' do
|
|
4
|
+
include Factory
|
|
5
|
+
|
|
6
|
+
context 'when used without semantics' do
|
|
7
|
+
subject{ vars("a.b.c", "d.e.f") }
|
|
8
|
+
|
|
9
|
+
it 'works as expected' do
|
|
10
|
+
expect(subject).to be_a(Array)
|
|
11
|
+
expect(subject.size).to eql(2)
|
|
12
|
+
expect(subject.all?{|p| p.is_a?(Var) && p.semantics == :dig })
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
context 'when used with semantics' do
|
|
17
|
+
subject{ vars("a.b.c", "d.e.f", :jsonpath) }
|
|
18
|
+
|
|
19
|
+
it 'works as expected' do
|
|
20
|
+
expect(subject).to be_a(Array)
|
|
21
|
+
expect(subject.size).to eql(2)
|
|
22
|
+
expect(subject.all?{|p| p.is_a?(Var) && p.semantics == :jsonpath })
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#jeny(predicate)
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
class Predicate
|
|
4
|
+
describe ${OpName}, "evaluate" do
|
|
5
|
+
|
|
6
|
+
let(:predicate){ Factory.${op_name}(TODO) }
|
|
7
|
+
|
|
8
|
+
subject{ predicate.evaluate(context) }
|
|
9
|
+
|
|
10
|
+
context "on TODO" do
|
|
11
|
+
let(:context){ TODO }
|
|
12
|
+
|
|
13
|
+
it 'works as expected' do
|
|
14
|
+
TODO
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
class Predicate
|
|
3
|
+
describe Empty, "evaluate" do
|
|
4
|
+
|
|
5
|
+
let(:predicate){ Factory.empty(:x) }
|
|
6
|
+
|
|
7
|
+
subject{ predicate.evaluate(context) }
|
|
8
|
+
|
|
9
|
+
context "on an empty array" do
|
|
10
|
+
let(:context){ { x: [] } }
|
|
11
|
+
|
|
12
|
+
it{ expect(subject).to eq(true) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "on a non empty array" do
|
|
16
|
+
let(:context){ { x: [1, 2, 3] } }
|
|
17
|
+
|
|
18
|
+
it{ expect(subject).to eq(false) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
context "on a empty string" do
|
|
22
|
+
let(:context){ { x: "" } }
|
|
23
|
+
|
|
24
|
+
it{ expect(subject).to eq(true) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "on a non empty string" do
|
|
28
|
+
let(:context){ { x: "1233" } }
|
|
29
|
+
|
|
30
|
+
it{ expect(subject).to eq(false) }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "on an object that does not respond to empty?" do
|
|
34
|
+
let(:context){ { x: 14567 } }
|
|
35
|
+
|
|
36
|
+
it{
|
|
37
|
+
expect{ subject }.to raise_error(TypeError, "Expected 14567 to respond to empty?")
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
class Predicate
|
|
3
|
+
describe HasSize, "evaluate" do
|
|
4
|
+
|
|
5
|
+
let(:predicate){
|
|
6
|
+
Factory.has_size(:x, y)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
subject{ predicate.evaluate(context) }
|
|
10
|
+
|
|
11
|
+
context 'against a Range' do
|
|
12
|
+
let(:y){ 1..10 }
|
|
13
|
+
|
|
14
|
+
context "on a match x" do
|
|
15
|
+
let(:context){ { x: "1234567" } }
|
|
16
|
+
|
|
17
|
+
it{ expect(subject).to eq(true) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "on a non matching x" do
|
|
21
|
+
let(:context){ { x: "1234567891011" } }
|
|
22
|
+
|
|
23
|
+
it{ expect(subject).to eq(false) }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'against an Integer' do
|
|
28
|
+
let(:y){ 10 }
|
|
29
|
+
|
|
30
|
+
context "on a match x" do
|
|
31
|
+
let(:context){ { x: "0123456789" } }
|
|
32
|
+
|
|
33
|
+
it{ expect(subject).to eq(true) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "on a non matching x" do
|
|
37
|
+
let(:context){ { x: "1234567891011" } }
|
|
38
|
+
|
|
39
|
+
it{ expect(subject).to eq(false) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class Predicate
|
|
4
|
+
describe 'pg_factory' do
|
|
5
|
+
|
|
6
|
+
subject{
|
|
7
|
+
predicate
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let(:predicate) {
|
|
11
|
+
Predicate.tautology
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
it_should_behave_like "a predicate"
|
|
15
|
+
|
|
16
|
+
context 'pg_array_literal' do
|
|
17
|
+
let(:predicate) do
|
|
18
|
+
Predicate.pg_array_literal([12])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'pg_array_empty' do
|
|
23
|
+
let(:predicate) do
|
|
24
|
+
Predicate.pg_array_empty(:x)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context 'pg_array_overlaps' do
|
|
29
|
+
let(:predicate) do
|
|
30
|
+
Predicate.pg_array_overlaps(:x, [1,2,3])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'has expected free variables' do
|
|
34
|
+
expect(subject.free_variables).to eql([:x])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'pg_array_overlaps (2)' do
|
|
39
|
+
let(:predicate) do
|
|
40
|
+
Predicate.pg_array_overlaps(:x, :y)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'has expected free variables' do
|
|
44
|
+
expect(subject.free_variables).to eql([:x, :y])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|