msfl_visitors 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +40 -0
  5. data/LICENSE +22 -0
  6. data/README.md +64 -0
  7. data/Rakefile +10 -0
  8. data/circle.yml +9 -0
  9. data/lib/msfl_visitors.rb +7 -0
  10. data/lib/msfl_visitors/ast.rb +15 -0
  11. data/lib/msfl_visitors/nodes.rb +26 -0
  12. data/lib/msfl_visitors/nodes/and.rb +13 -0
  13. data/lib/msfl_visitors/nodes/base.rb +7 -0
  14. data/lib/msfl_visitors/nodes/binary.rb +26 -0
  15. data/lib/msfl_visitors/nodes/boolean.rb +7 -0
  16. data/lib/msfl_visitors/nodes/comparison.rb +7 -0
  17. data/lib/msfl_visitors/nodes/constant_value.rb +11 -0
  18. data/lib/msfl_visitors/nodes/date.rb +7 -0
  19. data/lib/msfl_visitors/nodes/date_time.rb +7 -0
  20. data/lib/msfl_visitors/nodes/equal.rb +7 -0
  21. data/lib/msfl_visitors/nodes/filter.rb +23 -0
  22. data/lib/msfl_visitors/nodes/greater_than.rb +7 -0
  23. data/lib/msfl_visitors/nodes/greater_than_equal.rb +7 -0
  24. data/lib/msfl_visitors/nodes/grouping/close.rb +9 -0
  25. data/lib/msfl_visitors/nodes/grouping/grouping.rb +24 -0
  26. data/lib/msfl_visitors/nodes/grouping/open.rb +9 -0
  27. data/lib/msfl_visitors/nodes/less_than.rb +7 -0
  28. data/lib/msfl_visitors/nodes/less_than_equal.rb +7 -0
  29. data/lib/msfl_visitors/nodes/number.rb +7 -0
  30. data/lib/msfl_visitors/nodes/range_value.rb +7 -0
  31. data/lib/msfl_visitors/nodes/time.rb +7 -0
  32. data/lib/msfl_visitors/nodes/value.rb +22 -0
  33. data/lib/msfl_visitors/nodes/word.rb +7 -0
  34. data/lib/msfl_visitors/parsers/msfl_parser.rb +78 -0
  35. data/lib/msfl_visitors/visitors/base.rb +15 -0
  36. data/lib/msfl_visitors/visitors/chewy_term_filter.rb +70 -0
  37. data/msfl_visitors.gemspec +20 -0
  38. data/simplecov_custom_profiles.rb +5 -0
  39. data/spec/parsers/parse_msfl_spec.rb +22 -0
  40. data/spec/spec_helper.rb +5 -0
  41. data/spec/visitors/chewy_term_filter/visit_binary_spec.rb +26 -0
  42. data/spec/visitors/chewy_term_filter/visit_comparison_spec.rb +61 -0
  43. data/spec/visitors/chewy_term_filter/visit_range_value_spec.rb +63 -0
  44. data/spec/visitors/chewy_term_filter/visit_value_spec.rb +44 -0
  45. metadata +183 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 095f9e51f9996a5555813581c265dbf5633b2272
