msfl_visitors 1.2.3 → 1.3.0.dev.f
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/lib/msfl_visitors/visitor.rb +95 -3
- data/lib/msfl_visitors.rb +12 -0
- data/msfl_visitors.gemspec +2 -2
- data/spec/msfl_visitors/visitors/arel_filter_spec.rb +451 -0
- metadata +23 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec528af7457f8551d7febd5d497337a2b805ac8b
|
4
|
+
data.tar.gz: 5e3de6c175ad703eb72b351c30cb3e7a025d9c82
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a78faedefd14402aba98cbdcd9e4c8ff976c43fc80aa5a9370cf17d6cc1d3ddc0350e47e4d4c3ced60669253fc7840ff0fe54ece4d6a46641f8a55b1449667c8
|
7
|
+
data.tar.gz: c05b7c376734ef110bb67a02f16c2e4bc167da846e07a30b00c6197900655b85da0c3adf6b671384f3b55da9e8ca19ff8bda38ff6c53599c021752344be4d89c
|
@@ -19,11 +19,15 @@ 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
|
22
26
|
end
|
23
27
|
|
24
28
|
class Visitor
|
25
29
|
|
26
|
-
attr_accessor :clauses, :current_clause
|
30
|
+
attr_accessor :clauses, :current_clause, :arel_table
|
27
31
|
attr_writer :mode
|
28
32
|
|
29
33
|
def initialize
|
@@ -35,6 +39,8 @@ module MSFLVisitors
|
|
35
39
|
def visit(node)
|
36
40
|
if mode == :es_term
|
37
41
|
get_visitor.visit(node)
|
42
|
+
elsif mode == :arel
|
43
|
+
get_visitor.visit(node)
|
38
44
|
else
|
39
45
|
case node
|
40
46
|
when Nodes::Partial
|
@@ -54,6 +60,8 @@ module MSFLVisitors
|
|
54
60
|
TermFilterVisitor.new(self)
|
55
61
|
when :es_term
|
56
62
|
ESTermFilterVisitor.new(self)
|
63
|
+
when :arel
|
64
|
+
ArelFilterVisitor.new(self, arel_table)
|
57
65
|
else
|
58
66
|
AggregationsVisitor.new(self)
|
59
67
|
end
|
@@ -66,8 +74,11 @@ module MSFLVisitors
|
|
66
74
|
result
|
67
75
|
end
|
68
76
|
|
69
|
-
def visit_tree(root)
|
70
|
-
[{clause: root.accept(self)}].concat(clauses).reject { |c| c[:clause] == "" }
|
77
|
+
def visit_tree(root, arel_table: nil)
|
78
|
+
return [{clause: root.accept(self)}].concat(clauses).reject { |c| c[:clause] == "" } unless mode == :arel
|
79
|
+
raise ArgumentError unless arel_table
|
80
|
+
@arel_table = arel_table.name
|
81
|
+
"#{root.accept(self)}"
|
71
82
|
end
|
72
83
|
|
73
84
|
private
|
@@ -245,6 +256,87 @@ module MSFLVisitors
|
|
245
256
|
attr_reader :visitor
|
246
257
|
end
|
247
258
|
|
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
|
+
|
248
340
|
# ESTermFilterVisitor is not currently used and so not all node types are implemented
|
249
341
|
class ESTermFilterVisitor
|
250
342
|
def initialize(visitor)
|
data/lib/msfl_visitors.rb
CHANGED
@@ -15,5 +15,17 @@ 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
|
18
30
|
end
|
19
31
|
end
|
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.
|
4
|
-
s.date = '
|
3
|
+
s.version = '1.3.0.dev.f'
|
4
|
+
s.date = '2016-09-30'
|
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"]
|
@@ -0,0 +1,451 @@
|
|
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
|
metadata
CHANGED
@@ -1,103 +1,103 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: msfl_visitors
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0.dev.f
|
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: 2016-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msfl
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.2'
|
20
|
-
- -
|
20
|
+
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 1.2.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ~>
|
27
|
+
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '1.2'
|
30
|
-
- -
|
30
|
+
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 1.2.1
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: rake
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - ~>
|
37
|
+
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '10.3'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - ~>
|
44
|
+
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '10.3'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: simplecov
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
50
50
|
requirements:
|
51
|
-
- - ~>
|
51
|
+
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0.10'
|
54
54
|
type: :development
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
|
-
- - ~>
|
58
|
+
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0.10'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: yard
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- - ~>
|
65
|
+
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '0.8'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- - ~>
|
72
|
+
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0.8'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: rspec
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
|
-
- - ~>
|
79
|
+
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '3.2'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
|
-
- - ~>
|
86
|
+
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '3.2'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: byebug
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
|
-
- - ~>
|
93
|
+
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '4.0'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
|
-
- - ~>
|
100
|
+
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
102
|
version: '4.0'
|
103
103
|
description: Visitor pattern approach to converting MSFL to other forms.
|
@@ -106,7 +106,7 @@ executables: []
|
|
106
106
|
extensions: []
|
107
107
|
extra_rdoc_files: []
|
108
108
|
files:
|
109
|
-
- .gitignore
|
109
|
+
- ".gitignore"
|
110
110
|
- Gemfile
|
111
111
|
- Gemfile.lock
|
112
112
|
- LICENSE
|
@@ -154,6 +154,7 @@ files:
|
|
154
154
|
- spec/msfl_visitors/nodes/iterator_spec.rb
|
155
155
|
- spec/msfl_visitors/parsers/msfl_parser_spec.rb
|
156
156
|
- spec/msfl_visitors/visitor_spec.rb
|
157
|
+
- spec/msfl_visitors/visitors/arel_filter_spec.rb
|
157
158
|
- spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
|
158
159
|
- spec/msfl_visitors/visitors/es_term_filter_spec.rb
|
159
160
|
- spec/msfl_visitors_spec.rb
|
@@ -168,17 +169,17 @@ require_paths:
|
|
168
169
|
- lib
|
169
170
|
required_ruby_version: !ruby/object:Gem::Requirement
|
170
171
|
requirements:
|
171
|
-
- -
|
172
|
+
- - ">="
|
172
173
|
- !ruby/object:Gem::Version
|
173
174
|
version: '0'
|
174
175
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
176
|
requirements:
|
176
|
-
- -
|
177
|
+
- - ">"
|
177
178
|
- !ruby/object:Gem::Version
|
178
|
-
version:
|
179
|
+
version: 1.3.1
|
179
180
|
requirements: []
|
180
181
|
rubyforge_project:
|
181
|
-
rubygems_version: 2.4.
|
182
|
+
rubygems_version: 2.4.3
|
182
183
|
signing_key:
|
183
184
|
specification_version: 4
|
184
185
|
summary: Convert MSFL to other forms
|
@@ -187,6 +188,7 @@ test_files:
|
|
187
188
|
- spec/msfl_visitors/nodes/iterator_spec.rb
|
188
189
|
- spec/msfl_visitors/parsers/msfl_parser_spec.rb
|
189
190
|
- spec/msfl_visitors/visitor_spec.rb
|
191
|
+
- spec/msfl_visitors/visitors/arel_filter_spec.rb
|
190
192
|
- spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
|
191
193
|
- spec/msfl_visitors/visitors/es_term_filter_spec.rb
|
192
194
|
- spec/msfl_visitors_spec.rb
|