msfl_visitors 1.3.0.dev.f → 1.3.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/Gemfile +3 -6
- data/Gemfile.lock +18 -5
- data/circle.yml +6 -1
- data/lib/msfl_visitors.rb +0 -12
- data/lib/msfl_visitors/nodes.rb +1 -0
- data/lib/msfl_visitors/nodes/or.rb +22 -0
- data/lib/msfl_visitors/parsers/msfl_parser.rb +5 -4
- data/lib/msfl_visitors/visitor.rb +16 -100
- data/msfl_visitors.gemspec +3 -2
- data/spec/msfl_visitors/nodes/or_spec.rb +21 -0
- data/spec/msfl_visitors/visitors/chewy_term_filter_spec.rb +175 -0
- metadata +21 -7
- data/spec/msfl_visitors/visitors/arel_filter_spec.rb +0 -451
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff27f36c62e1d5dc9848c0779ee2a997fa390479
|
4
|
+
data.tar.gz: e8fcbac337f97ad9d34366c1e32804a8b51c44e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3b37837e8ce9c138a42db0196c42aef3905d367bdc1d310184543578ea2626e824b97de9f1b523b744d478beaee5dbf07512811bd509e5807c4c67bcf86dce6
|
7
|
+
data.tar.gz: 466355a1f210564a0ad16c52f513d6f3f0853e1999e02b284bb9715d99710386e86f81a6224ab2f7e56dfefad3fd051d583e343ee568eb1fbbe11c65efd12b36
|
data/Gemfile
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
source
|
2
|
-
|
3
|
-
|
4
|
-
gem 'rspec' # MIT https://github.com/rspec/rspec/blob/master/License.txt
|
5
|
-
gem 'byebug'
|
6
|
-
gem 'msfl', "~> 1.2", ">=1.2.2"
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
msfl_visitors (1.3.0)
|
5
|
+
msfl (~> 1.2, >= 1.2.1)
|
6
|
+
|
1
7
|
GEM
|
2
8
|
remote: https://rubygems.org/
|
3
9
|
specs:
|
10
|
+
builder (3.2.3)
|
4
11
|
byebug (4.0.5)
|
5
12
|
columnize (= 0.9.0)
|
6
13
|
columnize (0.9.0)
|
@@ -9,6 +16,7 @@ GEM
|
|
9
16
|
json (1.8.2)
|
10
17
|
msfl (1.2.2)
|
11
18
|
json (~> 1.7)
|
19
|
+
rake (10.4.2)
|
12
20
|
rspec (3.2.0)
|
13
21
|
rspec-core (~> 3.2.0)
|
14
22
|
rspec-expectations (~> 3.2.0)
|
@@ -22,6 +30,9 @@ GEM
|
|
22
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
23
31
|
rspec-support (~> 3.2.0)
|
24
32
|
rspec-support (3.2.2)
|
33
|
+
rspec_junit_formatter (0.2.3)
|
34
|
+
builder (< 4)
|
35
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
25
36
|
simplecov (0.10.0)
|
26
37
|
docile (~> 1.1.0)
|
27
38
|
json (~> 1.8)
|
@@ -33,8 +44,10 @@ PLATFORMS
|
|
33
44
|
ruby
|
34
45
|
|
35
46
|
DEPENDENCIES
|
36
|
-
byebug
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
byebug (~> 4.0)
|
48
|
+
msfl_visitors!
|
49
|
+
rake (~> 10.3)
|
50
|
+
rspec (~> 3.2)
|
51
|
+
rspec_junit_formatter (~> 0.2)
|
52
|
+
simplecov (~> 0.10)
|
53
|
+
yard (~> 0.8)
|
data/circle.yml
CHANGED
data/lib/msfl_visitors.rb
CHANGED
@@ -15,17 +15,5 @@ module MSFLVisitors
|
|
15
15
|
ast = parser.parse nmsfl
|
16
16
|
visitor.visit_tree ast
|
17
17
|
end
|
18
|
-
|
19
|
-
def get_arel(dataset, msfl, visitor = MSFLVisitors::Visitor.new)
|
20
|
-
visitor.mode = :arel
|
21
|
-
unless dataset.is_a? MSFL::Datasets::Base
|
22
|
-
raise ArgumentError, "The first argument to MSFLVisitors.get_arel must be a descendant of MSFL::Datasets::Base."
|
23
|
-
end
|
24
|
-
parser = MSFLVisitors::Parsers::MSFLParser.new dataset
|
25
|
-
converter = MSFL::Converters::Operator.new
|
26
|
-
nmsfl = converter.run_conversions msfl
|
27
|
-
ast = parser.parse nmsfl
|
28
|
-
visitor.visit_tree ast, arel_table: dataset.arel_table
|
29
|
-
end
|
30
18
|
end
|
31
19
|
end
|
data/lib/msfl_visitors/nodes.rb
CHANGED
@@ -22,6 +22,7 @@ require_relative 'nodes/less_than_equal'
|
|
22
22
|
require_relative 'nodes/match'
|
23
23
|
require_relative 'nodes/named_value'
|
24
24
|
require_relative 'nodes/number'
|
25
|
+
require_relative 'nodes/or'
|
25
26
|
require_relative 'nodes/partial'
|
26
27
|
require_relative 'nodes/query_string'
|
27
28
|
require_relative 'nodes/range_value'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative 'iterator'
|
2
|
+
require_relative 'value'
|
3
|
+
module MSFLVisitors
|
4
|
+
module Nodes
|
5
|
+
class Or < Iterator
|
6
|
+
|
7
|
+
def initialize(set)
|
8
|
+
super
|
9
|
+
unless valid_set_children?
|
10
|
+
fail ArgumentError, "Members of child Set node of Or node must be expressions, not values, only containment Set nodes have values as children."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def valid_set_children?
|
16
|
+
set.each do |child|
|
17
|
+
return false if child.is_a?(Value)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,6 +4,7 @@ module MSFLVisitors
|
|
4
4
|
class MSFLParser
|
5
5
|
OPERATORS_TO_NODE_CLASS = {
|
6
6
|
and: Nodes::And,
|
7
|
+
or: Nodes::Or,
|
7
8
|
gt: Nodes::GreaterThan,
|
8
9
|
gte: Nodes::GreaterThanEqual,
|
9
10
|
eq: Nodes::Equal,
|
@@ -25,10 +26,10 @@ module MSFLVisitors
|
|
25
26
|
MSFLVisitors::Nodes::Number.new obj
|
26
27
|
|
27
28
|
when Hash
|
28
|
-
|
29
|
+
parse_hash obj, lhs
|
29
30
|
|
30
31
|
when MSFL::Types::Set
|
31
|
-
|
32
|
+
parse_set obj, lhs
|
32
33
|
|
33
34
|
when Symbol, String, NilClass
|
34
35
|
MSFLVisitors::Nodes::Word.new obj.to_s
|
@@ -47,7 +48,7 @@ module MSFLVisitors
|
|
47
48
|
|
48
49
|
attr_accessor :dataset
|
49
50
|
|
50
|
-
def
|
51
|
+
def parse_hash(obj, lhs = false)
|
51
52
|
nodes = Array.new
|
52
53
|
obj.each do |k, v|
|
53
54
|
nodes << hash_dispatch(k, v, lhs)
|
@@ -61,7 +62,7 @@ module MSFLVisitors
|
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
64
|
-
def
|
65
|
+
def parse_set(obj, lhs = false)
|
65
66
|
nodes = MSFL::Types::Set.new([])
|
66
67
|
obj.each do |item|
|
67
68
|
nodes << parse(item)
|
@@ -19,15 +19,11 @@ module MSFLVisitors
|
|
19
19
|
def composable_expr_for(regex_as_literal_string)
|
20
20
|
regex_as_literal_string[3..-4]
|
21
21
|
end
|
22
|
-
|
23
|
-
def coerce_value_to_unquoted(value)
|
24
|
-
return value[1..-2] if value[0] == "\""
|
25
|
-
end
|
26
22
|
end
|
27
23
|
|
28
24
|
class Visitor
|
29
25
|
|
30
|
-
attr_accessor :clauses, :current_clause
|
26
|
+
attr_accessor :clauses, :current_clause
|
31
27
|
attr_writer :mode
|
32
28
|
|
33
29
|
def initialize
|
@@ -39,8 +35,6 @@ module MSFLVisitors
|
|
39
35
|
def visit(node)
|
40
36
|
if mode == :es_term
|
41
37
|
get_visitor.visit(node)
|
42
|
-
elsif mode == :arel
|
43
|
-
get_visitor.visit(node)
|
44
38
|
else
|
45
39
|
case node
|
46
40
|
when Nodes::Partial
|
@@ -60,8 +54,6 @@ module MSFLVisitors
|
|
60
54
|
TermFilterVisitor.new(self)
|
61
55
|
when :es_term
|
62
56
|
ESTermFilterVisitor.new(self)
|
63
|
-
when :arel
|
64
|
-
ArelFilterVisitor.new(self, arel_table)
|
65
57
|
else
|
66
58
|
AggregationsVisitor.new(self)
|
67
59
|
end
|
@@ -74,11 +66,8 @@ module MSFLVisitors
|
|
74
66
|
result
|
75
67
|
end
|
76
68
|
|
77
|
-
def visit_tree(root
|
78
|
-
|
79
|
-
raise ArgumentError unless arel_table
|
80
|
-
@arel_table = arel_table.name
|
81
|
-
"#{root.accept(self)}"
|
69
|
+
def visit_tree(root)
|
70
|
+
[{clause: root.accept(self)}].concat(clauses).reject { |c| c[:clause] == "" }
|
82
71
|
end
|
83
72
|
|
84
73
|
private
|
@@ -102,7 +91,10 @@ module MSFLVisitors
|
|
102
91
|
Nodes::Match => '=~',
|
103
92
|
}
|
104
93
|
|
105
|
-
|
94
|
+
ITERATOR_OPERATORS = {
|
95
|
+
Nodes::And => "&",
|
96
|
+
Nodes::Or => "|"
|
97
|
+
}
|
106
98
|
|
107
99
|
def visit(node)
|
108
100
|
case node
|
@@ -144,11 +136,11 @@ module MSFLVisitors
|
|
144
136
|
node.contents.map { |n| "( " + n.accept(visitor) + " )" }.join(" & ")
|
145
137
|
end
|
146
138
|
|
147
|
-
when Nodes::
|
139
|
+
when Nodes::Iterator
|
148
140
|
if node.set.contents.count == 1
|
149
141
|
node.set.contents.first.accept(visitor)
|
150
142
|
else
|
151
|
-
node.set.contents.map { |n| "( " + n.accept(visitor) + " )" }.join("
|
143
|
+
node.set.contents.map { |n| "( " + n.accept(visitor) + " )" }.join(" #{ITERATOR_OPERATORS[node.class]} ")
|
152
144
|
end
|
153
145
|
|
154
146
|
when Nodes::Foreign
|
@@ -182,6 +174,11 @@ module MSFLVisitors
|
|
182
174
|
Nodes::QueryString => :query_string,
|
183
175
|
}
|
184
176
|
|
177
|
+
ITERATOR_OPERATORS = {
|
178
|
+
Nodes::And => :and,
|
179
|
+
Nodes::Or => :or,
|
180
|
+
}
|
181
|
+
|
185
182
|
def visit(node)
|
186
183
|
case node
|
187
184
|
when Nodes::Partial
|
@@ -240,8 +237,8 @@ module MSFLVisitors
|
|
240
237
|
else
|
241
238
|
{ and: node.contents.map { |n| n.accept(visitor) } }
|
242
239
|
end
|
243
|
-
when Nodes::
|
244
|
-
{
|
240
|
+
when Nodes::Iterator
|
241
|
+
{ ITERATOR_OPERATORS[node.class] => node.set.accept(visitor) }
|
245
242
|
|
246
243
|
when Nodes::Foreign
|
247
244
|
{ foreign: Hash[[[:type, node.left.accept(visitor)], [:filter, node.right.accept(visitor)]]] }
|
@@ -256,87 +253,6 @@ module MSFLVisitors
|
|
256
253
|
attr_reader :visitor
|
257
254
|
end
|
258
255
|
|
259
|
-
class ArelFilterVisitor
|
260
|
-
include VisitorHelpers
|
261
|
-
|
262
|
-
attr_accessor :arel_table
|
263
|
-
|
264
|
-
def initialize(visitor, arel_table)
|
265
|
-
@visitor = visitor
|
266
|
-
@arel_table = arel_table
|
267
|
-
end
|
268
|
-
|
269
|
-
BINARY_OPERATORS = {
|
270
|
-
Nodes::Containment => 'in',
|
271
|
-
Nodes::GreaterThan => 'gt',
|
272
|
-
Nodes::GreaterThanEqual => 'gte',
|
273
|
-
Nodes::Equal => 'eq',
|
274
|
-
Nodes::LessThan => 'lt',
|
275
|
-
Nodes::LessThanEqual => 'lte',
|
276
|
-
Nodes::Match => '=~',
|
277
|
-
}
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
def visit(node)
|
282
|
-
case node
|
283
|
-
when Nodes::Field
|
284
|
-
"#{node.value.to_sym}"
|
285
|
-
when Nodes::Regex
|
286
|
-
regex_escape node.value.to_s
|
287
|
-
when Nodes::Word
|
288
|
-
"\"#{node.value}\""
|
289
|
-
when Nodes::Date, Nodes::Time
|
290
|
-
"\"#{node.value.iso8601}\""
|
291
|
-
when Nodes::Number, Nodes::Boolean
|
292
|
-
node.value
|
293
|
-
|
294
|
-
when Nodes::QueryString
|
295
|
-
%(#{node.left.accept(visitor)} LIKE '%#{coerce_value_to_unquoted node.right.accept(visitor)}%')
|
296
|
-
|
297
|
-
when Nodes::Match
|
298
|
-
if node.right.is_a? Nodes::Set
|
299
|
-
escaped_str_frags = node.right.contents.map { |right_child| composable_expr_for(MSFLVisitors::Nodes::Regex.new(right_child.value.to_s).accept(visitor).inspect) }
|
300
|
-
escaped_str = "(" + escaped_str_frags.join('|') + ")"
|
301
|
-
"#{node.left.accept(visitor)} #{BINARY_OPERATORS[node.class]} " + %r[.*#{escaped_str}.*].inspect
|
302
|
-
else
|
303
|
-
"#{node.left.accept(visitor)} #{BINARY_OPERATORS[node.class]} " + MSFLVisitors::Nodes::Regex.new(node.right.value.to_s).accept(visitor).inspect
|
304
|
-
end
|
305
|
-
when Nodes::Containment,
|
306
|
-
Nodes::GreaterThan,
|
307
|
-
Nodes::GreaterThanEqual,
|
308
|
-
Nodes::Equal,
|
309
|
-
Nodes::LessThan,
|
310
|
-
Nodes::LessThanEqual
|
311
|
-
%(#{arel_table}[:#{node.left.accept(visitor)}].#{BINARY_OPERATORS[node.class]}(#{node.right.accept(visitor)}))
|
312
|
-
when Nodes::Set
|
313
|
-
"[" + node.contents.map { |n| n.accept(visitor) }.join(", ") + "]"
|
314
|
-
when Nodes::Filter
|
315
|
-
node.contents.reduce("") { |res, n|
|
316
|
-
next(n.accept(visitor)) unless res.length > 0
|
317
|
-
res + ".and(#{n.accept(visitor)})"
|
318
|
-
}
|
319
|
-
when Nodes::And
|
320
|
-
node.set.contents.reduce("") { |res, n|
|
321
|
-
next(n.accept(visitor)) unless res.length > 0
|
322
|
-
res + ".and(#{n.accept(visitor)})"
|
323
|
-
}
|
324
|
-
when Nodes::Foreign
|
325
|
-
"#{node.left.accept visitor}.filter { #{node.right.accept visitor} }"
|
326
|
-
|
327
|
-
when Nodes::Dataset
|
328
|
-
"has_child( :#{node.value} )"
|
329
|
-
|
330
|
-
else
|
331
|
-
fail ArgumentError, "ArelFilter cannot visit: #{node.class.name}"
|
332
|
-
end
|
333
|
-
end
|
334
|
-
|
335
|
-
private
|
336
|
-
|
337
|
-
attr_reader :visitor
|
338
|
-
end
|
339
|
-
|
340
256
|
# ESTermFilterVisitor is not currently used and so not all node types are implemented
|
341
257
|
class ESTermFilterVisitor
|
342
258
|
def initialize(visitor)
|
data/msfl_visitors.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'msfl_visitors'
|
3
|
-
s.version = '1.3.0
|
4
|
-
s.date = '
|
3
|
+
s.version = '1.3.0'
|
4
|
+
s.date = '2017-04-06'
|
5
5
|
s.summary = "Convert MSFL to other forms"
|
6
6
|
s.description = "Visitor pattern approach to converting MSFL to other forms."
|
7
7
|
s.authors = ["Courtland Caldwell"]
|
@@ -16,5 +16,6 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_development_dependency "yard", "~> 0.8"
|
17
17
|
s.add_development_dependency "rspec", "~> 3.2"
|
18
18
|
s.add_development_dependency "byebug", "~> 4.0"
|
19
|
+
s.add_development_dependency "rspec_junit_formatter", "~> 0.2" # MIT https://github.com/sj26/rspec_junit_formatter/blob/master/LICENSE
|
19
20
|
s.license = "MIT"
|
20
21
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFLVisitors::Nodes::Or do
|
4
|
+
|
5
|
+
context "when creating an instance of #{described_class}" do
|
6
|
+
|
7
|
+
subject { described_class.new node }
|
8
|
+
|
9
|
+
context "when passed as Set node as the argument to the constructor" do
|
10
|
+
|
11
|
+
let(:node) { MSFLVisitors::Nodes::Set.new [MSFLVisitors::Nodes::Number.new(1)] }
|
12
|
+
|
13
|
+
context "when the Set contains any nodes which inherit from Value" do
|
14
|
+
|
15
|
+
it "raises an ArgumentError" do
|
16
|
+
expect { subject }.to raise_error ArgumentError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -731,6 +731,181 @@ describe MSFLVisitors::Visitor do
|
|
731
731
|
end
|
732
732
|
end
|
733
733
|
|
734
|
+
describe "an Or node" do
|
735
|
+
|
736
|
+
let(:first_field) { MSFLVisitors::Nodes::Field.new "first_field" }
|
737
|
+
|
738
|
+
let(:first_value) { MSFLVisitors::Nodes::Word.new "first_word" }
|
739
|
+
|
740
|
+
let(:first) { MSFLVisitors::Nodes::Equal.new first_field, first_value }
|
741
|
+
|
742
|
+
let(:second_field) { MSFLVisitors::Nodes::Field.new "second_field" }
|
743
|
+
|
744
|
+
let(:second_value) { MSFLVisitors::Nodes::Word.new "second_word" }
|
745
|
+
|
746
|
+
let(:second) { MSFLVisitors::Nodes::Equal.new second_field, second_value }
|
747
|
+
|
748
|
+
let(:third_field) { MSFLVisitors::Nodes::Field.new "third_field" }
|
749
|
+
|
750
|
+
let(:third_value) { MSFLVisitors::Nodes::Word.new "third_word" }
|
751
|
+
|
752
|
+
let(:third) { MSFLVisitors::Nodes::Equal.new third_field, third_value }
|
753
|
+
|
754
|
+
let(:node) { MSFLVisitors::Nodes::Or.new set_node }
|
755
|
+
|
756
|
+
context "when the Or node has zero items" do
|
757
|
+
|
758
|
+
let(:set_node) { MSFLVisitors::Nodes::Set.new [] }
|
759
|
+
|
760
|
+
context "when using the TermFilter visitor" do
|
761
|
+
|
762
|
+
it "is empty" do
|
763
|
+
expect(result).to be_empty
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
context "when using the Aggregations visitor" do
|
768
|
+
|
769
|
+
before { visitor.mode = :aggregations }
|
770
|
+
|
771
|
+
it "returns: { and: [] }" do
|
772
|
+
expect(result).to eq({ or: [] })
|
773
|
+
end
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
context "when the node has one item" do
|
778
|
+
|
779
|
+
let(:set_node) { MSFLVisitors::Nodes::Set.new [first] }
|
780
|
+
|
781
|
+
context "when using the TermFilter visitor" do
|
782
|
+
|
783
|
+
it "returns: the item without adding parentheses" do
|
784
|
+
expect(result).to eq 'first_field == "first_word"'
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
context "when using the Aggregations visitor" do
|
789
|
+
|
790
|
+
before { visitor.mode = :aggregations }
|
791
|
+
|
792
|
+
it "returns: { or: [{ agg_field_name: :first_field, operator: :eq, test_value: \"first_word\" }]}" do
|
793
|
+
expect(result).to eq({ or: [{ agg_field_name: :first_field, operator: :eq, test_value: "first_word" }]})
|
794
|
+
end
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
context "when the node has two items" do
|
799
|
+
|
800
|
+
let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second] }
|
801
|
+
|
802
|
+
context "when using the TermFilter visitor" do
|
803
|
+
|
804
|
+
it "returns: '( first_field == \"first_word\" ) | ( second_field == \"second_word\" )'" do
|
805
|
+
expect(result).to eq '( first_field == "first_word" ) | ( second_field == "second_word" )'
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
context "when using the Aggregations visitor" do
|
810
|
+
|
811
|
+
before { visitor.mode = :aggregations }
|
812
|
+
|
813
|
+
it "returns: {
|
814
|
+
or: [{ agg_field_name: :first_field, operator: :eq, test_value: \"first_word\" },
|
815
|
+
{ agg_field_name: :second_field, operator: :eq, test_value: \"second_word\" }
|
816
|
+
]}" do
|
817
|
+
expect(result).to eq({
|
818
|
+
or: [{ agg_field_name: :first_field, operator: :eq, test_value: "first_word" },
|
819
|
+
{ agg_field_name: :second_field, operator: :eq, test_value: "second_word" }
|
820
|
+
]})
|
821
|
+
end
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
context "when the node has three items" do
|
826
|
+
|
827
|
+
let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second, third] }
|
828
|
+
|
829
|
+
context "when using the TermFilter visitor" do
|
830
|
+
|
831
|
+
it "returns: '( first_field == \"first_word\" ) | ( second_field == \"second_word\" ) | ( third_field == \"third_word\" )'" do
|
832
|
+
expect(result).to eq '( first_field == "first_word" ) | ( second_field == "second_word" ) | ( third_field == "third_word" )'
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
context "when using the Aggregations visitor" do
|
837
|
+
|
838
|
+
before { visitor.mode = :aggregations }
|
839
|
+
|
840
|
+
it "returns: {
|
841
|
+
or: [{ agg_field_name: :first_field, operator: :eq, test_value: \"first_word\" },
|
842
|
+
{ agg_field_name: :second_field, operator: :eq, test_value: \"second_word\"},
|
843
|
+
{agg_field_name: :third_field, operator: :eq, test_value: \"third_word\"}
|
844
|
+
]}" do
|
845
|
+
expect(result).to eq({
|
846
|
+
or: [{ agg_field_name: :first_field, operator: :eq, test_value: "first_word" },
|
847
|
+
{ agg_field_name: :second_field, operator: :eq, test_value: "second_word"},
|
848
|
+
{agg_field_name: :third_field, operator: :eq, test_value: "third_word"}
|
849
|
+
]})
|
850
|
+
end
|
851
|
+
end
|
852
|
+
end
|
853
|
+
|
854
|
+
context "when one of the node's items is a containment node" do
|
855
|
+
|
856
|
+
let(:node) do
|
857
|
+
MSFLVisitors::Nodes::Or.new(
|
858
|
+
MSFLVisitors::Nodes::Set.new(
|
859
|
+
[
|
860
|
+
MSFLVisitors::Nodes::Filter.new(
|
861
|
+
[
|
862
|
+
MSFLVisitors::Nodes::Containment.new(
|
863
|
+
MSFLVisitors::Nodes::Field.new(:make),
|
864
|
+
MSFLVisitors::Nodes::Set.new(
|
865
|
+
[
|
866
|
+
MSFLVisitors::Nodes::Word.new("Honda"),
|
867
|
+
MSFLVisitors::Nodes::Word.new("Chevy"),
|
868
|
+
MSFLVisitors::Nodes::Word.new("Volvo")
|
869
|
+
]
|
870
|
+
)
|
871
|
+
)
|
872
|
+
]
|
873
|
+
),
|
874
|
+
MSFLVisitors::Nodes::Filter.new(
|
875
|
+
[
|
876
|
+
MSFLVisitors::Nodes::GreaterThanEqual.new(
|
877
|
+
MSFLVisitors::Nodes::Field.new(:value),
|
878
|
+
MSFLVisitors::Nodes::Number.new(1000)
|
879
|
+
)
|
880
|
+
]
|
881
|
+
)
|
882
|
+
]
|
883
|
+
)
|
884
|
+
)
|
885
|
+
end
|
886
|
+
|
887
|
+
context "when using the TermFilter visitor" do
|
888
|
+
|
889
|
+
it "returns: '( make == [ \"Honda\" , \"Chevy\" , \"Volvo\" ] ) | ( value >= 1000 )'" do
|
890
|
+
expect(result).to eq '( make == [ "Honda" , "Chevy" , "Volvo" ] ) | ( value >= 1000 )'
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
context "when using the Aggregations visitor" do
|
895
|
+
|
896
|
+
before { visitor.mode = :aggregations }
|
897
|
+
|
898
|
+
it "returns: { or: [{ agg_field_name: :make, operator: :in, test_value: [\"Honda\", \"Chevy\", \"Volvo\"] }, { agg_field_name: :value, operator: :gte, test_value: 1000 } }] }" do
|
899
|
+
expected = { or: [
|
900
|
+
{ agg_field_name: :make, operator: :in, test_value: ["Honda", "Chevy", "Volvo"] },
|
901
|
+
{ agg_field_name: :value, operator: :gte, test_value: 1000}
|
902
|
+
]}
|
903
|
+
expect(result).to eq expected
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
end
|
908
|
+
|
734
909
|
describe "value nodes" do
|
735
910
|
describe "a Boolean node" do
|
736
911
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl_visitors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.0
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Courtland Caldwell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msfl
|
@@ -100,6 +100,20 @@ dependencies:
|
|
100
100
|
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '4.0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rspec_junit_formatter
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.2'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.2'
|
103
117
|
description: Visitor pattern approach to converting MSFL to other forms.
|
104
118
|
email: courtland@mattermark.com
|
105
119
|
executables: []
|
@@ -138,6 +152,7 @@ files:
|
|
138
152
|
- lib/msfl_visitors/nodes/match.rb
|
139
153
|
- lib/msfl_visitors/nodes/named_value.rb
|
140
154
|
- lib/msfl_visitors/nodes/number.rb
|
155
|
+
- lib/msfl_visitors/nodes/or.rb
|
141
156
|
- lib/msfl_visitors/nodes/partial.rb
|
142
157
|
- lib/msfl_visitors/nodes/query_string.rb
|
143
158
|
- lib/msfl_visitors/nodes/range_value.rb
|
@@ -152,9 +167,9 @@ files:
|
|
152
167
|
- simplecov_custom_profiles.rb
|
153
168
|
- spec/msfl_visitors/nodes/and_spec.rb
|
154
169
|
- spec/msfl_visitors/nodes/iterator_spec.rb
|
170
|
+
- spec/msfl_visitors/nodes/or_spec.rb
|
155
171
|
- spec/msfl_visitors/parsers/msfl_parser_spec.rb
|
156
172
|
- spec/msfl_visitors/visitor_spec.rb
|
157
|
-
- spec/msfl_visitors/visitors/arel_filter_spec.rb
|
158
173
|
- spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
|
159
174
|
- spec/msfl_visitors/visitors/es_term_filter_spec.rb
|
160
175
|
- spec/msfl_visitors_spec.rb
|
@@ -174,9 +189,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
174
189
|
version: '0'
|
175
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
191
|
requirements:
|
177
|
-
- - "
|
192
|
+
- - ">="
|
178
193
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
194
|
+
version: '0'
|
180
195
|
requirements: []
|
181
196
|
rubyforge_project:
|
182
197
|
rubygems_version: 2.4.3
|
@@ -186,11 +201,10 @@ summary: Convert MSFL to other forms
|
|
186
201
|
test_files:
|
187
202
|
- spec/msfl_visitors/nodes/and_spec.rb
|
188
203
|
- spec/msfl_visitors/nodes/iterator_spec.rb
|
204
|
+
- spec/msfl_visitors/nodes/or_spec.rb
|
189
205
|
- spec/msfl_visitors/parsers/msfl_parser_spec.rb
|
190
206
|
- spec/msfl_visitors/visitor_spec.rb
|
191
|
-
- spec/msfl_visitors/visitors/arel_filter_spec.rb
|
192
207
|
- spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
|
193
208
|
- spec/msfl_visitors/visitors/es_term_filter_spec.rb
|
194
209
|
- spec/msfl_visitors_spec.rb
|
195
210
|
- spec/spec_helper.rb
|
196
|
-
has_rdoc:
|
@@ -1,451 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe MSFLVisitors::Visitor do
|
4
|
-
|
5
|
-
let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
|
6
|
-
|
7
|
-
let(:visitor) { described_class.new }
|
8
|
-
|
9
|
-
let(:left) { MSFLVisitors::Nodes::Field.new "lhs" }
|
10
|
-
|
11
|
-
let(:right) { MSFLVisitors::Nodes::Word.new "rhs" }
|
12
|
-
|
13
|
-
subject(:result) { node.accept visitor }
|
14
|
-
|
15
|
-
context "when using the ArelFilter visitor" do
|
16
|
-
|
17
|
-
before {
|
18
|
-
visitor.mode = :arel
|
19
|
-
visitor.arel_table = :cars
|
20
|
-
}
|
21
|
-
|
22
|
-
context "when visiting" do
|
23
|
-
|
24
|
-
describe "an unsupported node" do
|
25
|
-
|
26
|
-
class UnsupportedNode
|
27
|
-
|
28
|
-
def accept(visitor)
|
29
|
-
visitor.visit self
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
let(:node) { UnsupportedNode.new }
|
34
|
-
|
35
|
-
it "raises an ArgumentError" do
|
36
|
-
expect { subject }.to raise_error ArgumentError
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# describe "a Partial node" do
|
41
|
-
#
|
42
|
-
# let(:node) { MSFLVisitors::Nodes::Partial.new given_node, named_value }
|
43
|
-
#
|
44
|
-
# let(:given_node) { MSFLVisitors::Nodes::Given.new [given_equal_node] }
|
45
|
-
#
|
46
|
-
# let(:given_equal_node) { MSFLVisitors::Nodes::Equal.new given_field_node, given_value_node }
|
47
|
-
#
|
48
|
-
# let(:given_field_node) { MSFLVisitors::Nodes::Field.new :make }
|
49
|
-
#
|
50
|
-
# let(:given_value_node) { MSFLVisitors::Nodes::Word.new "Toyota" }
|
51
|
-
#
|
52
|
-
#
|
53
|
-
# let(:named_value) { MSFLVisitors::Nodes::NamedValue.new MSFLVisitors::Nodes::Word.new("partial"), explicit_filter_node }
|
54
|
-
#
|
55
|
-
# let(:explicit_filter_node) { MSFLVisitors::Nodes::ExplicitFilter.new [greater_than_node] }
|
56
|
-
#
|
57
|
-
# let(:greater_than_node) { MSFLVisitors::Nodes::GreaterThan.new field_node, value_node }
|
58
|
-
#
|
59
|
-
# let(:field_node) { MSFLVisitors::Nodes::Field.new :age }
|
60
|
-
#
|
61
|
-
# let(:value_node) { MSFLVisitors::Nodes::Number.new 10 }
|
62
|
-
#
|
63
|
-
#
|
64
|
-
# subject { visitor.visit_tree node }
|
65
|
-
#
|
66
|
-
# it "results in the appropriate clause" do
|
67
|
-
# exp = [{
|
68
|
-
# clause: {
|
69
|
-
# given: {
|
70
|
-
# filter: {
|
71
|
-
# term: { make: "Toyota" }
|
72
|
-
# },
|
73
|
-
# aggs: {
|
74
|
-
# partial: {
|
75
|
-
# filter: { range: { age: { gt: 10 }}}
|
76
|
-
# }
|
77
|
-
# }
|
78
|
-
# }
|
79
|
-
# }
|
80
|
-
# }]
|
81
|
-
# expect(subject).to eq exp
|
82
|
-
# end
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# describe "a Given node" do
|
86
|
-
#
|
87
|
-
# let(:node) { MSFLVisitors::Nodes::Given.new [given_equal_node] }
|
88
|
-
#
|
89
|
-
# let(:given_equal_node) { MSFLVisitors::Nodes::Equal.new given_field_node, given_value_node }
|
90
|
-
#
|
91
|
-
# let(:given_field_node) { MSFLVisitors::Nodes::Field.new :make }
|
92
|
-
#
|
93
|
-
# let(:given_value_node) { MSFLVisitors::Nodes::Word.new "Toyota" }
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# it "results in: [:filter, { term: { make: \"Toyota\" } }]" do
|
97
|
-
# expect(subject).to eq([:filter, { term: { make: "Toyota" } }])
|
98
|
-
# end
|
99
|
-
# end
|
100
|
-
#
|
101
|
-
# describe "a Foreign node" do
|
102
|
-
#
|
103
|
-
# let(:node) { MSFLVisitors::Nodes::Foreign.new dataset_node, filter_node }
|
104
|
-
#
|
105
|
-
# let(:dataset_node) { MSFLVisitors::Nodes::Dataset.new "person" }
|
106
|
-
#
|
107
|
-
# let(:filter_node) { MSFLVisitors::Nodes::ExplicitFilter.new [equal_node] }
|
108
|
-
#
|
109
|
-
# let(:equal_node) { MSFLVisitors::Nodes::Equal.new left, right }
|
110
|
-
#
|
111
|
-
# let(:left) { MSFLVisitors::Nodes::Field.new :age }
|
112
|
-
#
|
113
|
-
# let(:right) { MSFLVisitors::Nodes::Number.new 25 }
|
114
|
-
#
|
115
|
-
# it "results in: { has_child: { type: \"person\", filter: { term: { age: 25 } } } }" do
|
116
|
-
# expect(subject).to eq({ has_child: { type: "person", filter: { term: { age: 25 } } } })
|
117
|
-
# end
|
118
|
-
# end
|
119
|
-
|
120
|
-
describe "a Containment node" do
|
121
|
-
|
122
|
-
let(:node) { MSFLVisitors::Nodes::Containment.new field, values }
|
123
|
-
|
124
|
-
let(:values) { MSFLVisitors::Nodes::Set.new(MSFL::Types::Set.new([item_one, item_two, item_three])) }
|
125
|
-
|
126
|
-
let(:item_one) { MSFLVisitors::Nodes::Word.new "item_one" }
|
127
|
-
|
128
|
-
let(:item_two) { MSFLVisitors::Nodes::Word.new "item_two" }
|
129
|
-
|
130
|
-
let(:item_three) { MSFLVisitors::Nodes::Word.new "item_three" }
|
131
|
-
|
132
|
-
let(:field) { left }
|
133
|
-
|
134
|
-
it %(results in: 'cars[:lhs].in(["item_one", "item_two", "item_three"])') do
|
135
|
-
expect(subject).to eq(%(cars[:lhs].in(["item_one", "item_two", "item_three"])))
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
describe "a Set node" do
|
140
|
-
|
141
|
-
let(:node) { MSFLVisitors::Nodes::Set.new values }
|
142
|
-
|
143
|
-
let(:values) { MSFL::Types::Set.new([item_one, item_two]) }
|
144
|
-
|
145
|
-
let(:item_one) { MSFLVisitors::Nodes::Word.new "item_one" }
|
146
|
-
|
147
|
-
let(:item_two) { MSFLVisitors::Nodes::Word.new "item_two" }
|
148
|
-
|
149
|
-
it %(results in: '["item_one", "item_two"]') do
|
150
|
-
expect(result).to eq %(["item_one", "item_two"])
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
describe "an Equal node" do
|
155
|
-
|
156
|
-
let(:node) { MSFLVisitors::Nodes::Equal.new left, right }
|
157
|
-
|
158
|
-
it %(results in: 'cars[:lhs].eq("rhs")') do
|
159
|
-
expect(result).to eq(%(cars[:lhs].eq("rhs")))
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
#
|
164
|
-
# Models::Investor.arel_table.project(Arel.sql('*')).where(send MSFLVisitors.get_arel(MSFL::Datasets::Investor.new, { linkedin_id: { gt: 200 } })).to_a
|
165
|
-
#
|
166
|
-
describe "a GreaterThan node" do
|
167
|
-
|
168
|
-
let(:node) { MSFLVisitors::Nodes::GreaterThan.new left, right }
|
169
|
-
|
170
|
-
let(:right) { MSFLVisitors::Nodes::Number.new 1000 }
|
171
|
-
|
172
|
-
it %(results in: 'cars[:lhs].gt(1000)') do
|
173
|
-
expect(result).to eq(%(cars[:lhs].gt(1000)))
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
describe "a GreaterThanEqual node" do
|
178
|
-
|
179
|
-
let(:node) { MSFLVisitors::Nodes::GreaterThanEqual.new left, right }
|
180
|
-
|
181
|
-
let(:right) { MSFLVisitors::Nodes::Number.new 10.52 }
|
182
|
-
|
183
|
-
it %(results in: 'cars[:lhs].gte(10.52)') do
|
184
|
-
expect(result).to eq(%(cars[:lhs].gte(10.52)))
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
describe "a LessThan node" do
|
189
|
-
|
190
|
-
let(:node) { MSFLVisitors::Nodes::LessThan.new left, right }
|
191
|
-
|
192
|
-
let(:right) { MSFLVisitors::Nodes::Number.new 133.7 }
|
193
|
-
|
194
|
-
it %(returns: 'cars[:lhs].lt(133.7)') do
|
195
|
-
expect(result).to eq(%(cars[:lhs].lt(133.7)))
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
describe "a LessThanEqual node" do
|
200
|
-
|
201
|
-
let(:node) { MSFLVisitors::Nodes::LessThanEqual.new left, right }
|
202
|
-
|
203
|
-
let(:right) { MSFLVisitors::Nodes::Date.new Date.today }
|
204
|
-
|
205
|
-
it %(returns: 'cars[:lhs].lte(#{Date.today})') do
|
206
|
-
expect(result).to eq(%(cars[:lhs].lte("#{Date.today}")))
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
describe "a QueryString node" do
|
211
|
-
|
212
|
-
let(:node) { MSFLVisitors::Nodes::QueryString.new left, right }
|
213
|
-
|
214
|
-
let(:right) { MSFLVisitors::Nodes::Word.new "happy" }
|
215
|
-
|
216
|
-
it %(returns: "lhs LIKE '%happy%'") do
|
217
|
-
expect(result).to eq(%(lhs LIKE '%happy%'))
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
describe "a Filter node" do
|
222
|
-
|
223
|
-
let(:node) { MSFLVisitors::Nodes::Filter.new filtered_nodes }
|
224
|
-
|
225
|
-
let(:filtered_nodes) do
|
226
|
-
[
|
227
|
-
MSFLVisitors::Nodes::GreaterThanEqual.new(
|
228
|
-
MSFLVisitors::Nodes::Field.new(:value),
|
229
|
-
MSFLVisitors::Nodes::Number.new(1000))
|
230
|
-
]
|
231
|
-
end
|
232
|
-
|
233
|
-
it %(returns: 'cars[:value].gte(1000)') do
|
234
|
-
expect(result).to eq(%(cars[:value].gte(1000)))
|
235
|
-
end
|
236
|
-
|
237
|
-
context "when the filter has multiple children" do
|
238
|
-
|
239
|
-
let(:filtered_nodes) do
|
240
|
-
[
|
241
|
-
MSFLVisitors::Nodes::Equal.new(
|
242
|
-
MSFLVisitors::Nodes::Field.new(:make),
|
243
|
-
MSFLVisitors::Nodes::Word.new("Chevy")
|
244
|
-
),
|
245
|
-
MSFLVisitors::Nodes::GreaterThanEqual.new(
|
246
|
-
MSFLVisitors::Nodes::Field.new(:value),
|
247
|
-
MSFLVisitors::Nodes::Number.new(1000))
|
248
|
-
]
|
249
|
-
end
|
250
|
-
|
251
|
-
it %(returns: 'cars[:make].eq("Chevy").and(cars[:value].gte(1000))') do
|
252
|
-
expect(result).to eq(%(cars[:make].eq("Chevy").and(cars[:value].gte(1000))))
|
253
|
-
end
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
describe "an And node" do
|
258
|
-
|
259
|
-
let(:first_field) { MSFLVisitors::Nodes::Field.new "first_field" }
|
260
|
-
|
261
|
-
let(:first_value) { MSFLVisitors::Nodes::Word.new "first_word" }
|
262
|
-
|
263
|
-
let(:first) { MSFLVisitors::Nodes::Equal.new(first_field, first_value) }
|
264
|
-
|
265
|
-
let(:second_field) { MSFLVisitors::Nodes::Field.new "second_field" }
|
266
|
-
|
267
|
-
let(:second_value) { MSFLVisitors::Nodes::Word.new "second_word" }
|
268
|
-
|
269
|
-
let(:second) { MSFLVisitors::Nodes::Equal.new(second_field, second_value) }
|
270
|
-
|
271
|
-
let(:third_field) { MSFLVisitors::Nodes::Field.new "third_field" }
|
272
|
-
|
273
|
-
let(:third_value) { MSFLVisitors::Nodes::Word.new "third_word" }
|
274
|
-
|
275
|
-
let(:third) { MSFLVisitors::Nodes::Equal.new(third_field, third_value) }
|
276
|
-
|
277
|
-
let(:node) { MSFLVisitors::Nodes::And.new(set_node) }
|
278
|
-
|
279
|
-
context "when the And node has zero items" do
|
280
|
-
|
281
|
-
let(:set_node) { MSFLVisitors::Nodes::Set.new [] }
|
282
|
-
|
283
|
-
it %(returns: '') do
|
284
|
-
expect(result).to eq('')
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
context "when the node has one item" do
|
289
|
-
|
290
|
-
let(:set_node) { MSFLVisitors::Nodes::Set.new [first] }
|
291
|
-
|
292
|
-
it %(returns: 'cars[:first_field].eq("first_word")') do
|
293
|
-
expect(result).to eq(%(cars[:first_field].eq("first_word")))
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
context "when the node has two items" do
|
298
|
-
|
299
|
-
let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second] }
|
300
|
-
|
301
|
-
it %(returns: 'cars[:first_field].eq("first_word").and(cars[:second_field].eq("second_word"))') do
|
302
|
-
expect(result).to eq(%(cars[:first_field].eq("first_word").and(cars[:second_field].eq("second_word"))))
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
context "when the node has three items" do
|
307
|
-
|
308
|
-
let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second, third] }
|
309
|
-
|
310
|
-
it %(returns: 'cars[:first_field].eq("first_word").and(cars[:second_field].eq("second_word")).and(cars[:third_field].eq("third_word"))') do
|
311
|
-
expect(result).to eq(%(cars[:first_field].eq("first_word").and(cars[:second_field].eq("second_word")).and(cars[:third_field].eq("third_word"))))
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
context "when one of the node's items is a containment node" do
|
316
|
-
|
317
|
-
let(:node) do
|
318
|
-
MSFLVisitors::Nodes::And.new(
|
319
|
-
MSFLVisitors::Nodes::Set.new(
|
320
|
-
[
|
321
|
-
MSFLVisitors::Nodes::Filter.new(
|
322
|
-
[
|
323
|
-
MSFLVisitors::Nodes::Containment.new(
|
324
|
-
MSFLVisitors::Nodes::Field.new(:make),
|
325
|
-
MSFLVisitors::Nodes::Set.new(
|
326
|
-
[
|
327
|
-
MSFLVisitors::Nodes::Word.new("Honda"),
|
328
|
-
MSFLVisitors::Nodes::Word.new("Chevy"),
|
329
|
-
MSFLVisitors::Nodes::Word.new("Volvo")
|
330
|
-
]
|
331
|
-
)
|
332
|
-
)
|
333
|
-
]
|
334
|
-
),
|
335
|
-
MSFLVisitors::Nodes::Filter.new(
|
336
|
-
[
|
337
|
-
MSFLVisitors::Nodes::GreaterThanEqual.new(
|
338
|
-
MSFLVisitors::Nodes::Field.new(:value),
|
339
|
-
MSFLVisitors::Nodes::Number.new(1000)
|
340
|
-
)
|
341
|
-
]
|
342
|
-
)
|
343
|
-
]
|
344
|
-
)
|
345
|
-
)
|
346
|
-
end
|
347
|
-
it %(returns: 'cars[:make].in(["Honda", "Chevy", "Volvo"]).and(cars[:value].gte(1000))') do
|
348
|
-
expected = %(cars[:make].in(["Honda", "Chevy", "Volvo"]).and(cars[:value].gte(1000)))
|
349
|
-
expect(result).to eq expected
|
350
|
-
end
|
351
|
-
end
|
352
|
-
end
|
353
|
-
|
354
|
-
describe "value nodes" do
|
355
|
-
describe "a Boolean node" do
|
356
|
-
|
357
|
-
let(:node) { MSFLVisitors::Nodes::Boolean.new value }
|
358
|
-
|
359
|
-
subject(:result) { node.accept visitor }
|
360
|
-
|
361
|
-
context "with a value of true" do
|
362
|
-
|
363
|
-
let(:value) { true }
|
364
|
-
|
365
|
-
it "returns: true" do
|
366
|
-
expect(result).to eq true
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
context "with a value of false" do
|
371
|
-
|
372
|
-
let(:value) { false }
|
373
|
-
|
374
|
-
it "returns: false" do
|
375
|
-
expect(result).to eq false
|
376
|
-
end
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
describe "a Word node" do
|
381
|
-
|
382
|
-
let(:word) { "node_content" }
|
383
|
-
|
384
|
-
let(:node) { MSFLVisitors::Nodes::Word.new word }
|
385
|
-
|
386
|
-
it "returns: the literal string" do
|
387
|
-
expect(result).to eq "\"#{word}\""
|
388
|
-
end
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
describe "range value nodes" do
|
393
|
-
|
394
|
-
subject(:result) { node.accept visitor }
|
395
|
-
|
396
|
-
describe "a Date node" do
|
397
|
-
|
398
|
-
let(:today) { Date.today }
|
399
|
-
|
400
|
-
let(:node) { MSFLVisitors::Nodes::Date.new today }
|
401
|
-
|
402
|
-
it "returns: the date using iso8601 formatting" do
|
403
|
-
expect(result).to eq "\"#{today.iso8601}\""
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
describe "a Time node" do
|
408
|
-
|
409
|
-
let(:now) { Time.now }
|
410
|
-
|
411
|
-
let(:node) { MSFLVisitors::Nodes::Time.new now }
|
412
|
-
|
413
|
-
it "returns: the date using iso8601 formatting" do
|
414
|
-
expect(result).to eq "\"#{now.iso8601}\""
|
415
|
-
end
|
416
|
-
end
|
417
|
-
|
418
|
-
describe "a DateTime node" do
|
419
|
-
|
420
|
-
let(:now) { DateTime.now }
|
421
|
-
|
422
|
-
let(:node) { MSFLVisitors::Nodes::DateTime.new now }
|
423
|
-
|
424
|
-
it "returns: the date and time using iso8601 formatting" do
|
425
|
-
expect(result).to eq "\"#{now.iso8601}\""
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
describe "a Number node" do
|
430
|
-
|
431
|
-
let(:number) { 123 }
|
432
|
-
|
433
|
-
let(:node) { MSFLVisitors::Nodes::Number.new number }
|
434
|
-
|
435
|
-
it "returns: 123" do
|
436
|
-
expect(result).to eq number
|
437
|
-
end
|
438
|
-
|
439
|
-
context "when the number is a float" do
|
440
|
-
|
441
|
-
let(:number) { 123.456 }
|
442
|
-
|
443
|
-
it "returns: the number with the same precision" do
|
444
|
-
expect(result).to eq number
|
445
|
-
end
|
446
|
-
end
|
447
|
-
end
|
448
|
-
end
|
449
|
-
end
|
450
|
-
end
|
451
|
-
end
|