squeel 1.0.9 → 1.0.11
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.
- data/CHANGELOG.md +10 -0
- data/lib/squeel.rb +5 -0
- data/lib/squeel/adapters/active_record/3.0/association_preload_extensions.rb +2 -2
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +98 -46
- data/lib/squeel/adapters/active_record/3.1/preloader_extensions.rb +2 -2
- data/lib/squeel/adapters/active_record/3.1/relation_extensions.rb +71 -85
- data/lib/squeel/adapters/active_record/base_extensions.rb +0 -19
- data/lib/squeel/adapters/active_record/relation_extensions.rb +6 -9
- data/lib/squeel/nodes/binary.rb +4 -0
- data/lib/squeel/nodes/function.rb +10 -0
- data/lib/squeel/nodes/nary.rb +1 -1
- data/lib/squeel/nodes/order.rb +11 -1
- data/lib/squeel/nodes/predicate.rb +2 -2
- data/lib/squeel/nodes/stub.rb +2 -0
- data/lib/squeel/nodes/unary.rb +4 -0
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors.rb +10 -3
- data/lib/squeel/visitors/from_visitor.rb +6 -0
- data/lib/squeel/visitors/group_visitor.rb +6 -0
- data/lib/squeel/visitors/having_visitor.rb +9 -0
- data/lib/squeel/visitors/order_visitor.rb +20 -0
- data/lib/squeel/visitors/predicate_visitation.rb +126 -0
- data/lib/squeel/visitors/predicate_visitor.rb +3 -326
- data/lib/squeel/visitors/{symbol_visitor.rb → preload_visitor.rb} +4 -4
- data/lib/squeel/visitors/select_visitor.rb +7 -0
- data/lib/squeel/visitors/visitor.rb +244 -12
- data/lib/squeel/visitors/where_visitor.rb +8 -0
- data/spec/helpers/squeel_helper.rb +14 -1
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +65 -49
- data/spec/squeel/nodes/as_spec.rb +20 -0
- data/spec/squeel/nodes/function_spec.rb +6 -0
- data/spec/squeel/nodes/grouping_spec.rb +6 -0
- data/spec/squeel/nodes/key_path_spec.rb +3 -3
- data/spec/squeel/nodes/operation_spec.rb +6 -0
- data/spec/squeel/nodes/order_spec.rb +6 -1
- data/spec/squeel/nodes/predicate_operators_spec.rb +1 -1
- data/spec/squeel/nodes/predicate_spec.rb +14 -1
- data/spec/squeel/nodes/sifter_spec.rb +2 -1
- data/spec/squeel/nodes/stub_spec.rb +4 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +36 -0
- data/spec/squeel/visitors/{symbol_visitor_spec.rb → preload_visitor_spec.rb} +4 -3
- data/spec/squeel/visitors/select_visitor_spec.rb +26 -0
- data/spec/squeel/visitors/{attribute_visitor_spec.rb → visitor_spec.rb} +23 -37
- data/spec/support/models.rb +4 -0
- metadata +22 -10
- data/lib/squeel/visitors/attribute_visitor.rb +0 -214
@@ -16,25 +16,6 @@ module Squeel
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def build_default_scope_with_squeel #:nodoc:
|
20
|
-
if defined?(::ActiveRecord::Scoping) &&
|
21
|
-
method(:default_scope).owner != ::ActiveRecord::Scoping::Default::ClassMethods
|
22
|
-
evaluate_default_scope { default_scope }
|
23
|
-
elsif default_scopes.any?
|
24
|
-
evaluate_default_scope do
|
25
|
-
default_scopes.inject(relation) do |default_scope, scope|
|
26
|
-
if scope.is_a?(Hash)
|
27
|
-
default_scope.apply_finder_options(scope)
|
28
|
-
elsif !scope.is_a?(::ActiveRecord::Relation) && scope.respond_to?(:call)
|
29
|
-
default_scope.merge(scope.call, true)
|
30
|
-
else
|
31
|
-
default_scope.merge(scope, true)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
19
|
end
|
39
20
|
end
|
40
21
|
end
|
@@ -10,26 +10,23 @@ module Squeel
|
|
10
10
|
|
11
11
|
build_join_dependency(arel, @joins_values) unless @joins_values.empty?
|
12
12
|
|
13
|
-
|
14
|
-
attribute_viz = attribute_visitor
|
13
|
+
collapse_wheres(arel, where_visit((@where_values - ['']).uniq))
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
arel.having(*predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})) unless @having_values.empty?
|
15
|
+
arel.having(*having_visit(@having_values.uniq.reject{|h| h.blank?})) unless @having_values.empty?
|
19
16
|
|
20
17
|
arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
|
21
18
|
arel.skip(@offset_value) if @offset_value
|
22
19
|
|
23
|
-
arel.group(*
|
20
|
+
arel.group(*group_visit(@group_values.uniq.reject{|g| g.blank?})) unless @group_values.empty?
|
24
21
|
|
25
|
-
order =
|
22
|
+
order = order_visit(@order_values)
|
26
23
|
order = reverse_sql_order(attrs_to_orderings(order)) if @reverse_order_value
|
27
24
|
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
|
28
25
|
|
29
|
-
build_select(arel,
|
26
|
+
build_select(arel, select_visit(@select_values.uniq))
|
30
27
|
|
31
28
|
arel.distinct(@uniq_value)
|
32
|
-
arel.from(@from_value) if @from_value
|
29
|
+
arel.from(from_visit(@from_value)) if @from_value
|
33
30
|
arel.lock(@lock_value) if @lock_value
|
34
31
|
|
35
32
|
arel
|
data/lib/squeel/nodes/binary.rb
CHANGED
data/lib/squeel/nodes/nary.rb
CHANGED
data/lib/squeel/nodes/order.rb
CHANGED
@@ -48,6 +48,16 @@ module Squeel
|
|
48
48
|
@direction = - @direction
|
49
49
|
self
|
50
50
|
end
|
51
|
+
|
52
|
+
def hash
|
53
|
+
[@expr, @direction].hash
|
54
|
+
end
|
55
|
+
|
56
|
+
def eql?(other)
|
57
|
+
self.class.eql?(other.class) &&
|
58
|
+
self.expr.eql?(other.expr) &&
|
59
|
+
self.direction.eql?(other.direction)
|
60
|
+
end
|
51
61
|
end
|
52
62
|
end
|
53
|
-
end
|
63
|
+
end
|
@@ -8,6 +8,7 @@ module Squeel
|
|
8
8
|
class Predicate
|
9
9
|
|
10
10
|
include PredicateOperators
|
11
|
+
include Aliasing
|
11
12
|
|
12
13
|
# @return The right-hand value being considered in this predicate.
|
13
14
|
attr_accessor :value
|
@@ -34,11 +35,10 @@ module Squeel
|
|
34
35
|
self.method_name.eql?(other.method_name) &&
|
35
36
|
self.value.eql?(other.value)
|
36
37
|
end
|
37
|
-
alias :== :eql?
|
38
38
|
|
39
39
|
# Implemented for equality testing
|
40
40
|
def hash
|
41
|
-
[
|
41
|
+
[@expr, @method_name, @value].hash
|
42
42
|
end
|
43
43
|
|
44
44
|
# Whether the value has been assigned yet.
|
data/lib/squeel/nodes/stub.rb
CHANGED
@@ -39,6 +39,8 @@ module Squeel
|
|
39
39
|
|
40
40
|
# Object comparison
|
41
41
|
def eql?(other)
|
42
|
+
# Should we maybe allow a stub to equal a symbol?
|
43
|
+
# I can see not doing to leading to confusion, but I don't like it. :(
|
42
44
|
self.class.eql?(other.class) &&
|
43
45
|
self.symbol.eql?(other.symbol)
|
44
46
|
end
|
data/lib/squeel/nodes/unary.rb
CHANGED
data/lib/squeel/version.rb
CHANGED
data/lib/squeel/visitors.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
-
require 'squeel/visitors/
|
2
|
-
require 'squeel/visitors/
|
3
|
-
|
1
|
+
require 'squeel/visitors/visitor'
|
2
|
+
require 'squeel/visitors/predicate_visitation'
|
3
|
+
|
4
|
+
require 'squeel/visitors/where_visitor'
|
5
|
+
require 'squeel/visitors/having_visitor'
|
6
|
+
require 'squeel/visitors/group_visitor'
|
7
|
+
require 'squeel/visitors/order_visitor'
|
8
|
+
require 'squeel/visitors/select_visitor'
|
9
|
+
require 'squeel/visitors/from_visitor'
|
10
|
+
require 'squeel/visitors/preload_visitor'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Squeel
|
2
|
+
module Visitors
|
3
|
+
class OrderVisitor < Visitor
|
4
|
+
include PredicateVisitation
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# Visit an Order node.
|
9
|
+
#
|
10
|
+
# @param [Nodes::Order] o The order node to visit
|
11
|
+
# @param parent The node's parent within the context
|
12
|
+
# @return [Arel::Nodes::Ordering] An ascending or desending ordering
|
13
|
+
def visit_Squeel_Nodes_Order(o, parent)
|
14
|
+
visit(o.expr, parent).send(o.descending? ? :desc : :asc)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Squeel
|
2
|
+
module Visitors
|
3
|
+
module PredicateVisitation
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
TRUE_SQL = Arel.sql('1=1').freeze
|
8
|
+
FALSE_SQL = Arel.sql('1=0').freeze
|
9
|
+
|
10
|
+
# Visit a Squeel sifter by executing its corresponding constraint block
|
11
|
+
# in the parent's class, with its given arguments, then visiting the
|
12
|
+
# result.
|
13
|
+
#
|
14
|
+
# @param [Nodes::Sifter] o The Sifter to visit
|
15
|
+
# @param parent The parent object in the context
|
16
|
+
# @return The result of visiting the executed block's return value
|
17
|
+
def visit_Squeel_Nodes_Sifter(o, parent)
|
18
|
+
klass = classify(parent)
|
19
|
+
visit(klass.send(o.name, *o.args), parent)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Visit a Squeel predicate, converting it into an ARel predicate
|
23
|
+
#
|
24
|
+
# @param [Nodes::Predicate] o The predicate to visit
|
25
|
+
# @param parent The parent object in the context
|
26
|
+
# @return An ARel predicate node
|
27
|
+
# (Arel::Nodes::Equality, Arel::Nodes::Matches, etc)
|
28
|
+
def visit_Squeel_Nodes_Predicate(o, parent)
|
29
|
+
value = o.value
|
30
|
+
|
31
|
+
case value
|
32
|
+
when Nodes::KeyPath
|
33
|
+
value = can_visit?(value.endpoint) ? visit(value, parent) : contextualize(traverse(value, parent))[value.endpoint.to_s]
|
34
|
+
when ActiveRecord::Relation
|
35
|
+
value = visit(
|
36
|
+
value.select_values.empty? ? value.select(value.klass.arel_table[value.klass.primary_key]) : value,
|
37
|
+
parent
|
38
|
+
)
|
39
|
+
else
|
40
|
+
value = visit(value, parent) if can_visit?(value)
|
41
|
+
end
|
42
|
+
|
43
|
+
value = quote_for_node(o.expr, value)
|
44
|
+
|
45
|
+
attribute = case o.expr
|
46
|
+
when Nodes::Stub, Nodes::Function, Nodes::Literal, Nodes::Grouping
|
47
|
+
visit(o.expr, parent)
|
48
|
+
else
|
49
|
+
contextualize(parent)[o.expr]
|
50
|
+
end
|
51
|
+
|
52
|
+
if Array === value && [:in, :not_in].include?(o.method_name)
|
53
|
+
o.method_name == :in ? attribute_in_array(attribute, value) : attribute_not_in_array(attribute, value)
|
54
|
+
else
|
55
|
+
attribute.send(o.method_name, value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Determine whether to use IN or equality testing for a predicate,
|
60
|
+
# based on its value class, then return the appropriate predicate.
|
61
|
+
#
|
62
|
+
# @param attribute The ARel attribute (or function/operation) the
|
63
|
+
# predicate will be created for
|
64
|
+
# @param value The value to be compared against
|
65
|
+
# @return [Arel::Nodes::Node] An ARel predicate node
|
66
|
+
def arel_predicate_for(attribute, value, parent)
|
67
|
+
if ActiveRecord::Relation === value && value.select_values.empty?
|
68
|
+
value = visit(value.select(value.klass.arel_table[value.klass.primary_key]), parent)
|
69
|
+
else
|
70
|
+
value = can_visit?(value) ? visit(value, parent) : value
|
71
|
+
end
|
72
|
+
|
73
|
+
case value
|
74
|
+
when Array
|
75
|
+
attribute_in_array(attribute, value)
|
76
|
+
when Range, Arel::SelectManager
|
77
|
+
attribute.in(value)
|
78
|
+
else
|
79
|
+
attribute.eq(value)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def attribute_in_array(attribute, array)
|
84
|
+
if array.empty?
|
85
|
+
FALSE_SQL
|
86
|
+
elsif array.include? nil
|
87
|
+
array = array.compact
|
88
|
+
array.empty? ? attribute.eq(nil) : attribute.in(array).or(attribute.eq nil)
|
89
|
+
else
|
90
|
+
attribute.in array
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def attribute_not_in_array(attribute, array)
|
95
|
+
if array.empty?
|
96
|
+
TRUE_SQL
|
97
|
+
elsif array.include? nil
|
98
|
+
array = array.compact
|
99
|
+
array.empty? ? attribute.not_eq(nil) : attribute.not_in(array).and(attribute.not_eq nil)
|
100
|
+
else
|
101
|
+
attribute.not_in array
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Certain nodes require us to do the quoting before the ARel
|
106
|
+
# visitor gets a chance to try, because we want to avoid having our
|
107
|
+
# values quoted as a type of the last visited column. Otherwise, we
|
108
|
+
# can end up with annoyances like having "joe" quoted to 0, if the
|
109
|
+
# last visited column was of an integer type.
|
110
|
+
#
|
111
|
+
# @param node The node we (might) be quoting for
|
112
|
+
# @param v The value to (possibly) quote
|
113
|
+
def quote_for_node(node, v)
|
114
|
+
case node
|
115
|
+
when Nodes::Function, Nodes::Literal
|
116
|
+
quote(v)
|
117
|
+
when Nodes::Predicate
|
118
|
+
quote_for_node(node.expr, v)
|
119
|
+
else
|
120
|
+
v
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
require 'squeel/visitors/visitor'
|
2
|
+
require 'squeel/visitors/predicate_visitation'
|
2
3
|
|
3
4
|
module Squeel
|
4
5
|
module Visitors
|
5
6
|
class PredicateVisitor < Visitor
|
6
|
-
|
7
|
-
TRUE_SQL = Arel.sql('1=1').freeze
|
8
|
-
FALSE_SQL = Arel.sql('1=0').freeze
|
7
|
+
include PredicateVisitation
|
9
8
|
|
10
9
|
private
|
11
10
|
|
@@ -16,15 +15,7 @@ module Squeel
|
|
16
15
|
# @param parent The current parent object in the context
|
17
16
|
# @return [Array] An array of values for use in a where or having clause
|
18
17
|
def visit_Hash(o, parent)
|
19
|
-
predicates =
|
20
|
-
if implies_hash_context_shift?(v)
|
21
|
-
visit_with_hash_context_shift(k, v, parent)
|
22
|
-
else
|
23
|
-
visit_without_hash_context_shift(k, v, parent)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
predicates.flatten!
|
18
|
+
predicates = super
|
28
19
|
|
29
20
|
if predicates.size > 1
|
30
21
|
Arel::Nodes::Grouping.new(Arel::Nodes::And.new predicates)
|
@@ -33,222 +24,6 @@ module Squeel
|
|
33
24
|
end
|
34
25
|
end
|
35
26
|
|
36
|
-
# Visit ActiveRecord::Base objects. These should be converted to their
|
37
|
-
# id before being used in a comparison.
|
38
|
-
#
|
39
|
-
# @param [ActiveRecord::Base] o The AR::Base object to visit
|
40
|
-
# @param parent The current parent object in the context
|
41
|
-
# @return [Fixnum] The id of the object
|
42
|
-
def visit_ActiveRecord_Base(o, parent)
|
43
|
-
o.id
|
44
|
-
end
|
45
|
-
|
46
|
-
# Visit a KeyPath by traversing the path and then visiting the endpoint.
|
47
|
-
#
|
48
|
-
# @param [Nodes::KeyPath] o The KeyPath to visit
|
49
|
-
# @param parent The parent object in the context
|
50
|
-
# @return The visited endpoint, in the context of the KeyPath's path
|
51
|
-
def visit_Squeel_Nodes_KeyPath(o, parent)
|
52
|
-
parent = traverse(o, parent)
|
53
|
-
|
54
|
-
visit(o.endpoint, parent)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Visit a symbol. This will return an attribute named after the symbol against
|
58
|
-
# the current parent's contextualized table.
|
59
|
-
#
|
60
|
-
# @param [Symbol] o The symbol to visit
|
61
|
-
# @param parent The symbol's parent within the context
|
62
|
-
# @return [Arel::Attribute] An attribute on the contextualized parent table
|
63
|
-
def visit_Symbol(o, parent)
|
64
|
-
contextualize(parent)[o]
|
65
|
-
end
|
66
|
-
|
67
|
-
# Visit a Stub by converting it to an ARel attribute
|
68
|
-
#
|
69
|
-
# @param [Nodes::Stub] o The Stub to visit
|
70
|
-
# @param parent The parent object in the context
|
71
|
-
# @return [Arel::Attribute] An attribute of the parent table with the
|
72
|
-
# Stub's column
|
73
|
-
def visit_Squeel_Nodes_Stub(o, parent)
|
74
|
-
contextualize(parent)[o.to_s]
|
75
|
-
end
|
76
|
-
|
77
|
-
# Visit a Literal by converting it to an ARel SqlLiteral
|
78
|
-
#
|
79
|
-
# @param [Nodes::Literal] o The Literal to visit
|
80
|
-
# @param parent The parent object in the context (unused)
|
81
|
-
# @return [Arel::Nodes::SqlLiteral] An SqlLiteral
|
82
|
-
def visit_Squeel_Nodes_Literal(o, parent)
|
83
|
-
Arel.sql(o.expr)
|
84
|
-
end
|
85
|
-
|
86
|
-
# Visit a Squeel sifter by executing its corresponding constraint block
|
87
|
-
# in the parent's class, with its given arguments, then visiting the result.
|
88
|
-
#
|
89
|
-
# @param [Nodes::Sifter] o The Sifter to visit
|
90
|
-
# @param parent The parent object in the context
|
91
|
-
# @return The result of visiting the executed block's return value
|
92
|
-
def visit_Squeel_Nodes_Sifter(o, parent)
|
93
|
-
klass = classify(parent)
|
94
|
-
visit(klass.send(o.name, *o.args), parent)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Visit a Squeel predicate, converting it into an ARel predicate
|
98
|
-
#
|
99
|
-
# @param [Nodes::Predicate] o The predicate to visit
|
100
|
-
# @param parent The parent object in the context
|
101
|
-
# @return An ARel predicate node
|
102
|
-
# (Arel::Nodes::Equality, Arel::Nodes::Matches, etc)
|
103
|
-
def visit_Squeel_Nodes_Predicate(o, parent)
|
104
|
-
value = o.value
|
105
|
-
|
106
|
-
case value
|
107
|
-
when Nodes::KeyPath
|
108
|
-
value = can_visit?(value.endpoint) ? visit(value, parent) : contextualize(traverse(value, parent))[value.endpoint.to_s]
|
109
|
-
when ActiveRecord::Relation
|
110
|
-
value = visit(
|
111
|
-
value.select_values.empty? ? value.select(value.klass.arel_table[value.klass.primary_key]) : value,
|
112
|
-
parent
|
113
|
-
)
|
114
|
-
else
|
115
|
-
value = visit(value, parent) if can_visit?(value)
|
116
|
-
end
|
117
|
-
|
118
|
-
value = quote_for_node(o.expr, value)
|
119
|
-
|
120
|
-
attribute = case o.expr
|
121
|
-
when Nodes::Stub, Nodes::Function, Nodes::Literal, Nodes::Grouping
|
122
|
-
visit(o.expr, parent)
|
123
|
-
else
|
124
|
-
contextualize(parent)[o.expr]
|
125
|
-
end
|
126
|
-
|
127
|
-
if Array === value && [:in, :not_in].include?(o.method_name)
|
128
|
-
o.method_name == :in ? attribute_in_array(attribute, value) : attribute_not_in_array(attribute, value)
|
129
|
-
else
|
130
|
-
attribute.send(o.method_name, value)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Visit a Squeel function, returning an ARel NamedFunction node.
|
135
|
-
#
|
136
|
-
# @param [Nodes::Function] o The function node to visit
|
137
|
-
# @param parent The parent object in the context
|
138
|
-
# @return [Arel::Nodes::NamedFunction] A named function node. Function
|
139
|
-
# arguments are visited, if necessary, before being passed to the NamedFunction.
|
140
|
-
def visit_Squeel_Nodes_Function(o, parent)
|
141
|
-
args = o.args.map do |arg|
|
142
|
-
case arg
|
143
|
-
when Nodes::Function, Nodes::As, Nodes::Literal, Nodes::Grouping
|
144
|
-
visit(arg, parent)
|
145
|
-
when ActiveRecord::Relation
|
146
|
-
arg.arel.ast
|
147
|
-
when Nodes::KeyPath
|
148
|
-
can_visit?(arg.endpoint) ? visit(arg, parent) : contextualize(traverse(arg, parent))[arg.endpoint.to_s]
|
149
|
-
when Symbol, Nodes::Stub
|
150
|
-
Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_s])
|
151
|
-
else
|
152
|
-
quote arg
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
Arel::Nodes::NamedFunction.new(o.name, args)
|
157
|
-
end
|
158
|
-
|
159
|
-
# Visit an ActiveRecord Relation, returning an Arel::SelectManager
|
160
|
-
# @param [ActiveRecord::Relation] o The Relation to visit
|
161
|
-
# @param parent The parent object in the context
|
162
|
-
# @return [Arel::SelectManager] The ARel select manager that represents
|
163
|
-
# the relation's query
|
164
|
-
def visit_ActiveRecord_Relation(o, parent)
|
165
|
-
o.arel
|
166
|
-
end
|
167
|
-
|
168
|
-
# Visit a Squeel operation node, convering it to an ARel InfixOperation
|
169
|
-
# (or subclass, as appropriate)
|
170
|
-
#
|
171
|
-
# @param [Nodes::Operation] o The Operation node to visit
|
172
|
-
# @param parent The parent object in the context
|
173
|
-
# @return [Arel::Nodes::InfixOperation] The InfixOperation (or Addition,
|
174
|
-
# Multiplication, etc) node, with both operands visited, if needed.
|
175
|
-
def visit_Squeel_Nodes_Operation(o, parent)
|
176
|
-
args = o.args.map do |arg|
|
177
|
-
case arg
|
178
|
-
when Nodes::Function, Nodes::As, Nodes::Literal, Nodes::Grouping
|
179
|
-
visit(arg, parent)
|
180
|
-
when Nodes::KeyPath
|
181
|
-
can_visit?(arg.endpoint) ? visit(arg, parent) : contextualize(traverse(arg, parent))[arg.endpoint.to_s]
|
182
|
-
when Symbol, Nodes::Stub
|
183
|
-
Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_s])
|
184
|
-
else
|
185
|
-
quote arg
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
op = case o.operator
|
190
|
-
when :+
|
191
|
-
Arel::Nodes::Addition.new(args[0], args[1])
|
192
|
-
when :-
|
193
|
-
Arel::Nodes::Subtraction.new(args[0], args[1])
|
194
|
-
when :*
|
195
|
-
Arel::Nodes::Multiplication.new(args[0], args[1])
|
196
|
-
when :/
|
197
|
-
Arel::Nodes::Division.new(args[0], args[1])
|
198
|
-
else
|
199
|
-
Arel::Nodes::InfixOperation.new(o.operator, args[0], args[1])
|
200
|
-
end
|
201
|
-
|
202
|
-
op
|
203
|
-
end
|
204
|
-
|
205
|
-
# Visit a Squeel And node, returning an ARel Grouping containing an
|
206
|
-
# ARel And node.
|
207
|
-
#
|
208
|
-
# @param [Nodes::And] o The And node to visit
|
209
|
-
# @param parent The parent object in the context
|
210
|
-
# @return [Arel::Nodes::Grouping] A grouping node, containnig an ARel
|
211
|
-
# And node as its expression. All children will be visited before
|
212
|
-
# being passed to the And.
|
213
|
-
def visit_Squeel_Nodes_And(o, parent)
|
214
|
-
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(visit(o.children, parent)))
|
215
|
-
end
|
216
|
-
|
217
|
-
# Visit a Squeel Or node, returning an ARel Or node.
|
218
|
-
#
|
219
|
-
# @param [Nodes::Or] o The Or node to visit
|
220
|
-
# @param parent The parent object in the context
|
221
|
-
# @return [Arel::Nodes::Or] An ARel Or node, with left and right sides visited
|
222
|
-
def visit_Squeel_Nodes_Or(o, parent)
|
223
|
-
Arel::Nodes::Grouping.new(Arel::Nodes::Or.new(visit(o.left, parent), (visit(o.right, parent))))
|
224
|
-
end
|
225
|
-
|
226
|
-
# Visit a Squeel Not node, returning an ARel Not node.
|
227
|
-
#
|
228
|
-
# @param [Nodes::Not] o The Not node to visit
|
229
|
-
# @param parent The parent object in the context
|
230
|
-
# @return [Arel::Nodes::Not] An ARel Not node, with expression visited
|
231
|
-
def visit_Squeel_Nodes_Not(o, parent)
|
232
|
-
Arel::Nodes::Not.new(visit(o.expr, parent))
|
233
|
-
end
|
234
|
-
|
235
|
-
# Visit a Squeel Grouping node, returning an ARel Grouping node.
|
236
|
-
#
|
237
|
-
# @param [Nodes::Grouping] o The Grouping node to visit
|
238
|
-
# @param parent The parent object in the context
|
239
|
-
# @return [Arel::Nodes::Grouping] An ARel Grouping node, with expression visited
|
240
|
-
def visit_Squeel_Nodes_Grouping(o, parent)
|
241
|
-
Arel::Nodes::Grouping.new(visit(o.expr, parent))
|
242
|
-
end
|
243
|
-
|
244
|
-
# Visit a Squeel As node, resulting in am ARel As node.
|
245
|
-
#
|
246
|
-
# @param [Nodes::As] The As node to visit
|
247
|
-
# @param parent The parent object in the context
|
248
|
-
# @return [Arel::Nodes::As] The resulting as node.
|
249
|
-
def visit_Squeel_Nodes_As(o, parent)
|
250
|
-
visit(o.left, parent).as(o.right)
|
251
|
-
end
|
252
27
|
|
253
28
|
# @return [Boolean] Whether the given value implies a context change
|
254
29
|
# @param v The value to consider
|
@@ -263,39 +38,6 @@ module Squeel
|
|
263
38
|
end
|
264
39
|
end
|
265
40
|
|
266
|
-
# Change context (by setting the new parent to the result of a #find or
|
267
|
-
# #traverse on the key), then accept the given value.
|
268
|
-
#
|
269
|
-
# @param k The hash key
|
270
|
-
# @param v The hash value
|
271
|
-
# @param parent The current parent object in the context
|
272
|
-
# @return The visited value
|
273
|
-
def visit_with_hash_context_shift(k, v, parent)
|
274
|
-
@hash_context_depth += 1
|
275
|
-
|
276
|
-
parent = case k
|
277
|
-
when Nodes::KeyPath
|
278
|
-
traverse(k, parent, true)
|
279
|
-
else
|
280
|
-
find(k, parent)
|
281
|
-
end
|
282
|
-
|
283
|
-
case v
|
284
|
-
when Hash, Nodes::KeyPath, Nodes::Predicate, Nodes::Unary, Nodes::Binary, Nodes::Nary, Nodes::Sifter
|
285
|
-
visit(v, parent || k)
|
286
|
-
when Array
|
287
|
-
v.map {|val| visit(val, parent || k)}
|
288
|
-
else
|
289
|
-
raise ArgumentError, <<-END
|
290
|
-
Hashes, Predicates, and arrays of visitables as values imply that their
|
291
|
-
corresponding keys are a parent. This didn't work out so well in the case
|
292
|
-
of key = #{k} and value = #{v}"
|
293
|
-
END
|
294
|
-
end
|
295
|
-
ensure
|
296
|
-
@hash_context_depth -= 1
|
297
|
-
end
|
298
|
-
|
299
41
|
# Create a predicate for a given key/value pair. If the value is
|
300
42
|
# a Symbol, Stub, or KeyPath, it's converted to a table.column for
|
301
43
|
# the predicate value.
|
@@ -331,71 +73,6 @@ module Squeel
|
|
331
73
|
end
|
332
74
|
end
|
333
75
|
|
334
|
-
# Determine whether to use IN or equality testing for a predicate,
|
335
|
-
# based on its value class, then return the appropriate predicate.
|
336
|
-
#
|
337
|
-
# @param attribute The ARel attribute (or function/operation) the
|
338
|
-
# predicate will be created for
|
339
|
-
# @param value The value to be compared against
|
340
|
-
# @return [Arel::Nodes::Node] An ARel predicate node
|
341
|
-
def arel_predicate_for(attribute, value, parent)
|
342
|
-
if ActiveRecord::Relation === value && value.select_values.empty?
|
343
|
-
value = visit(value.select(value.klass.arel_table[value.klass.primary_key]), parent)
|
344
|
-
else
|
345
|
-
value = can_visit?(value) ? visit(value, parent) : value
|
346
|
-
end
|
347
|
-
|
348
|
-
case value
|
349
|
-
when Array
|
350
|
-
attribute_in_array(attribute, value)
|
351
|
-
when Range, Arel::SelectManager
|
352
|
-
attribute.in(value)
|
353
|
-
else
|
354
|
-
attribute.eq(value)
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
def attribute_in_array(attribute, array)
|
359
|
-
if array.empty?
|
360
|
-
FALSE_SQL
|
361
|
-
elsif array.include? nil
|
362
|
-
array = array.compact
|
363
|
-
array.empty? ? attribute.eq(nil) : attribute.in(array).or(attribute.eq nil)
|
364
|
-
else
|
365
|
-
attribute.in array
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
def attribute_not_in_array(attribute, array)
|
370
|
-
if array.empty?
|
371
|
-
TRUE_SQL
|
372
|
-
elsif array.include? nil
|
373
|
-
array = array.compact
|
374
|
-
array.empty? ? attribute.not_eq(nil) : attribute.not_in(array).and(attribute.not_eq nil)
|
375
|
-
else
|
376
|
-
attribute.not_in array
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
# Certain nodes require us to do the quoting before the ARel
|
381
|
-
# visitor gets a chance to try, because we want to avoid having our
|
382
|
-
# values quoted as a type of the last visited column. Otherwise, we
|
383
|
-
# can end up with annoyances like having "joe" quoted to 0, if the
|
384
|
-
# last visited column was of an integer type.
|
385
|
-
#
|
386
|
-
# @param node The node we (might) be quoting for
|
387
|
-
# @param v The value to (possibly) quote
|
388
|
-
def quote_for_node(node, v)
|
389
|
-
case node
|
390
|
-
when Nodes::Function, Nodes::Literal
|
391
|
-
quote(v)
|
392
|
-
when Nodes::Predicate
|
393
|
-
quote_for_node(node.expr, v)
|
394
|
-
else
|
395
|
-
v
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
76
|
end
|
400
77
|
end
|
401
78
|
end
|