msfl_visitors 0.0.4 → 0.1.0.dev
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/ast.rb +7 -2
- data/lib/msfl_visitors/nodes/and.rb +16 -5
- data/lib/msfl_visitors/nodes/binary_and.rb +7 -0
- data/lib/msfl_visitors/nodes/containment.rb +7 -0
- data/lib/msfl_visitors/nodes/field.rb +7 -0
- data/lib/msfl_visitors/nodes/filter.rb +2 -18
- data/lib/msfl_visitors/nodes/iterator.rb +18 -0
- data/lib/msfl_visitors/nodes/set/close.rb +9 -0
- data/lib/msfl_visitors/nodes/set/delimiter.rb +9 -0
- data/lib/msfl_visitors/nodes/set/open.rb +9 -0
- data/lib/msfl_visitors/nodes/set/set.rb +39 -0
- data/lib/msfl_visitors/nodes.rb +8 -0
- data/lib/msfl_visitors/parsers/msfl_parser.rb +20 -5
- data/lib/msfl_visitors/renderers/chewy/term_filter.rb +17 -3
- data/msfl_visitors.gemspec +2 -2
- data/spec/ast_spec.rb +78 -0
- data/spec/nodes/iterator_spec.rb +39 -0
- data/spec/parsers/msfl_parser_spec.rb +70 -0
- data/spec/renderers/chewy/term_filter_spec.rb +24 -0
- data/spec/visitors/chewy_term_filter_spec.rb +76 -12
- metadata +20 -6
- data/spec/parsers/parse_msfl_spec.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c087841af641cd54315d9e01ede56327e432e50
|
4
|
+
data.tar.gz: 62b019613fe964774360e3cfde4ce61603c20686
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0deb50dbdb4057b19c49daa95d151bfff4a8885451d5ad588b8b704247e0e5ccded964ad7d5e1cd9336a7d0d5c836d77b8cd464b8c98d7a5b8e039c15a5dd61
|
7
|
+
data.tar.gz: a79e6d2c395b9e1b573e5a6207504fb59fcffbdf491e2b0081aba6d70d75f492a1e01314f9f0473983407a28e99aef1e7c436d418fb93eca2f9e9fdd575dd2d5
|
data/lib/msfl_visitors/ast.rb
CHANGED
@@ -3,13 +3,18 @@ module MSFLVisitors
|
|
3
3
|
|
4
4
|
attr_accessor :root
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
self.root =
|
6
|
+
def initialize(obj, parser = MSFLVisitors::Parsers::MSFLParser.new)
|
7
|
+
self.root = parser.parse obj
|
8
8
|
end
|
9
9
|
|
10
10
|
# Use this method to walk the AST with a particular visitor
|
11
11
|
def accept(visitor)
|
12
12
|
root.accept visitor
|
13
13
|
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
self.class == other.class &&
|
17
|
+
root == other.root
|
18
|
+
end
|
14
19
|
end
|
15
20
|
end
|
@@ -1,12 +1,23 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'iterator'
|
2
2
|
module MSFLVisitors
|
3
3
|
module Nodes
|
4
|
-
class And <
|
4
|
+
class And < Iterator
|
5
5
|
|
6
6
|
def accept(visitor)
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
nodes = Array.new
|
8
|
+
if items.count > 1
|
9
|
+
items.each do |item|
|
10
|
+
nodes << MSFLVisitors::Nodes::Grouping::Grouping.new(item)
|
11
|
+
nodes << BinaryAnd.new
|
12
|
+
end
|
13
|
+
nodes.pop
|
14
|
+
elsif items.count == 1
|
15
|
+
nodes << items.first
|
16
|
+
end
|
17
|
+
|
18
|
+
nodes.each do |node|
|
19
|
+
node.accept visitor
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end
|
12
23
|
end
|
@@ -1,23 +1,7 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'set/set'
|
2
2
|
module MSFLVisitors
|
3
3
|
module Nodes
|
4
|
-
class Filter <
|
5
|
-
|
6
|
-
attr_accessor :contents
|
7
|
-
|
8
|
-
def accept(visitor)
|
9
|
-
contents.accept visitor
|
10
|
-
end
|
11
|
-
|
12
|
-
# @param nodes [Array<MSFL::Nodes::Base>] the nodes that the filter surrounds
|
13
|
-
def initialize(nodes)
|
14
|
-
self.contents = nodes
|
15
|
-
end
|
16
|
-
|
17
|
-
def ==(other)
|
18
|
-
self.class == other.class &&
|
19
|
-
contents == other.contents
|
20
|
-
end
|
4
|
+
class Filter < Set::Set
|
21
5
|
end
|
22
6
|
end
|
23
7
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
module MSFLVisitors
|
3
|
+
module Nodes
|
4
|
+
class Iterator < Base
|
5
|
+
|
6
|
+
attr_accessor :items
|
7
|
+
|
8
|
+
def initialize(nodes)
|
9
|
+
self.items = nodes
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
self.class == other.class &&
|
14
|
+
items == other.items
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../base'
|
2
|
+
module MSFLVisitors
|
3
|
+
module Nodes
|
4
|
+
module Set
|
5
|
+
class Set < Base
|
6
|
+
|
7
|
+
attr_accessor :contents
|
8
|
+
|
9
|
+
def accept(visitor)
|
10
|
+
nodes = Array.new
|
11
|
+
nodes << Open.new
|
12
|
+
if contents.count > 0
|
13
|
+
contents.each do |item|
|
14
|
+
nodes << item
|
15
|
+
nodes << Delimiter.new
|
16
|
+
end
|
17
|
+
# Remove the last (and therefore extra) delimiter
|
18
|
+
nodes.pop
|
19
|
+
end
|
20
|
+
nodes << Close.new
|
21
|
+
|
22
|
+
nodes.each do |node|
|
23
|
+
node.accept visitor
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param nodes [Array<MSFL::Nodes::Base>] the nodes that the filter surrounds
|
28
|
+
def initialize(nodes)
|
29
|
+
self.contents = Array(nodes)
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
self.class == other.class &&
|
34
|
+
contents == other.contents
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/msfl_visitors/nodes.rb
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
require_relative 'nodes/and'
|
3
3
|
require_relative 'nodes/base'
|
4
4
|
require_relative 'nodes/binary'
|
5
|
+
require_relative 'nodes/binary_and'
|
5
6
|
require_relative 'nodes/boolean'
|
6
7
|
require_relative 'nodes/comparison'
|
7
8
|
require_relative 'nodes/constant_value'
|
9
|
+
require_relative 'nodes/containment'
|
8
10
|
require_relative 'nodes/date'
|
9
11
|
require_relative 'nodes/date_time'
|
10
12
|
require_relative 'nodes/equal'
|
13
|
+
require_relative 'nodes/field'
|
11
14
|
require_relative 'nodes/filter'
|
15
|
+
require_relative 'nodes/iterator'
|
12
16
|
require_relative 'nodes/greater_than'
|
13
17
|
require_relative 'nodes/greater_than_equal'
|
14
18
|
require_relative 'nodes/grouping/close'
|
@@ -18,6 +22,10 @@ require_relative 'nodes/less_than'
|
|
18
22
|
require_relative 'nodes/less_than_equal'
|
19
23
|
require_relative 'nodes/number'
|
20
24
|
require_relative 'nodes/range_value'
|
25
|
+
require_relative 'nodes/set/close'
|
26
|
+
require_relative 'nodes/set/delimiter'
|
27
|
+
require_relative 'nodes/set/set'
|
28
|
+
require_relative 'nodes/set/open'
|
21
29
|
require_relative 'nodes/time'
|
22
30
|
require_relative 'nodes/value'
|
23
31
|
require_relative 'nodes/word'
|
@@ -4,8 +4,13 @@ module MSFLVisitors
|
|
4
4
|
class MSFLParser
|
5
5
|
include MSFL::Validators::Definitions::HashKey
|
6
6
|
|
7
|
+
OPERATORS_TO_NODE_CLASS = {
|
8
|
+
gt: Nodes::GreaterThan,
|
9
|
+
gte: Nodes::GreaterThanEqual,
|
10
|
+
in: Nodes::Containment,
|
11
|
+
}
|
12
|
+
|
7
13
|
def parse(obj, lhs = false)
|
8
|
-
# send("parse_#{obj.class.to_s.gsub('::', '_')}", obj, lhs)
|
9
14
|
case obj
|
10
15
|
|
11
16
|
when Float, Fixnum
|
@@ -35,11 +40,21 @@ module MSFLVisitors
|
|
35
40
|
obj.each do |k, v|
|
36
41
|
nodes << hash_dispatch(k, v, lhs)
|
37
42
|
end
|
38
|
-
|
43
|
+
# If there's exactly one node in nodes and it's a filter node we don't want to wrap it in yet
|
44
|
+
# another filter node, so we just return the filter node
|
45
|
+
if nodes.count == 1 && nodes.first.is_a?(MSFLVisitors::Nodes::Filter)
|
46
|
+
nodes.first
|
47
|
+
else
|
48
|
+
MSFLVisitors::Nodes::Filter.new nodes
|
49
|
+
end
|
39
50
|
end
|
40
51
|
|
41
52
|
def parse_Set(obj, lhs = false)
|
42
|
-
|
53
|
+
nodes = MSFL::Types::Set.new([])
|
54
|
+
obj.each do |item|
|
55
|
+
nodes << parse(item)
|
56
|
+
end
|
57
|
+
MSFLVisitors::Nodes::Set::Set.new nodes
|
43
58
|
end
|
44
59
|
|
45
60
|
def hash_dispatch(key, value, lhs = false)
|
@@ -47,7 +62,7 @@ module MSFLVisitors
|
|
47
62
|
# Detect the node type, forward the lhs if it was passed in (essentially when the operator is a binary op)
|
48
63
|
args = [lhs, parse(value)] if lhs
|
49
64
|
args ||= [parse(value)]
|
50
|
-
|
65
|
+
OPERATORS_TO_NODE_CLASS[key].new(*args)
|
51
66
|
else
|
52
67
|
# the key is a field
|
53
68
|
# there are three possible scenarios when they key is a field
|
@@ -61,7 +76,7 @@ module MSFLVisitors
|
|
61
76
|
# { model: { in: ["Corolla", "Civic", "Mustang"] } }
|
62
77
|
#
|
63
78
|
# 2 & 3 are just hashes and can be parsed using the same method
|
64
|
-
lhs = MSFLVisitors::Nodes::
|
79
|
+
lhs = MSFLVisitors::Nodes::Field.new key
|
65
80
|
|
66
81
|
# the node type generated by parsing value can use the lhs node when appropriate and otherwise ignore it
|
67
82
|
# although I can't think of a situation when it would ignore it.
|
@@ -3,7 +3,6 @@ module MSFLVisitors
|
|
3
3
|
module Chewy
|
4
4
|
class TermFilter
|
5
5
|
BINARY_OPERATORS = {
|
6
|
-
Nodes::And => ' & ',
|
7
6
|
Nodes::GreaterThan => ' > ',
|
8
7
|
Nodes::LessThan => ' < ',
|
9
8
|
Nodes::GreaterThanEqual => ' >= ',
|
@@ -14,24 +13,39 @@ module MSFLVisitors
|
|
14
13
|
def render(node)
|
15
14
|
case node
|
16
15
|
|
17
|
-
when Nodes::Comparison
|
16
|
+
when Nodes::Comparison
|
18
17
|
BINARY_OPERATORS[node.class]
|
19
18
|
|
19
|
+
when Nodes::BinaryAnd
|
20
|
+
' & '
|
21
|
+
|
20
22
|
when Nodes::Date, Nodes::Time
|
21
23
|
node.value.iso8601
|
22
24
|
|
23
25
|
when Nodes::Boolean, Nodes::Number
|
24
26
|
node.value
|
25
27
|
|
26
|
-
when Nodes::
|
28
|
+
when Nodes::Field
|
27
29
|
node.value.to_s
|
28
30
|
|
31
|
+
when Nodes::Word
|
32
|
+
%("#{node.value.to_s}")
|
33
|
+
|
29
34
|
when Nodes::Grouping::Close
|
30
35
|
' )'
|
31
36
|
|
32
37
|
when Nodes::Grouping::Open
|
33
38
|
'( '
|
34
39
|
|
40
|
+
when Nodes::Set::Close
|
41
|
+
' ]'
|
42
|
+
|
43
|
+
when Nodes::Set::Delimiter
|
44
|
+
', '
|
45
|
+
|
46
|
+
when Nodes::Set::Open
|
47
|
+
'[ '
|
48
|
+
|
35
49
|
else
|
36
50
|
fail ArgumentError.new("Unrecognized node type: #{node.class}")
|
37
51
|
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 = '0.0.
|
4
|
-
s.date = '2015-05-
|
3
|
+
s.version = '0.1.0.dev'
|
4
|
+
s.date = '2015-05-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"]
|
data/spec/ast_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFLVisitors::AST do
|
4
|
+
|
5
|
+
let(:msfl) { { make: "Honda" } }
|
6
|
+
|
7
|
+
describe "#initialize" do
|
8
|
+
|
9
|
+
let(:parser) do
|
10
|
+
p = double('Fake Parser')
|
11
|
+
expect(p).to receive(:parse).with(msfl).once
|
12
|
+
p
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { described_class.new msfl, parser }
|
16
|
+
|
17
|
+
it "eagerly parses the first argument" do
|
18
|
+
subject
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when a parser is specified" do
|
22
|
+
|
23
|
+
it "uses the specified parser" do
|
24
|
+
subject
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#accept" do
|
30
|
+
|
31
|
+
let(:parser) { double('Fake Parser', parse: root) }
|
32
|
+
|
33
|
+
let(:root) do
|
34
|
+
r = double('Fake Root Node')
|
35
|
+
expect(r).to receive(:accept).with(visitor).once
|
36
|
+
r
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:visitor) { double('Fake Visitor') }
|
40
|
+
|
41
|
+
subject { described_class.new(msfl, parser).accept visitor }
|
42
|
+
|
43
|
+
it "delegates to the root node" do
|
44
|
+
subject
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#==" do
|
49
|
+
|
50
|
+
subject { left == right }
|
51
|
+
|
52
|
+
let(:left) { described_class.new(msfl) }
|
53
|
+
|
54
|
+
let(:right) { described_class.new(msfl) }
|
55
|
+
|
56
|
+
context "when the two ASTs are the same class" do
|
57
|
+
|
58
|
+
context "when the two ASTs have equal root nodes" do
|
59
|
+
|
60
|
+
it { is_expected.to be true }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when the two ASTs do not have equal root nodes" do
|
64
|
+
|
65
|
+
let(:right) { described_class.new({ value: 1000 }) }
|
66
|
+
|
67
|
+
it { is_expected.to be false }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when the two ASTs are not the same class" do
|
72
|
+
|
73
|
+
let(:right) { double('Fake AST') }
|
74
|
+
|
75
|
+
it { is_expected.to be false }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFLVisitors::Nodes::Iterator do
|
4
|
+
|
5
|
+
describe "#==" do
|
6
|
+
|
7
|
+
let(:one) { MSFLVisitors::Nodes::Number.new(1) }
|
8
|
+
|
9
|
+
let(:two) { MSFLVisitors::Nodes::Number.new(2) }
|
10
|
+
|
11
|
+
let(:left) { MSFLVisitors::Nodes::Iterator.new [one, two] }
|
12
|
+
|
13
|
+
let(:right) { MSFLVisitors::Nodes::Iterator.new [one, two] }
|
14
|
+
|
15
|
+
subject { left == right }
|
16
|
+
|
17
|
+
context "when lhs and rhs are the same class" do
|
18
|
+
|
19
|
+
context "when lhs#items is equal to rhs#items" do
|
20
|
+
|
21
|
+
it { is_expected.to be true }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when lhs#items is not equal to rhs#items" do
|
25
|
+
|
26
|
+
let(:right) { MSFLVisitors::Nodes::Iterator.new [one] }
|
27
|
+
|
28
|
+
it { is_expected.to be false }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when lhs is a different class than rhs" do
|
33
|
+
|
34
|
+
let(:right) { Object.new }
|
35
|
+
|
36
|
+
it { is_expected.to be false }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# This is a working file until I split these up
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe MSFLVisitors::Parsers::MSFLParser do
|
5
|
+
|
6
|
+
let(:expected_node) { ->(comp_node) { MSFLVisitors::Nodes::Filter.new [ comp_node ] } }
|
7
|
+
|
8
|
+
let(:left) { MSFLVisitors::Nodes::Field.new :value }
|
9
|
+
|
10
|
+
let(:right) { MSFLVisitors::Nodes::Number.new 1000 }
|
11
|
+
|
12
|
+
describe "parsing a trivial filter" do
|
13
|
+
|
14
|
+
subject { described_class.new.parse msfl }
|
15
|
+
|
16
|
+
let(:msfl) { { value: 1000 } }
|
17
|
+
|
18
|
+
it "is the expected node" do
|
19
|
+
expect(subject).to eq expected_node.call(MSFLVisitors::Nodes::Equal.new(left, right))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#parse" do
|
24
|
+
|
25
|
+
context "when parsing a filter" do
|
26
|
+
|
27
|
+
subject { -> (filter) { described_class.new.parse filter } }
|
28
|
+
|
29
|
+
let(:implicit_equality_filter) { { value: 1000 } }
|
30
|
+
|
31
|
+
let(:explicit_gte_filter) { { value: { gte: 1000 } } }
|
32
|
+
|
33
|
+
let(:containment_filter) { { value: { in: containment_values } } }
|
34
|
+
|
35
|
+
let(:containment_values) { MSFL::Types::Set.new [50, 250, 20000] }
|
36
|
+
|
37
|
+
it "handles implicit equality comparisons" do
|
38
|
+
expect(subject.call(implicit_equality_filter)).to eq expected_node.call(MSFLVisitors::Nodes::Equal.new(left, right))
|
39
|
+
end
|
40
|
+
|
41
|
+
it "handles explicit comparisons" do
|
42
|
+
comparison_node = MSFLVisitors::Nodes::GreaterThanEqual.new left, right
|
43
|
+
expect(subject.call(explicit_gte_filter)).to eq expected_node.call(comparison_node)
|
44
|
+
end
|
45
|
+
|
46
|
+
# { value: { in: [50, 250, 20000] } }
|
47
|
+
#
|
48
|
+
# => Nodes::Containment.new(Nodes::Field.new(:value),
|
49
|
+
# Nodes::Set::Set.new([
|
50
|
+
# Nodes::Number.new(50),
|
51
|
+
# Nodes::Number.new(250),
|
52
|
+
# Nodes::Number.new(20000)]))
|
53
|
+
it "handles containments" do
|
54
|
+
contained_nodes = containment_values.map { |value| MSFLVisitors::Nodes::Number.new(value) }
|
55
|
+
set_node = MSFLVisitors::Nodes::Set::Set.new(contained_nodes)
|
56
|
+
containment_node = MSFLVisitors::Nodes::Containment.new left, set_node
|
57
|
+
expect(subject.call(containment_filter)).to eq expected_node.call(containment_node)
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when the filter contains an unsupported type" do
|
61
|
+
|
62
|
+
let(:bad_filter) { { foo: Object.new } }
|
63
|
+
|
64
|
+
it "raises an ArgumentError" do
|
65
|
+
expect { subject.call(bad_filter) }.to raise_error ArgumentError
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MSFLVisitors::Renderers::Chewy::TermFilter do
|
4
|
+
|
5
|
+
let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
|
6
|
+
|
7
|
+
let(:collector) { String.new }
|
8
|
+
|
9
|
+
let(:renderer) { MSFLVisitors::Renderers::Chewy::TermFilter.new }
|
10
|
+
|
11
|
+
describe "#render" do
|
12
|
+
|
13
|
+
let(:node) { double('Unsupported Type') }
|
14
|
+
|
15
|
+
subject { renderer.render node }
|
16
|
+
|
17
|
+
context "when attempting to render an unsupported type" do
|
18
|
+
|
19
|
+
it "raises an ArgumentError" do
|
20
|
+
expect { subject }.to raise_error ArgumentError
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -10,20 +10,37 @@ describe MSFLVisitors::Visitor do
|
|
10
10
|
|
11
11
|
let(:visitor) { described_class.new collector, renderer }
|
12
12
|
|
13
|
-
let(:left) { MSFLVisitors::Nodes::
|
13
|
+
let(:left) { MSFLVisitors::Nodes::Field.new "lhs" }
|
14
14
|
|
15
15
|
let(:right) { MSFLVisitors::Nodes::Word.new "rhs" }
|
16
16
|
|
17
|
-
subject(:result) {
|
17
|
+
subject(:result) { collector }
|
18
|
+
|
19
|
+
before(:each) { node.accept visitor }
|
18
20
|
|
19
21
|
context "when visiting" do
|
20
22
|
|
23
|
+
describe "a Set node" do
|
24
|
+
|
25
|
+
let(:node) { MSFLVisitors::Nodes::Set::Set.new values }
|
26
|
+
|
27
|
+
let(:values) { MSFL::Types::Set.new([item_one, item_two]) }
|
28
|
+
|
29
|
+
let(:item_one) { MSFLVisitors::Nodes::Word.new "item_one" }
|
30
|
+
|
31
|
+
let(:item_two) { MSFLVisitors::Nodes::Word.new "item_two" }
|
32
|
+
|
33
|
+
it "results in: '[ \"item_one\", \"item_two\" ]'" do
|
34
|
+
expect(result).to eq "[ \"item_one\", \"item_two\" ]"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
21
38
|
describe "an Equal node" do
|
22
39
|
|
23
40
|
let(:node) { MSFLVisitors::Nodes::Equal.new left, right }
|
24
41
|
|
25
42
|
it "results in: 'left == right'" do
|
26
|
-
expect(result).to eq "lhs == rhs"
|
43
|
+
expect(result).to eq "lhs == \"rhs\""
|
27
44
|
end
|
28
45
|
end
|
29
46
|
|
@@ -32,7 +49,7 @@ describe MSFLVisitors::Visitor do
|
|
32
49
|
let(:node) { MSFLVisitors::Nodes::GreaterThan.new left, right }
|
33
50
|
|
34
51
|
it "returns: 'left > right'" do
|
35
|
-
expect(result).to eq "lhs > rhs"
|
52
|
+
expect(result).to eq "lhs > \"rhs\""
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
@@ -41,7 +58,7 @@ describe MSFLVisitors::Visitor do
|
|
41
58
|
let(:node) { MSFLVisitors::Nodes::GreaterThanEqual.new left, right }
|
42
59
|
|
43
60
|
it "returns: 'left >= right'" do
|
44
|
-
expect(result).to eq "lhs >= rhs"
|
61
|
+
expect(result).to eq "lhs >= \"rhs\""
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
@@ -50,7 +67,7 @@ describe MSFLVisitors::Visitor do
|
|
50
67
|
let(:node) { MSFLVisitors::Nodes::LessThan.new left, right }
|
51
68
|
|
52
69
|
it "returns: 'left < right'" do
|
53
|
-
expect(result).to eq
|
70
|
+
expect(result).to eq 'lhs < "rhs"'
|
54
71
|
end
|
55
72
|
end
|
56
73
|
|
@@ -59,16 +76,63 @@ describe MSFLVisitors::Visitor do
|
|
59
76
|
let(:node) { MSFLVisitors::Nodes::LessThanEqual.new left, right }
|
60
77
|
|
61
78
|
it "returns: 'left <= right'" do
|
62
|
-
expect(result).to eq
|
79
|
+
expect(result).to eq 'lhs <= "rhs"'
|
63
80
|
end
|
64
81
|
end
|
65
82
|
|
66
83
|
describe "an And node" do
|
67
84
|
|
68
|
-
let(:
|
85
|
+
let(:first_field) { MSFLVisitors::Nodes::Field.new "first_field" }
|
86
|
+
|
87
|
+
let(:first_value) { MSFLVisitors::Nodes::Word.new "first_word" }
|
88
|
+
|
89
|
+
let(:first) { MSFLVisitors::Nodes::Equal.new(first_field, first_value) }
|
90
|
+
|
91
|
+
let(:second_field) { MSFLVisitors::Nodes::Field.new "second_field" }
|
92
|
+
|
93
|
+
let(:second_value) { MSFLVisitors::Nodes::Word.new "second_word" }
|
94
|
+
|
95
|
+
let(:second) { MSFLVisitors::Nodes::Equal.new(second_field, second_value) }
|
96
|
+
|
97
|
+
let(:third_field) { MSFLVisitors::Nodes::Field.new "third_field" }
|
98
|
+
|
99
|
+
let(:third_value) { MSFLVisitors::Nodes::Word.new "third_word" }
|
100
|
+
|
101
|
+
let(:third) { MSFLVisitors::Nodes::Equal.new(third_field, third_value) }
|
69
102
|
|
70
|
-
|
71
|
-
|
103
|
+
context "when the And node has zero items" do
|
104
|
+
let(:node) { MSFLVisitors::Nodes::And.new([]) }
|
105
|
+
|
106
|
+
it "is empty" do
|
107
|
+
expect(result).to be_empty
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "when the node has one item" do
|
112
|
+
|
113
|
+
let(:node) { MSFLVisitors::Nodes::And.new([first])}
|
114
|
+
|
115
|
+
it "returns: the item without adding parentheses" do
|
116
|
+
expect(result).to eq 'first_field == "first_word"'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when the node has two items" do
|
121
|
+
|
122
|
+
let(:node) { MSFLVisitors::Nodes::And.new([first, second]) }
|
123
|
+
|
124
|
+
it "returns: '( first_field == \"first_word\" ) & ( second_field == \"second_word\" )'" do
|
125
|
+
expect(result).to eq '( first_field == "first_word" ) & ( second_field == "second_word" )'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when the node has three items" do
|
130
|
+
|
131
|
+
let(:node) { MSFLVisitors::Nodes::And.new([first, second, third]) }
|
132
|
+
|
133
|
+
it "returns: '( first_field == \"first_word\" ) & ( second_field == \"second_word\" ) & ( third_field == \"third_word\" )'" do
|
134
|
+
expect(result).to eq '( first_field == "first_word" ) & ( second_field == "second_word" ) & ( third_field == "third_word" )'
|
135
|
+
end
|
72
136
|
end
|
73
137
|
end
|
74
138
|
|
@@ -106,8 +170,8 @@ describe MSFLVisitors::Visitor do
|
|
106
170
|
|
107
171
|
let(:node) { MSFLVisitors::Nodes::Word.new word }
|
108
172
|
|
109
|
-
it "is a literal string" do
|
110
|
-
expect(result).to eq word
|
173
|
+
it "is a double quoted literal string" do
|
174
|
+
expect(result).to eq "\"#{word}\""
|
111
175
|
end
|
112
176
|
end
|
113
177
|
end
|
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: 0.0.
|
4
|
+
version: 0.1.0.dev
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Courtland Caldwell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: msfl
|
@@ -119,22 +119,30 @@ files:
|
|
119
119
|
- lib/msfl_visitors/nodes/and.rb
|
120
120
|
- lib/msfl_visitors/nodes/base.rb
|
121
121
|
- lib/msfl_visitors/nodes/binary.rb
|
122
|
+
- lib/msfl_visitors/nodes/binary_and.rb
|
122
123
|
- lib/msfl_visitors/nodes/boolean.rb
|
123
124
|
- lib/msfl_visitors/nodes/comparison.rb
|
124
125
|
- lib/msfl_visitors/nodes/constant_value.rb
|
126
|
+
- lib/msfl_visitors/nodes/containment.rb
|
125
127
|
- lib/msfl_visitors/nodes/date.rb
|
126
128
|
- lib/msfl_visitors/nodes/date_time.rb
|
127
129
|
- lib/msfl_visitors/nodes/equal.rb
|
130
|
+
- lib/msfl_visitors/nodes/field.rb
|
128
131
|
- lib/msfl_visitors/nodes/filter.rb
|
129
132
|
- lib/msfl_visitors/nodes/greater_than.rb
|
130
133
|
- lib/msfl_visitors/nodes/greater_than_equal.rb
|
131
134
|
- lib/msfl_visitors/nodes/grouping/close.rb
|
132
135
|
- lib/msfl_visitors/nodes/grouping/grouping.rb
|
133
136
|
- lib/msfl_visitors/nodes/grouping/open.rb
|
137
|
+
- lib/msfl_visitors/nodes/iterator.rb
|
134
138
|
- lib/msfl_visitors/nodes/less_than.rb
|
135
139
|
- lib/msfl_visitors/nodes/less_than_equal.rb
|
136
140
|
- lib/msfl_visitors/nodes/number.rb
|
137
141
|
- lib/msfl_visitors/nodes/range_value.rb
|
142
|
+
- lib/msfl_visitors/nodes/set/close.rb
|
143
|
+
- lib/msfl_visitors/nodes/set/delimiter.rb
|
144
|
+
- lib/msfl_visitors/nodes/set/open.rb
|
145
|
+
- lib/msfl_visitors/nodes/set/set.rb
|
138
146
|
- lib/msfl_visitors/nodes/time.rb
|
139
147
|
- lib/msfl_visitors/nodes/value.rb
|
140
148
|
- lib/msfl_visitors/nodes/word.rb
|
@@ -143,7 +151,10 @@ files:
|
|
143
151
|
- lib/msfl_visitors/visitor.rb
|
144
152
|
- msfl_visitors.gemspec
|
145
153
|
- simplecov_custom_profiles.rb
|
146
|
-
- spec/
|
154
|
+
- spec/ast_spec.rb
|
155
|
+
- spec/nodes/iterator_spec.rb
|
156
|
+
- spec/parsers/msfl_parser_spec.rb
|
157
|
+
- spec/renderers/chewy/term_filter_spec.rb
|
147
158
|
- spec/spec_helper.rb
|
148
159
|
- spec/visitors/chewy_term_filter_spec.rb
|
149
160
|
homepage: https://github.com/Referly/msfl_visitors
|
@@ -161,9 +172,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
172
|
version: '0'
|
162
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
174
|
requirements:
|
164
|
-
- - "
|
175
|
+
- - ">"
|
165
176
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
177
|
+
version: 1.3.1
|
167
178
|
requirements: []
|
168
179
|
rubyforge_project:
|
169
180
|
rubygems_version: 2.2.2
|
@@ -171,7 +182,10 @@ signing_key:
|
|
171
182
|
specification_version: 4
|
172
183
|
summary: Convert MSFL to other forms
|
173
184
|
test_files:
|
174
|
-
- spec/
|
185
|
+
- spec/ast_spec.rb
|
186
|
+
- spec/nodes/iterator_spec.rb
|
187
|
+
- spec/parsers/msfl_parser_spec.rb
|
188
|
+
- spec/renderers/chewy/term_filter_spec.rb
|
175
189
|
- spec/spec_helper.rb
|
176
190
|
- spec/visitors/chewy_term_filter_spec.rb
|
177
191
|
has_rdoc:
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# This is a working file until I split these up
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe MSFLVisitors::Parsers::MSFLParser do
|
5
|
-
|
6
|
-
describe "parsing a trivial filter" do
|
7
|
-
|
8
|
-
subject { described_class.new.parse msfl }
|
9
|
-
|
10
|
-
let(:msfl) { { make: "Ferrari" } }
|
11
|
-
|
12
|
-
let(:expected) { MSFLVisitors::Nodes::Filter.new [ MSFLVisitors::Nodes::Equal.new(left, right) ] }
|
13
|
-
|
14
|
-
let(:left) { MSFLVisitors::Nodes::Word.new :make }
|
15
|
-
|
16
|
-
let(:right) { MSFLVisitors::Nodes::Word.new "Ferrari" }
|
17
|
-
|
18
|
-
it "is the expected AST" do
|
19
|
-
expect(subject).to eq expected
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|