CloudSesame 0.7.5 → 0.7.7
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.lock +14 -12
- data/Guardfile +2 -2
- data/cloud_sesame.gemspec +2 -2
- data/coverage/.last_run.json +1 -1
- data/coverage/.resultset.json +1363 -248
- data/lib/cloud_sesame/domain/base.rb +1 -1
- data/lib/cloud_sesame/query/ast/abstract/multi_expression_operator.rb +3 -4
- data/lib/cloud_sesame/query/ast/abstract/value.rb +10 -2
- data/lib/cloud_sesame/query/ast/date_value.rb +1 -4
- data/lib/cloud_sesame/query/ast/literal.rb +20 -12
- data/lib/cloud_sesame/query/ast/{field_array.rb → multi_expression_operator_children.rb} +8 -2
- data/lib/cloud_sesame/query/ast/not.rb +1 -7
- data/lib/cloud_sesame/query/ast/range_value.rb +43 -28
- data/lib/cloud_sesame/query/ast/root.rb +2 -4
- data/lib/cloud_sesame/query/ast/value.rb +1 -9
- data/lib/cloud_sesame/query/builder.rb +9 -11
- data/lib/cloud_sesame/query/domain/block.rb +1 -3
- data/lib/cloud_sesame/query/domain/chaining_block.rb +3 -4
- data/lib/cloud_sesame/query/domain/literal.rb +1 -3
- data/lib/cloud_sesame/query/dsl/bind_caller.rb +13 -6
- data/lib/cloud_sesame/query/dsl/{field_array_methods.rb → literal_chaining_methods.rb} +1 -6
- data/lib/cloud_sesame/query/dsl/{field_accessors.rb → literal_methods.rb} +1 -1
- data/lib/cloud_sesame/query/node/fuzziness.rb +1 -1
- data/lib/cloud_sesame.rb +4 -3
- data/spec/cloud_sesame/query/ast/abstract/multi_expression_operator_spec.rb +5 -1
- data/spec/cloud_sesame/query/ast/field_array_spec.rb +57 -0
- data/spec/cloud_sesame/query/ast/literal_spec.rb +136 -0
- data/spec/cloud_sesame/query/ast/not_spec.rb +30 -0
- data/spec/cloud_sesame/query/ast/phrase_spec.rb +20 -0
- data/spec/cloud_sesame/query/ast/range_value_spec.rb +245 -30
- data/spec/cloud_sesame/query/ast/string_value_spec.rb +39 -0
- data/spec/cloud_sesame/query/ast/term_spec.rb +20 -0
- data/spec/cloud_sesame/query/ast/value_spec.rb +109 -0
- data/spec/cloud_sesame/query/builder_spec.rb +4 -4
- data/spec/cloud_sesame/query/domain/block_spec.rb +3 -3
- data/spec/cloud_sesame/query/dsl/{field_array_methods_spec.rb → literal_chaining_methods_spec.rb} +1 -1
- data/spec/cloud_sesame/query/dsl/{field_accessors_spec.rb → literal_methods_spec.rb} +3 -3
- data/spec/cloud_sesame/query/node/query_spec.rb +22 -0
- data/spec/profiling_spec.rb +155 -155
- metadata +23 -9
@@ -9,14 +9,14 @@ module CloudSesame
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def applied(included = true)
|
12
|
-
children.map { |child| child.applied
|
12
|
+
children.map { |child| child.applied included }
|
13
13
|
end
|
14
14
|
|
15
15
|
def children
|
16
16
|
@children ||= build_children
|
17
17
|
end
|
18
18
|
|
19
|
-
def compile
|
19
|
+
def compile(_ = nil)
|
20
20
|
if !children.empty? && (compiled = children.compile) && !compiled.empty?
|
21
21
|
"(#{ symbol }#{ boost } #{ compiled })"
|
22
22
|
end
|
@@ -25,8 +25,7 @@ module CloudSesame
|
|
25
25
|
private
|
26
26
|
|
27
27
|
def build_children
|
28
|
-
(
|
29
|
-
children
|
28
|
+
MultiExpressionOperatorChildren.build(self)
|
30
29
|
end
|
31
30
|
|
32
31
|
end
|
@@ -6,6 +6,8 @@ module CloudSesame
|
|
6
6
|
module Abstract
|
7
7
|
class Value
|
8
8
|
|
9
|
+
# CONSTANTS
|
10
|
+
# =====================================
|
9
11
|
RANGE_FORMAT = Regexp.new(/\A(\[|{)(.*),(.*)(\}|\])\z/).freeze
|
10
12
|
DIGIT_FORMAT = Regexp.new(/\A\d+(.\d+)?\z/).freeze
|
11
13
|
DATETIME_FORMAT = Regexp.new(/\d+{4}-\d+{2}-\d+{2}T\d+{2}:\d+{2}:\d+{2}/).freeze
|
@@ -13,12 +15,15 @@ module CloudSesame
|
|
13
15
|
|
14
16
|
attr_reader :value, :changed, :compiled
|
15
17
|
|
18
|
+
# CLASS METHODS
|
19
|
+
# =====================================
|
20
|
+
|
16
21
|
def self.range?(value)
|
17
22
|
value.kind_of?(Range)
|
18
23
|
end
|
19
24
|
|
20
25
|
def self.string_range?(value)
|
21
|
-
RANGE_FORMAT =~ strip(value)
|
26
|
+
value.is_a?(String) && !!(RANGE_FORMAT =~ strip(value))
|
22
27
|
end
|
23
28
|
|
24
29
|
def self.numeric?(value)
|
@@ -26,7 +31,7 @@ module CloudSesame
|
|
26
31
|
end
|
27
32
|
|
28
33
|
def self.string_numeric?(value)
|
29
|
-
DIGIT_FORMAT =~ value
|
34
|
+
value.is_a?(String) && !!(DIGIT_FORMAT =~ value)
|
30
35
|
end
|
31
36
|
|
32
37
|
def self.datetime?(value)
|
@@ -41,6 +46,9 @@ module CloudSesame
|
|
41
46
|
value.is_a?(String) && DATE_FORMAT =~ value
|
42
47
|
end
|
43
48
|
|
49
|
+
# INSTANCE METHODS
|
50
|
+
# =====================================
|
51
|
+
|
44
52
|
def initialize(value, type = nil)
|
45
53
|
self.value = value
|
46
54
|
@type = type
|
@@ -8,10 +8,7 @@ module CloudSesame
|
|
8
8
|
DATE_FORMAT = '%F'.freeze
|
9
9
|
|
10
10
|
def self.parse(value)
|
11
|
-
if value.kind_of?(RangeValue)
|
12
|
-
value.type = self
|
13
|
-
return value
|
14
|
-
end
|
11
|
+
return value.parse self if value.kind_of?(RangeValue)
|
15
12
|
|
16
13
|
range?(value) || string_range?(value) ? RangeValue.new(value, self) :
|
17
14
|
string_datetime?(value) ? new(parse_datetime(value)) :
|
@@ -6,45 +6,53 @@ module CloudSesame
|
|
6
6
|
SINGLE_QUATE = Regexp.new(/\'/).freeze
|
7
7
|
ESCAPE_QUATE = "\\'".freeze
|
8
8
|
|
9
|
-
attr_reader :value
|
9
|
+
attr_reader :field, :value, :options
|
10
10
|
|
11
11
|
def initialize(field, value, options = {})
|
12
12
|
@field = field
|
13
|
-
@options = options
|
14
|
-
@value =
|
13
|
+
@options = options || default_options
|
14
|
+
@value = parse_value(value) if value
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
@
|
19
|
-
@options.merge! options
|
17
|
+
def actual_field_name
|
18
|
+
@options[:as] || @field
|
20
19
|
end
|
21
20
|
|
22
21
|
def applied(included)
|
23
22
|
{ field: @field, value: @value, included: included } if @value
|
24
23
|
end
|
25
24
|
|
26
|
-
def as_field
|
27
|
-
@options[:as] || @field
|
28
|
-
end
|
29
|
-
|
30
25
|
def compile(detailed = false)
|
31
26
|
(detailed ? detailed_format : standard_format) if @value
|
32
27
|
end
|
33
28
|
|
29
|
+
def is_for(field, options)
|
30
|
+
@field = field
|
31
|
+
@options.merge! options if options
|
32
|
+
end
|
33
|
+
|
34
34
|
private
|
35
35
|
|
36
36
|
def standard_format
|
37
|
-
"#{
|
37
|
+
"#{ actual_field_name }:#{ @value.compile }"
|
38
38
|
end
|
39
39
|
|
40
40
|
def detailed_format
|
41
|
-
"field=#{ escape
|
41
|
+
"field=#{ escape actual_field_name.to_s } #{ @value.compile }"
|
42
42
|
end
|
43
43
|
|
44
44
|
def escape(data)
|
45
45
|
"'#{ data.gsub(SINGLE_QUATE) { ESCAPE_QUATE } }'"
|
46
46
|
end
|
47
47
|
|
48
|
+
def parse_value(value)
|
49
|
+
LazyObject.new { (@options[:type] || Value).parse(value) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_options
|
53
|
+
{ type: Value }
|
54
|
+
end
|
55
|
+
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
module CloudSesame
|
2
2
|
module Query
|
3
3
|
module AST
|
4
|
-
class
|
5
|
-
include DSL::
|
4
|
+
class MultiExpressionOperatorChildren < Array
|
5
|
+
include DSL::LiteralChainingMethods
|
6
6
|
|
7
7
|
attr_accessor :_scope, :_return
|
8
8
|
attr_reader :field
|
9
9
|
|
10
|
+
def self.build(scope, children = nil)
|
11
|
+
array = new(children || [])
|
12
|
+
array._scope = scope
|
13
|
+
array
|
14
|
+
end
|
15
|
+
|
10
16
|
def field=(field)
|
11
17
|
parents.clear
|
12
18
|
@field = field
|
@@ -4,14 +4,8 @@ module CloudSesame
|
|
4
4
|
class Not < Abstract::SingleExpressionOperator
|
5
5
|
SYMBOL = :not
|
6
6
|
|
7
|
-
def compile
|
8
|
-
if child && (compiled = child.compile) && !compiled.empty?
|
9
|
-
"(#{ symbol }#{ boost } #{ compiled })"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
7
|
def applied(included = true)
|
14
|
-
child.applied
|
8
|
+
child.applied !included
|
15
9
|
end
|
16
10
|
|
17
11
|
end
|
@@ -4,35 +4,54 @@ module CloudSesame
|
|
4
4
|
class RangeValue < Abstract::Value
|
5
5
|
|
6
6
|
def initialize(value = nil, type = nil)
|
7
|
-
self.value =
|
8
|
-
|
9
|
-
|
7
|
+
self.value = RangeValue.range?(value) ? build_from_range(value) :
|
8
|
+
RangeValue.string_range?(value) ? build_from_string(value) :
|
9
|
+
initialize_value
|
10
|
+
self.parse type
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse(type)
|
14
|
+
if type && type.respond_to?(:parse)
|
15
|
+
@changed = true
|
16
|
+
value[1] = type.parse(self.begin) unless self.begin.to_s.empty?
|
17
|
+
value[2] = type.parse(self.end) unless self.end.to_s.empty?
|
18
|
+
end
|
19
|
+
return self
|
20
|
+
end
|
21
|
+
|
22
|
+
def begin
|
23
|
+
value[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def end
|
27
|
+
value[2]
|
28
|
+
end
|
10
29
|
|
11
|
-
|
30
|
+
def lower_bound
|
31
|
+
value[0]
|
12
32
|
end
|
13
33
|
|
14
|
-
def
|
15
|
-
|
16
|
-
type
|
34
|
+
def upper_bound
|
35
|
+
value[3]
|
17
36
|
end
|
18
37
|
|
19
38
|
def gt(value)
|
20
|
-
|
39
|
+
set_begin(value) if value
|
21
40
|
return self
|
22
41
|
end
|
23
42
|
|
24
43
|
def gte(value)
|
25
|
-
|
44
|
+
set_begin(value, '[') if value
|
26
45
|
return self
|
27
46
|
end
|
28
47
|
|
29
48
|
def lt(value)
|
30
|
-
|
49
|
+
set_end(value) if value
|
31
50
|
return self
|
32
51
|
end
|
33
52
|
|
34
53
|
def lte(value)
|
35
|
-
|
54
|
+
set_end(value, ']') if value
|
36
55
|
return self
|
37
56
|
end
|
38
57
|
|
@@ -41,7 +60,7 @@ module CloudSesame
|
|
41
60
|
end
|
42
61
|
|
43
62
|
def ==(object)
|
44
|
-
value == RangeValue.new(object, Value).value
|
63
|
+
value == (object.is_a?(RangeValue) ? object : RangeValue.new(object, Value)).value
|
45
64
|
end
|
46
65
|
|
47
66
|
private
|
@@ -50,30 +69,26 @@ module CloudSesame
|
|
50
69
|
super "#{ value[0] }#{ value[1] },#{ value[2] }#{ value[3] }"
|
51
70
|
end
|
52
71
|
|
53
|
-
def
|
54
|
-
|
55
|
-
self.value[
|
56
|
-
end
|
57
|
-
|
58
|
-
def update_uppoer_value(value, included = '}')
|
59
|
-
self.value[2] = value
|
60
|
-
self.value[3] = included
|
72
|
+
def set_begin(value, included = '{')
|
73
|
+
@changed = true
|
74
|
+
self.value[0, 2] = [included, value]
|
61
75
|
end
|
62
76
|
|
63
|
-
def
|
64
|
-
|
77
|
+
def set_end(value, included = '}')
|
78
|
+
@changed = true
|
79
|
+
self.value[2, 2] = [value, included]
|
65
80
|
end
|
66
81
|
|
67
|
-
def
|
68
|
-
|
82
|
+
def build_from_range(range)
|
83
|
+
initialize_value('[', range.begin, range.end, range.exclude_end? ? '}' : ']')
|
69
84
|
end
|
70
85
|
|
71
|
-
def
|
72
|
-
|
86
|
+
def build_from_string(string)
|
87
|
+
initialize_value(*RANGE_FORMAT.match(strip(string)).captures)
|
73
88
|
end
|
74
89
|
|
75
|
-
def
|
76
|
-
[
|
90
|
+
def initialize_value(lb = '[', bv = nil, ev = nil, up = ']')
|
91
|
+
[lb, bv, ev, up]
|
77
92
|
end
|
78
93
|
|
79
94
|
end
|
@@ -13,21 +13,13 @@ module CloudSesame
|
|
13
13
|
(klass =TYPES[symbol]) ? klass : self
|
14
14
|
end
|
15
15
|
|
16
|
-
# if the value is already a range value object
|
17
|
-
# set the type to Value and return the value
|
18
|
-
# else determine the type of value and create it
|
19
16
|
def self.parse(value)
|
20
|
-
if value.kind_of?(RangeValue)
|
21
|
-
value.type = self
|
22
|
-
return value
|
23
|
-
end
|
24
|
-
|
17
|
+
return value.parse self if value.kind_of?(RangeValue)
|
25
18
|
(
|
26
19
|
range_value?(value) ? RangeValue :
|
27
20
|
numeric_value?(value) ? NumericValue :
|
28
21
|
datetime?(value) ? DateValue : StringValue
|
29
22
|
).new(value, self)
|
30
|
-
|
31
23
|
end
|
32
24
|
|
33
25
|
def self.range_value?(value)
|
@@ -15,16 +15,16 @@ module CloudSesame
|
|
15
15
|
# ClassSpecific construct class callback
|
16
16
|
#
|
17
17
|
# after construct searchable specific builder,
|
18
|
-
# construct searchable specific DSL::
|
18
|
+
# construct searchable specific DSL::LiteralMethods,
|
19
19
|
# and Domain::Block and include the new field accessors
|
20
20
|
# in both builder and domain block
|
21
21
|
# ===================================================
|
22
22
|
after_construct do |searchable|
|
23
|
-
@
|
24
|
-
@block_domain = Domain::Block.construct_class(searchable, callback_args: [
|
23
|
+
@literal_methods = DSL::LiteralMethods.construct_module(searchable)
|
24
|
+
@block_domain = Domain::Block.construct_class(searchable, callback_args: [literal_methods])
|
25
25
|
@request = Node::Request.construct_class(searchable)
|
26
26
|
|
27
|
-
include
|
27
|
+
include literal_methods
|
28
28
|
end
|
29
29
|
|
30
30
|
# Domain::Block getter
|
@@ -37,9 +37,9 @@ module CloudSesame
|
|
37
37
|
@request ||= Node::Request
|
38
38
|
end
|
39
39
|
|
40
|
-
# DSL::
|
41
|
-
def self.
|
42
|
-
@
|
40
|
+
# DSL::LiteralMethods getter
|
41
|
+
def self.literal_methods
|
42
|
+
@literal_methods ||= DSL::LiteralMethods
|
43
43
|
end
|
44
44
|
|
45
45
|
# ===================================================
|
@@ -62,10 +62,8 @@ module CloudSesame
|
|
62
62
|
private
|
63
63
|
|
64
64
|
def _block_domain(block)
|
65
|
-
|
66
|
-
|
67
|
-
self.class.block_domain.new caller, _context
|
68
|
-
end
|
65
|
+
caller = block ? block.binding.eval("self") : nil
|
66
|
+
self.class.block_domain.new caller, _context
|
69
67
|
end
|
70
68
|
|
71
69
|
def _scope
|
@@ -15,11 +15,9 @@ module CloudSesame
|
|
15
15
|
attr_reader :_caller, :_context, :_scopes
|
16
16
|
|
17
17
|
def initialize(_caller, _context)
|
18
|
-
|
18
|
+
self._caller = _caller
|
19
19
|
@_context = _context
|
20
20
|
@_scopes = []
|
21
|
-
|
22
|
-
_bind_caller_instance_variables
|
23
21
|
end
|
24
22
|
|
25
23
|
def _eval(node, _scope, _return = _scope, &block)
|
@@ -28,10 +28,9 @@ module CloudSesame
|
|
28
28
|
private
|
29
29
|
|
30
30
|
def _block_domain(block)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
)
|
31
|
+
caller = block.binding.eval("self")
|
32
|
+
@_block_domain._caller = caller
|
33
|
+
@_block_domain
|
35
34
|
end
|
36
35
|
|
37
36
|
def missing_block
|
@@ -3,11 +3,16 @@ module CloudSesame
|
|
3
3
|
module DSL
|
4
4
|
module BindCaller
|
5
5
|
|
6
|
+
def _caller=(caller)
|
7
|
+
__bind_caller_instance_variables__(caller) if caller
|
8
|
+
@_caller = caller
|
9
|
+
end
|
10
|
+
|
6
11
|
private
|
7
12
|
|
8
|
-
def
|
9
|
-
|
10
|
-
value =
|
13
|
+
def __bind_caller_instance_variables__(caller)
|
14
|
+
caller.instance_variables.each do |name|
|
15
|
+
value = caller.instance_variable_get name
|
11
16
|
instance_variable_set name, value
|
12
17
|
end
|
13
18
|
end
|
@@ -15,9 +20,11 @@ module CloudSesame
|
|
15
20
|
# ACCESS CALLER'S METHODS
|
16
21
|
# =========================================
|
17
22
|
def method_missing(name, *args, &block)
|
18
|
-
_caller.
|
19
|
-
|
20
|
-
|
23
|
+
if _caller && _caller.respond_to?(name, *args, &block)
|
24
|
+
_caller.send(name, *args, &block)
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
end
|
@@ -1,12 +1,7 @@
|
|
1
|
-
# =========================================================
|
2
|
-
# field array methods enables field array to chain
|
3
|
-
# operators after calling the field expression
|
4
|
-
# accessor
|
5
|
-
# =========================================================
|
6
1
|
module CloudSesame
|
7
2
|
module Query
|
8
3
|
module DSL
|
9
|
-
module
|
4
|
+
module LiteralChainingMethods
|
10
5
|
|
11
6
|
# NOT
|
12
7
|
# =======================================
|
data/lib/cloud_sesame.rb
CHANGED
@@ -25,10 +25,11 @@ require 'cloud_sesame/config/credential'
|
|
25
25
|
require 'cloud_sesame/query/dsl/applied_filter_query'
|
26
26
|
require 'cloud_sesame/query/dsl/bind_caller'
|
27
27
|
require 'cloud_sesame/query/dsl/block_styled_operators'
|
28
|
-
require 'cloud_sesame/query/dsl/
|
28
|
+
require 'cloud_sesame/query/dsl/literal_methods'
|
29
|
+
require 'cloud_sesame/query/dsl/literal_chaining_methods'
|
29
30
|
require 'cloud_sesame/query/dsl/scope_accessors'
|
30
31
|
require 'cloud_sesame/query/dsl/operators'
|
31
|
-
|
32
|
+
|
32
33
|
require 'cloud_sesame/query/dsl/inspect_method'
|
33
34
|
require 'cloud_sesame/query/dsl/page_methods'
|
34
35
|
require 'cloud_sesame/query/dsl/query_methods'
|
@@ -49,7 +50,7 @@ require 'cloud_sesame/query/ast/abstract/operator'
|
|
49
50
|
require 'cloud_sesame/query/ast/abstract/multi_expression_operator'
|
50
51
|
require 'cloud_sesame/query/ast/abstract/single_expression_operator'
|
51
52
|
require 'cloud_sesame/query/ast/abstract/value'
|
52
|
-
require 'cloud_sesame/query/ast/
|
53
|
+
require 'cloud_sesame/query/ast/multi_expression_operator_children'
|
53
54
|
require 'cloud_sesame/query/ast/and'
|
54
55
|
require 'cloud_sesame/query/ast/or'
|
55
56
|
require 'cloud_sesame/query/ast/not'
|
@@ -19,11 +19,15 @@ module CloudSesame
|
|
19
19
|
expect(subject.children).to be_empty
|
20
20
|
end
|
21
21
|
it 'should be an Field Array' do
|
22
|
-
expect(subject.children).to be_a AST::
|
22
|
+
expect(subject.children).to be_a AST::MultiExpressionOperatorChildren
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
describe '#compile' do
|
27
|
+
it 'should accept one optional param' do
|
28
|
+
expect{ subject.compile(true) }.to_not raise_error
|
29
|
+
end
|
30
|
+
|
27
31
|
context 'when theres children' do
|
28
32
|
let(:child) { OpenStruct.new(compile: "compiled") }
|
29
33
|
before { subject.children << child }
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
module AST
|
4
|
+
describe MultiExpressionOperatorChildren do
|
5
|
+
|
6
|
+
describe '.build' do
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
context 'when children is given' do
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#field=' do
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#parents' do
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#_context' do
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#compile' do
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# class FieldArray < Array
|
32
|
+
# include DSL::FieldArrayMethods
|
33
|
+
|
34
|
+
# attr_accessor :_scope, :_return
|
35
|
+
# attr_reader :field
|
36
|
+
|
37
|
+
# def field=(field)
|
38
|
+
# parents.clear
|
39
|
+
# @field = field
|
40
|
+
# end
|
41
|
+
|
42
|
+
# def parents
|
43
|
+
# @parents ||= []
|
44
|
+
# end
|
45
|
+
|
46
|
+
# def _context
|
47
|
+
# _scope && _scope.context
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def compile
|
51
|
+
# map(&:compile).join(' ')
|
52
|
+
# end
|
53
|
+
|
54
|
+
# end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|