predicate 2.7.1 → 2.9.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/README.md +17 -0
- data/lib/predicate/asserter.rb +12 -0
- data/lib/predicate/nodes/and.rb +22 -4
- data/lib/predicate/nodes/empty.rb +6 -0
- data/lib/predicate/nodes/eq.rb +10 -0
- data/lib/predicate/nodes/expr.rb +8 -0
- data/lib/predicate/nodes/gt.rb +6 -0
- data/lib/predicate/nodes/gte.rb +6 -0
- data/lib/predicate/nodes/in.rb +12 -0
- data/lib/predicate/nodes/lt.rb +6 -0
- data/lib/predicate/nodes/lte.rb +6 -0
- data/lib/predicate/nodes/neq.rb +17 -0
- data/lib/predicate/nodes/tautology.rb +4 -0
- data/lib/predicate/version.rb +2 -2
- data/lib/predicate.rb +21 -2
- data/spec/factory/shared/a_comparison_factory_method.rb +12 -4
- data/spec/factory/shared/a_predicate_ast_node.rb +11 -5
- data/spec/factory/test_${op_name}.rb.jeny +3 -1
- data/spec/factory/test_and.rb +6 -2
- data/spec/factory/test_comp.rb +27 -9
- data/spec/factory/test_contradiction.rb +3 -1
- data/spec/factory/test_empty.rb +3 -1
- data/spec/factory/test_factor_predicate.rb +21 -7
- data/spec/factory/test_from_hash.rb +21 -7
- data/spec/factory/test_has_size.rb +3 -1
- data/spec/factory/test_identifier.rb +6 -2
- data/spec/factory/test_literal.rb +6 -2
- data/spec/factory/test_match.rb +12 -4
- data/spec/factory/test_native.rb +6 -2
- data/spec/factory/test_not.rb +6 -2
- data/spec/factory/test_or.rb +6 -2
- data/spec/factory/test_qualified_identifier.rb +6 -2
- data/spec/factory/test_set_ops.rb +6 -2
- data/spec/factory/test_tautology.rb +3 -1
- data/spec/factory/test_var.rb +12 -4
- data/spec/grammar/test_match.rb +19 -19
- data/spec/grammar/test_sexpr.rb +49 -17
- data/spec/nodes/and/test_and_split.rb +30 -10
- data/spec/nodes/dyadic_comp/test_and_split.rb +18 -8
- data/spec/nodes/eq/test_and.rb +12 -4
- data/spec/nodes/identifier/test_and_split.rb +6 -2
- data/spec/nodes/identifier/test_free_variables.rb +3 -1
- data/spec/nodes/identifier/test_name.rb +3 -1
- data/spec/nodes/in/test_and.rb +18 -6
- data/spec/nodes/nadic_bool/test_free_variables.rb +3 -1
- data/spec/nodes/or/test_and_split.rb +6 -2
- data/spec/nodes/qualified_identifier/test_and_split.rb +6 -2
- data/spec/nodes/qualified_identifier/test_free_variables.rb +3 -1
- data/spec/nodes/qualified_identifier/test_name.rb +3 -1
- data/spec/nodes/qualified_identifier/test_qualifier.rb +3 -1
- data/spec/predicate/test_and_split.rb +48 -16
- data/spec/predicate/test_assert!.rb +277 -0
- data/spec/predicate/test_attr_split.rb +36 -12
- data/spec/predicate/test_bool_and.rb +7 -3
- data/spec/predicate/test_bool_not.rb +30 -10
- data/spec/predicate/test_bool_or.rb +7 -3
- data/spec/predicate/test_coerce.rb +19 -17
- data/spec/predicate/test_constants.rb +78 -26
- data/spec/predicate/test_free_variables.rb +3 -1
- data/spec/predicate/test_hash_and_equal.rb +7 -3
- data/spec/predicate/test_qualify.rb +8 -6
- data/spec/predicate/test_rename.rb +21 -11
- data/spec/sequel/test_to_sequel.rb +0 -1
- data/spec/shared/a_predicate.rb +8 -8
- data/spec/spec_helper.rb +1 -0
- data/spec/test_readme.rb +1 -1
- metadata +24 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 571d712e35bc8c4144014e76e3827614fdf78191f944a10ce7a1f8f5595886de
|
4
|
+
data.tar.gz: c7919717e71c0a23a4c0869e8e0fa384c5e6b8e0db9af5d87ef7c9c89fa8f006
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d511b557d1b8d05cd4f56064977ba7189faaf37ab33562962a75027ab57454eae11063f9d5444c4a938b804cc5ec3316fe2e5aa39cad7ae33dafcfdd91dc6e4d
|
7
|
+
data.tar.gz: cd1ddf3e4e99abb752c940db00a0627f8be5a6e39556b5c37022f884a8457bcfd75cf27d053639887f8cd5da4125ca86926670f6bc3579a38c3563a6e4e5e02c
|
data/README.md
CHANGED
@@ -16,6 +16,9 @@ p.evaluate(:x => 2, :y => 6)
|
|
16
16
|
|
17
17
|
p.evaluate(:x => 2, :y => 3)
|
18
18
|
# => false
|
19
|
+
|
20
|
+
p.assert!(:x => 2, :y => 3)
|
21
|
+
# => Minitest::Assertion("Expected false to be truthy")
|
19
22
|
```
|
20
23
|
|
21
24
|
When building complex expressions, you can use the `dsl` method.
|
@@ -226,6 +229,20 @@ p.evaluate(:x => 2, :y => 6)
|
|
226
229
|
# => true
|
227
230
|
```
|
228
231
|
|
232
|
+
### Assert
|
233
|
+
|
234
|
+
`Predicate#assert!` takes a takes a Hash mapping each free variable to a value,
|
235
|
+
and raises a Minitest::AssertionError if the predicate evaluates to false.
|
236
|
+
A best effort is made to provide readable error messages on the assertion error.
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
# Let's build a simple predicate for 'x = 2 and not(y <= 3)'
|
240
|
+
p = Predicate.eq(:x, 2) & !Predicate.lte(:y, 3)
|
241
|
+
|
242
|
+
p.evaluate(:x => 2, :y => 8)
|
243
|
+
# => Expected false to be truthy (Minitest::Assertion)
|
244
|
+
```
|
245
|
+
|
229
246
|
### Rename
|
230
247
|
|
231
248
|
`Predicate#rename` allows renaming variables.
|
data/lib/predicate/nodes/and.rb
CHANGED
@@ -53,14 +53,32 @@ class Predicate
|
|
53
53
|
sexpr_body.all?{|operand| operand.evaluate(tuple) }
|
54
54
|
end
|
55
55
|
|
56
|
+
def assert!(tuple, asserter = Asserter.new)
|
57
|
+
sexpr_body.all?{|operand| operand.assert!(tuple, asserter) }
|
58
|
+
end
|
59
|
+
|
56
60
|
def to_hash
|
57
61
|
sexpr_body.inject({}) do |p,term|
|
58
|
-
p
|
59
|
-
super unless v1 == v2
|
60
|
-
v1
|
61
|
-
}
|
62
|
+
_hash_merge(p, term.to_hash)
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
66
|
+
def to_hashes
|
67
|
+
sexpr_body.inject([{},{}]) do |(pos,neg),term|
|
68
|
+
t_pos, t_neg = term.to_hashes
|
69
|
+
[
|
70
|
+
_hash_merge(pos, t_pos),
|
71
|
+
_hash_merge(neg, t_neg)
|
72
|
+
]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def _hash_merge(h1, h2)
|
77
|
+
h1.merge(h2){|k,v1,v2|
|
78
|
+
raise ArgumentError, "Unable to represent #{self} to a Hash" unless v1 == v2
|
79
|
+
v1
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
65
83
|
end
|
66
84
|
end
|
data/lib/predicate/nodes/eq.rb
CHANGED
@@ -46,6 +46,12 @@ class Predicate
|
|
46
46
|
left.evaluate(tuple) == right.evaluate(tuple)
|
47
47
|
end
|
48
48
|
|
49
|
+
def assert!(tuple, asserter = Asserter.new)
|
50
|
+
l, r = left.evaluate(tuple), right.evaluate(tuple)
|
51
|
+
asserter.assert_equal(l, r)
|
52
|
+
l
|
53
|
+
end
|
54
|
+
|
49
55
|
def to_hash
|
50
56
|
if left.identifier? && right.literal? && !right.has_placeholder?
|
51
57
|
{ left.name => right.value }
|
@@ -56,5 +62,9 @@ class Predicate
|
|
56
62
|
end
|
57
63
|
end
|
58
64
|
|
65
|
+
def to_hashes
|
66
|
+
[ to_hash, {} ]
|
67
|
+
end
|
68
|
+
|
59
69
|
end
|
60
70
|
end
|
data/lib/predicate/nodes/expr.rb
CHANGED
@@ -50,6 +50,10 @@ class Predicate
|
|
50
50
|
sexpr([:or, self, other])
|
51
51
|
end
|
52
52
|
|
53
|
+
def assert!(tuple, asserter = Asserter.new)
|
54
|
+
asserter.assert(evaluate(tuple))
|
55
|
+
end
|
56
|
+
|
53
57
|
def and_split(attr_list)
|
54
58
|
# If we have no reference to attr_list, then we are P2, else we are P1
|
55
59
|
(free_variables & attr_list).empty? ? [ tautology, self ] : [ self, tautology ]
|
@@ -99,6 +103,10 @@ class Predicate
|
|
99
103
|
raise ArgumentError, "Unable to represent #{self} to a Hash"
|
100
104
|
end
|
101
105
|
|
106
|
+
def to_hashes
|
107
|
+
raise ArgumentError, "Unable to represent #{self} to two Hashes"
|
108
|
+
end
|
109
|
+
|
102
110
|
def sexpr(arg)
|
103
111
|
Factory.sexpr(arg)
|
104
112
|
end
|
data/lib/predicate/nodes/gt.rb
CHANGED
data/lib/predicate/nodes/gte.rb
CHANGED
data/lib/predicate/nodes/in.rb
CHANGED
@@ -67,6 +67,14 @@ class Predicate
|
|
67
67
|
values.include?(identifier.evaluate(tuple))
|
68
68
|
end
|
69
69
|
|
70
|
+
def assert!(tuple, asserter = Asserter.new)
|
71
|
+
values = right.evaluate(tuple)
|
72
|
+
raise UnboundError if values.is_a?(Placeholder)
|
73
|
+
value = identifier.evaluate(tuple)
|
74
|
+
asserter.assert_includes(values, value)
|
75
|
+
value
|
76
|
+
end
|
77
|
+
|
70
78
|
def var_against_literal_value?
|
71
79
|
left.identifier? && right.literal? && !right.has_placeholder?
|
72
80
|
end
|
@@ -76,5 +84,9 @@ class Predicate
|
|
76
84
|
{ identifier.name => right.value }
|
77
85
|
end
|
78
86
|
|
87
|
+
def to_hashes
|
88
|
+
[ to_hash, {} ]
|
89
|
+
end
|
90
|
+
|
79
91
|
end
|
80
92
|
end
|
data/lib/predicate/nodes/lt.rb
CHANGED
data/lib/predicate/nodes/lte.rb
CHANGED
data/lib/predicate/nodes/neq.rb
CHANGED
@@ -10,5 +10,22 @@ class Predicate
|
|
10
10
|
left.evaluate(tuple) != right.evaluate(tuple)
|
11
11
|
end
|
12
12
|
|
13
|
+
def assert!(tuple, asserter = Asserter.new)
|
14
|
+
l, r = left.evaluate(tuple), right.evaluate(tuple)
|
15
|
+
asserter.refute_equal(l, r)
|
16
|
+
l
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hashes
|
20
|
+
hash = if left.identifier? && right.literal? && !right.has_placeholder?
|
21
|
+
{ left.name => right.value }
|
22
|
+
elsif right.identifier? && left.literal? && !left.has_placeholder?
|
23
|
+
{ right.name => left.value }
|
24
|
+
else
|
25
|
+
super
|
26
|
+
end
|
27
|
+
[ {}, hash ]
|
28
|
+
end
|
29
|
+
|
13
30
|
end
|
14
31
|
end
|
data/lib/predicate/version.rb
CHANGED
data/lib/predicate.rb
CHANGED
@@ -6,6 +6,7 @@ require_relative 'predicate/sugar'
|
|
6
6
|
require_relative 'predicate/grammar'
|
7
7
|
require_relative 'predicate/processors'
|
8
8
|
require_relative 'predicate/dsl'
|
9
|
+
require_relative 'predicate/asserter'
|
9
10
|
class Predicate
|
10
11
|
|
11
12
|
class Error < StandardError; end
|
@@ -125,6 +126,10 @@ class Predicate
|
|
125
126
|
expr.evaluate(tuple)
|
126
127
|
end
|
127
128
|
|
129
|
+
def assert!(tuple = {})
|
130
|
+
expr.assert!(tuple)
|
131
|
+
end
|
132
|
+
|
128
133
|
# Splits this predicate, say P, as too predicates P1 & P2
|
129
134
|
# such that `P <=> P1 & P2` and P2 makes no reference to
|
130
135
|
# any attribute in `attr_list`.
|
@@ -160,10 +165,24 @@ class Predicate
|
|
160
165
|
end
|
161
166
|
|
162
167
|
# If possible, converts this predicate back to a `{ attr: value, ... }`
|
163
|
-
# hash.
|
164
|
-
#
|
168
|
+
# hash.
|
169
|
+
#
|
170
|
+
# Raises an ArgumentError if the predicate cannot be represented that way.
|
165
171
|
def to_hash
|
166
172
|
expr.to_hash
|
167
173
|
end
|
168
174
|
|
175
|
+
# If possible, converts this predicate back to two `{ attr: value, ... }`
|
176
|
+
# hashes, where the first one is positive (e.g. x IN ...) and the second one
|
177
|
+
# is negative (e.g. x NOT IN ...)
|
178
|
+
#
|
179
|
+
# The result is such that the original predicate is equivalent to a AND
|
180
|
+
# of the two hashes, where the first includes all its values and the second
|
181
|
+
# excludes all its values.
|
182
|
+
#
|
183
|
+
# Raises an ArgumentError if the predicate cannot be represented that way.
|
184
|
+
def to_hashes
|
185
|
+
expr.to_hashes
|
186
|
+
end
|
187
|
+
|
169
188
|
end # class Predicate
|
@@ -7,8 +7,12 @@ shared_examples_for "a comparison factory method" do
|
|
7
7
|
subject{ self.send(method, true, true) }
|
8
8
|
|
9
9
|
it_should_behave_like "a predicate AST node"
|
10
|
-
it{
|
11
|
-
|
10
|
+
it {
|
11
|
+
expect(subject).to be_a(node_class)
|
12
|
+
}
|
13
|
+
it {
|
14
|
+
expect(subject).to eql([method, tautology, tautology])
|
15
|
+
}
|
12
16
|
end
|
13
17
|
|
14
18
|
context 'with a Hash operand (singleton)' do
|
@@ -18,7 +22,9 @@ shared_examples_for "a comparison factory method" do
|
|
18
22
|
}
|
19
23
|
|
20
24
|
it_should_behave_like "a predicate AST node"
|
21
|
-
it{
|
25
|
+
it {
|
26
|
+
expect(subject).to eql(expected)
|
27
|
+
}
|
22
28
|
end
|
23
29
|
|
24
30
|
context 'with a Hash operand' do
|
@@ -30,7 +36,9 @@ shared_examples_for "a comparison factory method" do
|
|
30
36
|
}
|
31
37
|
|
32
38
|
it_should_behave_like "a predicate AST node"
|
33
|
-
it{
|
39
|
+
it {
|
40
|
+
expect(subject).to eql(expected)
|
41
|
+
}
|
34
42
|
end
|
35
43
|
|
36
44
|
end
|
@@ -1,20 +1,26 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
shared_examples_for "a predicate AST node" do
|
3
3
|
|
4
|
-
it{
|
4
|
+
it {
|
5
|
+
expect(subject).to be_a(Sexpr)
|
6
|
+
}
|
5
7
|
|
6
|
-
it{
|
8
|
+
it {
|
9
|
+
expect(subject).to be_a(Predicate::Expr)
|
10
|
+
}
|
7
11
|
|
8
12
|
specify{
|
9
|
-
(subject.tautology? == subject.is_a?(Predicate::Tautology))
|
13
|
+
got = (subject.tautology? == subject.is_a?(Predicate::Tautology))
|
14
|
+
expect(got).to be(true)
|
10
15
|
}
|
11
16
|
|
12
17
|
specify{
|
13
|
-
(subject.contradiction? == subject.is_a?(Predicate::Contradiction))
|
18
|
+
got = (subject.contradiction? == subject.is_a?(Predicate::Contradiction))
|
19
|
+
expect(got).to be(true)
|
14
20
|
}
|
15
21
|
|
16
22
|
specify{
|
17
|
-
subject.free_variables.
|
23
|
+
expect(subject.free_variables).to be_a(Array) unless subject.is_a?(Predicate::Native)
|
18
24
|
}
|
19
25
|
|
20
26
|
end
|
data/spec/factory/test_and.rb
CHANGED
@@ -6,8 +6,12 @@ class Predicate
|
|
6
6
|
subject{ self.and(true, true) }
|
7
7
|
|
8
8
|
it_should_behave_like "a predicate AST node"
|
9
|
-
it{
|
10
|
-
|
9
|
+
it {
|
10
|
+
expect(subject).to be_a(And)
|
11
|
+
}
|
12
|
+
it {
|
13
|
+
expect(subject).to eql([:and, tautology, tautology])
|
14
|
+
}
|
11
15
|
|
12
16
|
end
|
13
17
|
end
|
data/spec/factory/test_comp.rb
CHANGED
@@ -7,15 +7,21 @@ class Predicate
|
|
7
7
|
context "when the hash is empty" do
|
8
8
|
let(:h){ {} }
|
9
9
|
|
10
|
-
it{
|
10
|
+
it {
|
11
|
+
expect(subject).to eq(Factory.tautology)
|
12
|
+
}
|
11
13
|
end
|
12
14
|
|
13
15
|
context "when the hash is a singelton" do
|
14
16
|
let(:h){ {:x => 12} }
|
15
17
|
|
16
18
|
it_should_behave_like "a predicate AST node"
|
17
|
-
it{
|
18
|
-
|
19
|
+
it {
|
20
|
+
expect(subject).to be_a(Eq)
|
21
|
+
}
|
22
|
+
it {
|
23
|
+
expect(subject).to eq([:eq, [:identifier, :x], [:literal, 12]])
|
24
|
+
}
|
19
25
|
end
|
20
26
|
|
21
27
|
context "when the hash is not singleton" do
|
@@ -27,8 +33,12 @@ class Predicate
|
|
27
33
|
}
|
28
34
|
|
29
35
|
it_should_behave_like "a predicate AST node"
|
30
|
-
it{
|
31
|
-
|
36
|
+
it {
|
37
|
+
expect(subject).to be_a(And)
|
38
|
+
}
|
39
|
+
it {
|
40
|
+
expect(subject).to eq(expected)
|
41
|
+
}
|
32
42
|
end
|
33
43
|
|
34
44
|
context "when the value is a Regexp" do
|
@@ -36,8 +46,12 @@ class Predicate
|
|
36
46
|
let(:h){ {:x => /[a-z]+/} }
|
37
47
|
|
38
48
|
it_should_behave_like "a predicate AST node"
|
39
|
-
it{
|
40
|
-
|
49
|
+
it {
|
50
|
+
expect(subject).to be_a(Match)
|
51
|
+
}
|
52
|
+
it {
|
53
|
+
expect(subject).to eq([:match, [:identifier, :x], [:literal, rx]])
|
54
|
+
}
|
41
55
|
end
|
42
56
|
|
43
57
|
context "when the hash mixes value types" do
|
@@ -50,8 +64,12 @@ class Predicate
|
|
50
64
|
}
|
51
65
|
|
52
66
|
it_should_behave_like "a predicate AST node"
|
53
|
-
it{
|
54
|
-
|
67
|
+
it {
|
68
|
+
expect(subject).to be_a(And)
|
69
|
+
}
|
70
|
+
it {
|
71
|
+
expect(subject).to eq(expected)
|
72
|
+
}
|
55
73
|
end
|
56
74
|
|
57
75
|
end
|
data/spec/factory/test_empty.rb
CHANGED
@@ -7,43 +7,57 @@ class Predicate
|
|
7
7
|
context "on Expr" do
|
8
8
|
let(:arg){ Grammar.sexpr([:literal, 12]) }
|
9
9
|
|
10
|
-
it{
|
10
|
+
it {
|
11
|
+
expect(subject).to be(arg)
|
12
|
+
}
|
11
13
|
end
|
12
14
|
|
13
15
|
context "on true" do
|
14
16
|
let(:arg){ true }
|
15
17
|
|
16
|
-
it{
|
18
|
+
it {
|
19
|
+
expect(subject).to be_a(Tautology)
|
20
|
+
}
|
17
21
|
end
|
18
22
|
|
19
23
|
context "on false" do
|
20
24
|
let(:arg){ false }
|
21
25
|
|
22
|
-
it{
|
26
|
+
it {
|
27
|
+
expect(subject).to be_a(Contradiction)
|
28
|
+
}
|
23
29
|
end
|
24
30
|
|
25
31
|
context "on Symbol" do
|
26
32
|
let(:arg){ :name }
|
27
33
|
|
28
|
-
it{
|
34
|
+
it {
|
35
|
+
expect(subject).to be_a(Identifier)
|
36
|
+
}
|
29
37
|
end
|
30
38
|
|
31
39
|
context "on Proc" do
|
32
40
|
let(:arg){ lambda{} }
|
33
41
|
|
34
|
-
it{
|
42
|
+
it {
|
43
|
+
expect(subject).to be_a(Native)
|
44
|
+
}
|
35
45
|
end
|
36
46
|
|
37
47
|
context "on Array" do
|
38
48
|
let(:arg){ [:identifier, :name] }
|
39
49
|
|
40
|
-
it{
|
50
|
+
it {
|
51
|
+
expect(subject).to be_a(Identifier)
|
52
|
+
}
|
41
53
|
end
|
42
54
|
|
43
55
|
context "on 12" do
|
44
56
|
let(:arg){ 12 }
|
45
57
|
|
46
|
-
it{
|
58
|
+
it {
|
59
|
+
expect(subject).to be_a(Literal)
|
60
|
+
}
|
47
61
|
end
|
48
62
|
|
49
63
|
end
|
@@ -7,15 +7,21 @@ class Predicate
|
|
7
7
|
context "when the hash is empty" do
|
8
8
|
let(:h){ {} }
|
9
9
|
|
10
|
-
it{
|
10
|
+
it {
|
11
|
+
expect(subject).to eq(Factory.tautology)
|
12
|
+
}
|
11
13
|
end
|
12
14
|
|
13
15
|
context "when the hash is a singelton" do
|
14
16
|
let(:h){ {:x => 12} }
|
15
17
|
|
16
18
|
it_should_behave_like "a predicate AST node"
|
17
|
-
it{
|
18
|
-
|
19
|
+
it {
|
20
|
+
expect(subject).to be_a(Eq)
|
21
|
+
}
|
22
|
+
it {
|
23
|
+
expect(subject).to eq([:eq, [:identifier, :x], [:literal, 12]])
|
24
|
+
}
|
19
25
|
end
|
20
26
|
|
21
27
|
context "when the hash is not a singleton" do
|
@@ -27,8 +33,12 @@ class Predicate
|
|
27
33
|
}
|
28
34
|
|
29
35
|
it_should_behave_like "a predicate AST node"
|
30
|
-
it{
|
31
|
-
|
36
|
+
it {
|
37
|
+
expect(subject).to be_a(And)
|
38
|
+
}
|
39
|
+
it {
|
40
|
+
expect(subject).to eq(expected)
|
41
|
+
}
|
32
42
|
end
|
33
43
|
|
34
44
|
context "when the hash has array values" do
|
@@ -40,8 +50,12 @@ class Predicate
|
|
40
50
|
}
|
41
51
|
|
42
52
|
it_should_behave_like "a predicate AST node"
|
43
|
-
it{
|
44
|
-
|
53
|
+
it {
|
54
|
+
expect(subject).to be_a(And)
|
55
|
+
}
|
56
|
+
it {
|
57
|
+
expect(subject).to eq(expected)
|
58
|
+
}
|
45
59
|
end
|
46
60
|
|
47
61
|
end
|
@@ -6,8 +6,12 @@ class Predicate
|
|
6
6
|
subject{ identifier(:name) }
|
7
7
|
|
8
8
|
it_should_behave_like "a predicate AST node"
|
9
|
-
it{
|
10
|
-
|
9
|
+
it {
|
10
|
+
expect(subject).to be_a(Identifier)
|
11
|
+
}
|
12
|
+
it {
|
13
|
+
expect(subject).to eql([:identifier, :name])
|
14
|
+
}
|
11
15
|
|
12
16
|
end
|
13
17
|
end
|
@@ -6,8 +6,12 @@ class Predicate
|
|
6
6
|
subject{ literal(12) }
|
7
7
|
|
8
8
|
it_should_behave_like "a predicate AST node"
|
9
|
-
it{
|
10
|
-
|
9
|
+
it {
|
10
|
+
expect(subject).to be_a(Literal)
|
11
|
+
}
|
12
|
+
it {
|
13
|
+
expect(subject).to eql([:literal, 12])
|
14
|
+
}
|
11
15
|
|
12
16
|
end
|
13
17
|
end
|