predicate 2.8.0 → 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 +4 -4
- data/README.md +17 -0
- data/lib/predicate/asserter.rb +1 -1
- data/lib/predicate/nodes/and.rb +22 -4
- data/lib/predicate/nodes/empty.rb +6 -0
- data/lib/predicate/nodes/eq.rb +4 -0
- data/lib/predicate/nodes/expr.rb +4 -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 +11 -0
- data/lib/predicate/nodes/tautology.rb +4 -0
- data/lib/predicate/version.rb +1 -1
- data/lib/predicate.rb +16 -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 +210 -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 +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
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/asserter.rb
CHANGED
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
data/lib/predicate/nodes/expr.rb
CHANGED
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
@@ -16,5 +16,16 @@ class Predicate
|
|
16
16
|
l
|
17
17
|
end
|
18
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
|
+
|
19
30
|
end
|
20
31
|
end
|
data/lib/predicate/version.rb
CHANGED
data/lib/predicate.rb
CHANGED
@@ -165,10 +165,24 @@ class Predicate
|
|
165
165
|
end
|
166
166
|
|
167
167
|
# If possible, converts this predicate back to a `{ attr: value, ... }`
|
168
|
-
# hash.
|
169
|
-
#
|
168
|
+
# hash.
|
169
|
+
#
|
170
|
+
# Raises an ArgumentError if the predicate cannot be represented that way.
|
170
171
|
def to_hash
|
171
172
|
expr.to_hash
|
172
173
|
end
|
173
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
|
+
|
174
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
|
data/spec/factory/test_match.rb
CHANGED
@@ -9,9 +9,13 @@ class Predicate
|
|
9
9
|
|
10
10
|
it_should_behave_like "a predicate AST node"
|
11
11
|
|
12
|
-
it{
|
12
|
+
it {
|
13
|
+
expect(subject).to be_a(Match)
|
14
|
+
}
|
13
15
|
|
14
|
-
it{
|
16
|
+
it {
|
17
|
+
expect(subject).to eql([:match, [:identifier, :name], [:literal, "London"]])
|
18
|
+
}
|
15
19
|
end
|
16
20
|
|
17
21
|
context 'with options' do
|
@@ -19,9 +23,13 @@ class Predicate
|
|
19
23
|
|
20
24
|
it_should_behave_like "a predicate AST node"
|
21
25
|
|
22
|
-
it{
|
26
|
+
it {
|
27
|
+
expect(subject).to be_a(Match)
|
28
|
+
}
|
23
29
|
|
24
|
-
it
|
30
|
+
it {
|
31
|
+
expect(subject).to eql([:match, [:identifier, :name], [:literal, "London"], {case_sensitive: false}])
|
32
|
+
}
|
25
33
|
end
|
26
34
|
|
27
35
|
end
|
data/spec/factory/test_native.rb
CHANGED
@@ -10,9 +10,13 @@ class Predicate
|
|
10
10
|
|
11
11
|
it_should_behave_like "a predicate AST node"
|
12
12
|
|
13
|
-
it{
|
13
|
+
it {
|
14
|
+
expect(subject).to be_a(Native)
|
15
|
+
}
|
14
16
|
|
15
|
-
it{
|
17
|
+
it {
|
18
|
+
expect(subject).to eql([:native, proc])
|
19
|
+
}
|
16
20
|
end
|
17
21
|
|
18
22
|
end
|