4
+ data.tar.gz: 59c199f8df4b4f53bfe8b332d430c3e3e502476e
5
+ SHA512:
6
+ metadata.gz: a41e25d060824497a403ffa1f0f7b65a2856322e1dbc45819409e441559e1c9704bd024c92d37cf1bf861d8d06726609354906c90dd8e7432a9a3e3425c7c592
7
+ data.tar.gz: c602d663fdcc0864218ae9825362cc3c4917c26b7cbbbf959f33419704d4135d9ff9c81f80a6aa7a756ecb26364dfa50d99b9942e38e7889f482345ad6fccd46
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ ### Ruby template
2
+ *.gem
3
+ *.rbc
4
+ /coverage/
5
+ /spec/reports/
6
+
7
+ ## Documentation cache and generated files:
8
+ /.yardoc/
9
+ /_yardoc/
10
+ /doc/
11
+ /rdoc/
12
+
13
+ ## Environment normalisation:
14
+ /.bundle/
15
+ /lib/bundler/man/
16
+ .idea/
17
+
18
+ ## Gemfile for development
19
+ #Gemfile
20
+ #Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gem 'simplecov', :require => false, :group => :test # MIT https://github.com/colszowka/simplecov/blob/master/MIT-LICENSE
3
+ gem 'yard' # MIT https://github.com/lsegal/yard/blob/master/LICENSE + Ruby license for one file from the Ruby source lib/parser/ruby/legacy/ruby_lex.rb
4
+ gem 'rspec' # MIT https://github.com/rspec/rspec/blob/master/License.txt
5
+ gem 'byebug'
6
+ gem 'msfl', "~> 1.1"
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ byebug (4.0.5)
5
+ columnize (= 0.9.0)
6
+ columnize (0.9.0)
7
+ diff-lcs (1.2.5)
8
+ docile (1.1.5)
9
+ json (1.8.2)
10
+ msfl (1.1.6)
11
+ json (~> 1.7)
12
+ rspec (3.2.0)
13
+ rspec-core (~> 3.2.0)
14
+ rspec-expectations (~> 3.2.0)
15
+ rspec-mocks (~> 3.2.0)
16
+ rspec-core (3.2.3)
17
+ rspec-support (~> 3.2.0)
18
+ rspec-expectations (3.2.1)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.2.0)
21
+ rspec-mocks (3.2.1)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.2.0)
24
+ rspec-support (3.2.2)
25
+ simplecov (0.10.0)
26
+ docile (~> 1.1.0)
27
+ json (~> 1.8)
28
+ simplecov-html (~> 0.10.0)
29
+ simplecov-html (0.10.0)
30
+ yard (0.8.7.6)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ byebug
37
+ msfl (~> 1.1)
38
+ rspec
39
+ simplecov
40
+ yard
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Referly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ [![Circle CI](https://circleci.com/gh/Referly/msfl_visitors.svg?style=svg)](https://circleci.com/gh/Referly/msfl-visitors)
2
+
3
+ # msfl_visitors
4
+ A visitor pattern based approach for converting MSFL to other forms
5
+
6
+ ## Usage
7
+
8
+ ```ruby
9
+ # This is not actually working code yet! @todo revisit once Matt's refactor branch is merged.
10
+ require 'msfl_visitor'
11
+
12
+ filter = { make: "Toyota" }
13
+
14
+ collector = String.new
15
+
16
+ renderer = MSFLVisitors::Renderers::Chewy::TermFilter.new
17
+
18
+ visitor = MSFLVisitors::Visitor.new collector, renderer
19
+
20
+ MSFLVisitors::AST.new(filter).accept(visitor)
21
+
22
+ => 'make == "Toyota"'
23
+
24
+ ```
25
+
26
+ ## Architecture
27
+
28
+ msfl_visitors is designed to consume normalized Mattermark Semantic Filter Language (NMSFL).
29
+ msfl_visitors implements a parser (parsers/msfl_parser) that converts NMSFL into an AST.
30
+ msfl_visitors implements a visitor that traverses the ast and produces the well formed output.
31
+ The behavior of the visitor is controlled through composition at construction. It accepts a collector and a renderer.
32
+
33
+ ## MSFLParser
34
+
35
+ The parser accepts a Hash containing NMSFL and produces an AST.
36
+ The parser uses a simplified version of the visitor pattern to traverse the NMSFL and produce the AST.
37
+
38
+ Typically one does not interact with the parser directly, instead a consumer of this gem should interact with the AST.
39
+
40
+ ## AST
41
+
42
+ The abstract syntax tree that represents a certain query filter. In the version of the visitor pattern herein
43
+ adopted, each node of the AST is responsible for managing its state and traversal of itself and children.
44
+
45
+ A consumer of this gem creates a new AST instance passing in a Hash of NMSFL. The AST will leverage the MSFL parser
46
+ to construct itself. The AST object is a Node as it implements the #accept(visitor) method.
47
+
48
+ ## visitor
49
+
50
+ Unlike the classical visitor pattern double dispatch is not strictly achieved through type matching in the visitor.
51
+ Instead the visitor is just a single service that is composed of a collector and a renderer.
52
+ The double dispatch is codified inside of a renderer, which like the visitors in the classic pattern can produce
53
+ multiple output DSLs.
54
+
55
+ ## collector
56
+
57
+ During traversal the output from each node needs to be stored or buffered somewhere. The collector serves this role.
58
+ It can be as simple as a String or an Array, or it can be more elaborate. Ultimately it must respond to the shovel
59
+ operator (<<)
60
+
61
+ ## renderer
62
+
63
+ The logic for rendering the AST nodes into the output DSL is codified in a renderer. The two principle renderers at
64
+ this time are Chewy::TermFilter and Chewy:QueryStringFilter
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake"
2
+ require 'rspec/core/rake_task'
3
+
4
+ task default: [:spec] do
5
+ ENV['RACK_ENV'] = 'test'
6
+ end
7
+
8
+ task :spec do
9
+ RSpec::Core::RakeTask.new(:spec)
10
+ end
data/circle.yml ADDED
@@ -0,0 +1,9 @@
1
+ machine:
2
+
3
+ timezone:
4
+ America/Los_Angeles # Set the timezone
5
+
6
+ # Version of ruby to use
7
+ ruby:
8
+ version:
9
+ 2.1.3
@@ -0,0 +1,7 @@
1
+ require_relative 'msfl_visitors/ast'
2
+ require_relative 'msfl_visitors/visitors/base'
3
+ require_relative 'msfl_visitors/nodes'
4
+ require_relative 'msfl_visitors/visitors/chewy_term_filter'
5
+ require_relative 'msfl_visitors/parsers/msfl_parser'
6
+ module MSFLVisitors
7
+ end
@@ -0,0 +1,15 @@
1
+ module MSFLVisitors
2
+ class AST
3
+
4
+ attr_accessor :root
5
+
6
+ def initialize(msfl)
7
+ self.root = MSFLVisitors::Parsers::MSFLParser.parse msfl
8
+ end
9
+
10
+ # Use this method to walk the AST with a particular visitor
11
+ def accept(visitor)
12
+ root.accept visitor
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,26 @@
1
+ # Using alphabetical order to keep these organized
2
+ require_relative 'nodes/and'
3
+ require_relative 'nodes/base'
4
+ require_relative 'nodes/binary'
5
+ require_relative 'nodes/boolean'
6
+ require_relative 'nodes/comparison'
7
+ require_relative 'nodes/constant_value'
8
+ require_relative 'nodes/date'
9
+ require_relative 'nodes/date_time'
10
+ require_relative 'nodes/equal'
11
+ require_relative 'nodes/filter'
12
+ require_relative 'nodes/greater_than'
13
+ require_relative 'nodes/greater_than_equal'
14
+ require_relative 'nodes/grouping/close'
15
+ require_relative 'nodes/grouping/grouping'
16
+ require_relative 'nodes/grouping/open'
17
+ require_relative 'nodes/less_than'
18
+ require_relative 'nodes/less_than_equal'
19
+ require_relative 'nodes/number'
20
+ require_relative 'nodes/range_value'
21
+ require_relative 'nodes/time'
22
+ require_relative 'nodes/value'
23
+ require_relative 'nodes/word'
24
+
25
+
26
+
@@ -0,0 +1,13 @@
1
+ require_relative 'binary'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class And < Binary
5
+
6
+ def accept(visitor)
7
+ MSFLVisitors::Nodes::Grouping::Grouping.new(left).accept visitor
8
+ visitor.visit self
9
+ MSFLVisitors::Nodes::Grouping::Grouping.new(right).accept visitor
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,7 @@
1
+ module MSFLVisitors
2
+ module Nodes
3
+ class Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'base'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Binary < Base
5
+
6
+ attr_accessor :left, :right
7
+
8
+ def accept(visitor)
9
+ visitor.visit left
10
+ visitor.visit self
11
+ visitor.visit right
12
+ end
13
+
14
+ def initialize(left, right)
15
+ self.left = left
16
+ self.right = right
17
+ end
18
+
19
+ def ==(other)
20
+ self.class == other.class &&
21
+ self.left == other.left &&
22
+ self.right == other.right
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Boolean < Value
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'binary'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Comparison < Binary
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'base'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class ConstantValue < Base
5
+
6
+ def accept(visitor)
7
+ visitor.visit self
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'range_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Date < RangeValue
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'range_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class DateTime < RangeValue
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'comparison'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Equal < Comparison
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'base'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Filter < Base
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
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'comparison'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class GreaterThan < Comparison
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'comparison'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class GreaterThanEqual < Comparison
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require_relative '../constant_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ module Grouping
5
+ class Close < ConstantValue
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../base'
2
+ require_relative 'close'
3
+ require_relative 'open'
4
+ module MSFLVisitors
5
+ module Nodes
6
+ module Grouping
7
+ class Grouping < Base
8
+
9
+ attr_accessor :contents
10
+
11
+ def accept(visitor)
12
+ Open.new.accept visitor
13
+ contents.accept visitor
14
+ Close.new.accept visitor
15
+ end
16
+
17
+ # @param node [MSFL::Nodes::Base] the node that the grouping surrounds
18
+ def initialize(node)
19
+ self.contents = node
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ require_relative '../constant_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ module Grouping
5
+ class Open < ConstantValue
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'comparison'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class LessThan < Comparison
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'comparison'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class LessThanEqual < Comparison
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'range_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Number < RangeValue
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class RangeValue < Value
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'range_value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Time < RangeValue
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require_relative 'base'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Value < Base
5
+
6
+ attr_accessor :value
7
+
8
+ def accept(visitor)
9
+ visitor.visit self
10
+ end
11
+
12
+ def initialize(expr)
13
+ self.value = expr
14
+ end
15
+
16
+ def ==(other)
17
+ self.class == other.class &&
18
+ value == other.value
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'value'
2
+ module MSFLVisitors
3
+ module Nodes
4
+ class Word < Value
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,78 @@
1
+ require 'msfl'
2
+ module MSFLVisitors
3
+ module Parsers
4
+ class MSFLParser
5
+ include MSFL::Validators::Definitions::HashKey
6
+
7
+ def parse(obj, lhs = false)
8
+ # send("parse_#{obj.class.to_s.gsub('::', '_')}", obj, lhs)
9
+ case obj
10
+
11
+ when Float, Fixnum
12
+ MSFLVisitors::Nodes::Number.new obj
13
+
14
+ when Hash
15
+ parse_Hash obj, lhs
16
+
17
+ when MSFL::Types::Set
18
+ parse_Set obj, lhs
19
+
20
+ when Symbol, String, NilClass
21
+ MSFLVisitors::Nodes::Word.new obj.to_s
22
+
23
+ else
24
+ fail ArgumentError, "Invalid NMSFL, unable to parse."
25
+ end
26
+ end
27
+
28
+
29
+
30
+
31
+ private
32
+
33
+ def parse_Hash(obj, lhs = false)
34
+ nodes = Array.new
35
+ obj.each do |k, v|
36
+ nodes << hash_dispatch(k, v, lhs)
37
+ end
38
+ MSFLVisitors::Nodes::Filter.new nodes
39
+ end
40
+
41
+ def parse_Set(obj, lhs = false)
42
+ fail NoMethodError, "#parse_Set is not yet implemented."
43
+ end
44
+
45
+ def hash_dispatch(key, value, lhs = false)
46
+ if hash_key_operators.include? key
47
+ # Detect the node type, forward the lhs if it was passed in (essentially when the operator is a binary op)
48
+ args = [lhs, parse(value)] if lhs
49
+ args ||= [parse(value)]
50
+ Object.const_get("MSFLVisitors::Nodes::#{key.to_s.capitalize}").new(*args)
51
+ else
52
+ # the key is a field
53
+ # there are three possible scenarios when they key is a field
54
+ # 1. the implicit equality scenario, where the right side is a value
55
+ # { make: "toyota" }
56
+ #
57
+ # 2. the explicit comparison scenario
58
+ # { value: { gte: 2000 } }
59
+ #
60
+ # 3. the containment scenario
61
+ # { model: { in: ["Corolla", "Civic", "Mustang"] } }
62
+ #
63
+ # 2 & 3 are just hashes and can be parsed using the same method
64
+ lhs = MSFLVisitors::Nodes::Word.new key
65
+
66
+ # the node type generated by parsing value can use the lhs node when appropriate and otherwise ignore it
67
+ # although I can't think of a situation when it would ignore it.
68
+ rhs = parse value, lhs
69
+ if rhs.is_a? MSFLVisitors::Nodes::Value
70
+ MSFLVisitors::Nodes::Equal.new lhs, rhs
71
+ else
72
+ rhs
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,15 @@
1
+ module MSFLVisitors
2
+ module Visitors
3
+ class Base
4
+ attr_accessor :collector
5
+
6
+ def visit(obj)
7
+ send("visit_#{obj.class.to_s.gsub('::', '_')}", obj, collector)
8
+ end
9
+
10
+ def initialize(collector)
11
+ self.collector = collector
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,70 @@
1
+ module MSFLVisitors
2
+ module Visitors
3
+ class ChewyTermFilter < Base
4
+
5
+ # Instead of using string interpolation only supported operators are enabled
6
+ BINARY_OPERATORS = { eq: "==", gt: ">", gte: ">=", lt: "<", lte: "<=", and: "&" }
7
+
8
+ def visit_MSFLVisitors_Nodes_And(obj, collector)
9
+ binary_helper :and, collector
10
+ end
11
+
12
+ def visit_MSFLVisitors_Nodes_Boolean(obj, collector)
13
+ collector << obj.value
14
+ end
15
+
16
+ def visit_MSFLVisitors_Nodes_Date(obj, collector)
17
+ collector << obj.value.iso8601
18
+ end
19
+
20
+ def visit_MSFLVisitors_Nodes_DateTime(obj, collector)
21
+ collector << obj.value.iso8601
22
+ end
23
+
24
+ def visit_MSFLVisitors_Nodes_Equal(obj, collector)
25
+ binary_helper :eq, collector
26
+ end
27
+
28
+ def visit_MSFLVisitors_Nodes_GreaterThan(obj, collector)
29
+ binary_helper :gt, collector
30
+ end
31
+
32
+ def visit_MSFLVisitors_Nodes_GreaterThanEqual(obj, collector)
33
+ binary_helper :gte, collector
34
+ end
35
+
36
+ def visit_MSFLVisitors_Nodes_Grouping_Close(obj, collector)
37
+ collector << " )"
38
+ end
39
+
40
+ def visit_MSFLVisitors_Nodes_Grouping_Open(obj, collector)
41
+ collector << "( "
42
+ end
43
+
44
+ def visit_MSFLVisitors_Nodes_LessThan(obj, collector)
45
+ binary_helper :lt, collector
46
+ end
47
+
48
+ def visit_MSFLVisitors_Nodes_LessThanEqual(obj, collector)
49
+ binary_helper :lte, collector
50
+ end
51
+
52
+ def visit_MSFLVisitors_Nodes_Number(obj, collector)
53
+ collector << obj.value
54
+ end
55
+
56
+ def visit_MSFLVisitors_Nodes_Time(obj, collector)
57
+ collector << obj.value.iso8601
58
+ end
59
+
60
+ def visit_MSFLVisitors_Nodes_Word(obj, collector)
61
+ collector << "#{obj.value.to_s}"
62
+ end
63
+
64
+ private
65
+ def binary_helper(operator, collector)
66
+ collector << " #{BINARY_OPERATORS.fetch(operator.to_sym)} "
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'msfl_visitors'
3
+ s.version = '0.0.1'
4
+ s.date = '2015-05-01'
5
+ s.summary = "Convert MSFL to other forms"
6
+ s.description = "Visitor pattern approach to converting MSFL to other forms."
7
+ s.authors = ["Courtland Caldwell"]
8
+ s.email = 'courtland@mattermark.com'
9
+ s.files = `git ls-files`.split("\n")
10
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
11
+ s.homepage =
12
+ 'https://github.com/Referly/msfl_visitors'
13
+ s.add_runtime_dependency "msfl", "~> 1.1", '>= 1.1.6'
14
+ s.add_development_dependency "rake", "~> 10.3"
15
+ s.add_development_dependency "simplecov", "~> 0.10"
16
+ s.add_development_dependency "yard", "~> 0.8"
17
+ s.add_development_dependency "rspec", "~> 3.2"
18
+ s.add_development_dependency "byebug", "~> 4.0"
19
+ s.license = "MIT"
20
+ end
@@ -0,0 +1,5 @@
1
+ require 'simplecov'
2
+ SimpleCov.profiles.define 'msfl-visitors' do
3
+ add_filter '/spec'
4
+ add_filter '/test'
5
+ end
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,5 @@
1
+ require_relative '../simplecov_custom_profiles'
2
+ SimpleCov.start 'msfl-visitors'
3
+ require 'rspec/support/spec'
4
+ require 'byebug'
5
+ require 'msfl_visitors'
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe MSFLVisitors::Visitors::ChewyTermFilter do
4
+
5
+ subject { node.accept visitor }
6
+
7
+ let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
8
+
9
+ let(:visitor) { described_class.new collector }
10
+
11
+ let(:collector) { String.new }
12
+
13
+ let(:left) { MSFLVisitors::Nodes::Word.new "lhs" }
14
+
15
+ let(:right) { MSFLVisitors::Nodes::Word.new "rhs" }
16
+
17
+ describe "visiting an And node" do
18
+
19
+ let(:node) { MSFLVisitors::Nodes::And.new left, right }
20
+
21
+ it "matches ( left ) & ( right )" do
22
+ expect(subject).to match "( lhs ) & ( rhs )"
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe MSFLVisitors::Visitors::ChewyTermFilter do
4
+
5
+ subject { node.accept visitor }
6
+
7
+ let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
8
+
9
+ let(:visitor) { described_class.new collector }
10
+
11
+ let(:collector) { String.new }
12
+
13
+ let(:left) { MSFLVisitors::Nodes::Word.new "lhs" }
14
+
15
+ let(:right) { MSFLVisitors::Nodes::Word.new "rhs" }
16
+
17
+ describe "visiting an Equal node" do
18
+
19
+ let(:node) { MSFLVisitors::Nodes::Equal.new left, right }
20
+
21
+ it "matches left == right" do
22
+ expect(subject).to match "lhs == rhs"
23
+ end
24
+ end
25
+
26
+ describe "visiting a GreaterThan node" do
27
+
28
+ let(:node) { MSFLVisitors::Nodes::GreaterThan.new left, right }
29
+
30
+ it "matches left > right" do
31
+ expect(subject).to match "lhs > rhs"
32
+ end
33
+ end
34
+
35
+ describe "visiting a GreaterThanEqual node" do
36
+
37
+ let(:node) { MSFLVisitors::Nodes::GreaterThanEqual.new left, right }
38
+
39
+ it "matches left >= right" do
40
+ expect(subject).to match "lhs >= rhs"
41
+ end
42
+ end
43
+
44
+ describe "visiting a LessThan node" do
45
+
46
+ let(:node) { MSFLVisitors::Nodes::LessThan.new left, right }
47
+
48
+ it "matches left < right" do
49
+ expect(subject).to match "lhs < rhs"
50
+ end
51
+ end
52
+
53
+ describe "visiting a LessThanEqual node" do
54
+
55
+ let(:node) { MSFLVisitors::Nodes::LessThanEqual.new left, right }
56
+
57
+ it "matches left <= right" do
58
+ expect(subject).to match "lhs <= rhs"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe MSFLVisitors::Visitors::ChewyTermFilter do
4
+
5
+ subject { node.accept visitor }
6
+
7
+ let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
8
+
9
+ let(:visitor) { described_class.new collector }
10
+
11
+ let(:collector) { Array.new }
12
+
13
+ describe "visiting a Date node" do
14
+
15
+ let(:node) { MSFLVisitors::Nodes::Date.new Date.today }
16
+
17
+ it "is today's date using iso8601 formatting" do
18
+ expect(subject.first).to eq Date.today.iso8601
19
+ end
20
+ end
21
+
22
+ describe "visiting a DateTime node" do
23
+
24
+ let(:now) { DateTime.now }
25
+
26
+ let(:node) { MSFLVisitors::Nodes::DateTime.new now }
27
+
28
+ it "is the current date and time using iso8601 formatting" do
29
+ expect(subject.first).to eq now.iso8601
30
+ end
31
+ end
32
+
33
+ describe "visiting a Number node" do
34
+
35
+ let(:node) { MSFLVisitors::Nodes::Number.new number }
36
+
37
+ let(:number) { 123 }
38
+
39
+ it "is the number" do
40
+ expect(subject.first).to eq number
41
+ end
42
+
43
+ context "when the number is a float" do
44
+
45
+ let(:number) { 123.456 }
46
+
47
+ it "is the number with the same precision" do
48
+ expect(subject.first).to eq number
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "visiting a Time node" do
54
+
55
+ let(:node) { MSFLVisitors::Nodes::Time.new current_time }
56
+
57
+ let(:current_time) { Time.now }
58
+
59
+ it "is the current time using iso8601 formatting" do
60
+ expect(subject.first).to eq current_time.iso8601
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe MSFLVisitors::Visitors::ChewyTermFilter do
4
+
5
+ subject { node.accept visitor }
6
+
7
+ let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
8
+
9
+ let(:visitor) { described_class.new collector }
10
+
11
+ let(:collector) { Array.new }
12
+
13
+ describe "visiting a Boolean node" do
14
+
15
+ let(:node) { MSFLVisitors::Nodes::Boolean.new value }
16
+
17
+ context "when the node has a value of true" do
18
+
19
+ let(:value) { true }
20
+
21
+ it "is true" do
22
+ expect(subject.first).to be true
23
+ end
24
+ end
25
+
26
+ context "when the node has a value of false" do
27
+
28
+ let(:value) { false }
29
+
30
+ it "is false" do
31
+ expect(subject.first).to be false
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "visiting a Word node" do
37
+
38
+ let(:node) { MSFLVisitors::Nodes::Word.new "node_content" }
39
+
40
+ it "is a literal string" do
41
+ expect(subject.first).to match /node_content/
42
+ end
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msfl_visitors
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Courtland Caldwell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: msfl
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.1.6
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.1.6
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '10.3'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '10.3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: simplecov
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.10'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.10'
61
+ - !ruby/object:Gem::Dependency
62
+ name: yard
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.8'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.8'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.2'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.2'
89
+ - !ruby/object:Gem::Dependency
90
+ name: byebug
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '4.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '4.0'
103
+ description: Visitor pattern approach to converting MSFL to other forms.
104
+ email: courtland@mattermark.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - ".gitignore"
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE
113
+ - README.md
114
+ - Rakefile
115
+ - circle.yml
116
+ - lib/msfl_visitors.rb
117
+ - lib/msfl_visitors/ast.rb
118
+ - lib/msfl_visitors/nodes.rb
119
+ - lib/msfl_visitors/nodes/and.rb
120
+ - lib/msfl_visitors/nodes/base.rb
121
+ - lib/msfl_visitors/nodes/binary.rb
122
+ - lib/msfl_visitors/nodes/boolean.rb
123
+ - lib/msfl_visitors/nodes/comparison.rb
124
+ - lib/msfl_visitors/nodes/constant_value.rb
125
+ - lib/msfl_visitors/nodes/date.rb
126
+ - lib/msfl_visitors/nodes/date_time.rb
127
+ - lib/msfl_visitors/nodes/equal.rb
128
+ - lib/msfl_visitors/nodes/filter.rb
129
+ - lib/msfl_visitors/nodes/greater_than.rb
130
+ - lib/msfl_visitors/nodes/greater_than_equal.rb
131
+ - lib/msfl_visitors/nodes/grouping/close.rb
132
+ - lib/msfl_visitors/nodes/grouping/grouping.rb
133
+ - lib/msfl_visitors/nodes/grouping/open.rb
134
+ - lib/msfl_visitors/nodes/less_than.rb
135
+ - lib/msfl_visitors/nodes/less_than_equal.rb
136
+ - lib/msfl_visitors/nodes/number.rb
137
+ - lib/msfl_visitors/nodes/range_value.rb
138
+ - lib/msfl_visitors/nodes/time.rb
139
+ - lib/msfl_visitors/nodes/value.rb
140
+ - lib/msfl_visitors/nodes/word.rb
141
+ - lib/msfl_visitors/parsers/msfl_parser.rb
142
+ - lib/msfl_visitors/visitors/base.rb
143
+ - lib/msfl_visitors/visitors/chewy_term_filter.rb
144
+ - msfl_visitors.gemspec
145
+ - simplecov_custom_profiles.rb
146
+ - spec/parsers/parse_msfl_spec.rb
147
+ - spec/spec_helper.rb
148
+ - spec/visitors/chewy_term_filter/visit_binary_spec.rb
149
+ - spec/visitors/chewy_term_filter/visit_comparison_spec.rb
150
+ - spec/visitors/chewy_term_filter/visit_range_value_spec.rb
151
+ - spec/visitors/chewy_term_filter/visit_value_spec.rb
152
+ homepage: https://github.com/Referly/msfl_visitors
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.2.2
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: Convert MSFL to other forms
176
+ test_files:
177
+ - spec/parsers/parse_msfl_spec.rb
178
+ - spec/spec_helper.rb
179
+ - spec/visitors/chewy_term_filter/visit_binary_spec.rb
180
+ - spec/visitors/chewy_term_filter/visit_comparison_spec.rb
181
+ - spec/visitors/chewy_term_filter/visit_range_value_spec.rb
182
+ - spec/visitors/chewy_term_filter/visit_value_spec.rb
183
+ has_rdoc: