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
@@ -1,214 +0,0 @@
|
|
1
|
-
require 'squeel/visitors/visitor'
|
2
|
-
|
3
|
-
module Squeel
|
4
|
-
module Visitors
|
5
|
-
# A visitor that tries to convert visited nodes into Arel::Attributes
|
6
|
-
# or other nodes that can be used for grouping, ordering, and the like.
|
7
|
-
class AttributeVisitor < Visitor
|
8
|
-
|
9
|
-
private
|
10
|
-
|
11
|
-
# Visit a Hash. This entails iterating through each key and value and
|
12
|
-
# visiting each value in turn.
|
13
|
-
#
|
14
|
-
# @param [Hash] o The Hash to visit
|
15
|
-
# @param parent The current parent object in the context
|
16
|
-
# @return [Array] An array of values for use in an ordering, grouping, etc.
|
17
|
-
def visit_Hash(o, parent)
|
18
|
-
o.map do |k, v|
|
19
|
-
if implies_hash_context_shift?(v)
|
20
|
-
visit_with_hash_context_shift(k, v, parent)
|
21
|
-
else
|
22
|
-
visit_without_hash_context_shift(k, v, parent)
|
23
|
-
end
|
24
|
-
end.flatten
|
25
|
-
end
|
26
|
-
|
27
|
-
# Visit a symbol. This will return an attribute named after the symbol against
|
28
|
-
# the current parent's contextualized table.
|
29
|
-
#
|
30
|
-
# @param [Symbol] o The symbol to visit
|
31
|
-
# @param parent The symbol's parent within the context
|
32
|
-
# @return [Arel::Attribute] An attribute on the contextualized parent table
|
33
|
-
def visit_Symbol(o, parent)
|
34
|
-
contextualize(parent)[o]
|
35
|
-
end
|
36
|
-
|
37
|
-
# Visit a stub. This will return an attribute named after the stub against
|
38
|
-
# the current parent's contextualized table.
|
39
|
-
#
|
40
|
-
# @param [Nodes::Stub] o The stub to visit
|
41
|
-
# @param parent The stub's parent within the context
|
42
|
-
# @return [Arel::Attribute] An attribute on the contextualized parent table
|
43
|
-
def visit_Squeel_Nodes_Stub(o, parent)
|
44
|
-
contextualize(parent)[o.to_s]
|
45
|
-
end
|
46
|
-
|
47
|
-
# Visit a Literal by converting it to an ARel SqlLiteral
|
48
|
-
#
|
49
|
-
# @param [Nodes::Literal] o The Literal to visit
|
50
|
-
# @param parent The parent object in the context (unused)
|
51
|
-
# @return [Arel::Nodes::SqlLiteral] An SqlLiteral
|
52
|
-
def visit_Squeel_Nodes_Literal(o, parent)
|
53
|
-
Arel.sql(o.expr)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Visit a keypath. This will traverse the keypath's "path", setting a new
|
57
|
-
# parent as though the keypath's endpoint was in a deeply-nested hash,
|
58
|
-
# then visit the endpoint with the new parent.
|
59
|
-
#
|
60
|
-
# @param [Nodes::KeyPath] o The keypath to visit
|
61
|
-
# @param parent The keypath's parent within the context
|
62
|
-
# @return The visited endpoint, with the parent from the KeyPath's path.
|
63
|
-
def visit_Squeel_Nodes_KeyPath(o, parent)
|
64
|
-
parent = traverse(o, parent)
|
65
|
-
|
66
|
-
visit(o.endpoint, parent)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Visit an Order node.
|
70
|
-
#
|
71
|
-
# @param [Nodes::Order] o The order node to visit
|
72
|
-
# @param parent The node's parent within the context
|
73
|
-
# @return [Arel::Nodes::Ordering] An ascending or desending ordering
|
74
|
-
def visit_Squeel_Nodes_Order(o, parent)
|
75
|
-
visit(o.expr, parent).send(o.descending? ? :desc : :asc)
|
76
|
-
end
|
77
|
-
|
78
|
-
# Visit a Function node. Each function argument will be visiteded or
|
79
|
-
# contextualized if appropriate. Keep in mind that this occurs with
|
80
|
-
# the current parent within the context.
|
81
|
-
#
|
82
|
-
# @example A function as the endpoint of a keypath
|
83
|
-
# Person.joins{children}.order{children.coalesce(name, '<no name>')}
|
84
|
-
# # => SELECT "people".* FROM "people"
|
85
|
-
# INNER JOIN "people" "children_people"
|
86
|
-
# ON "children_people"."parent_id" = "people"."id"
|
87
|
-
# ORDER BY coalesce("children_people"."name", '<no name>')
|
88
|
-
#
|
89
|
-
# @param [Nodes::Function] o The function node to visit
|
90
|
-
# @param parent The node's parent within the context
|
91
|
-
def visit_Squeel_Nodes_Function(o, parent)
|
92
|
-
args = o.args.map do |arg|
|
93
|
-
case arg
|
94
|
-
when Nodes::Function, Nodes::KeyPath, Nodes::As, Nodes::Literal, Nodes::Grouping
|
95
|
-
visit(arg, parent)
|
96
|
-
when Symbol, Nodes::Stub
|
97
|
-
Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_s])
|
98
|
-
else
|
99
|
-
quote arg
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
Arel::Nodes::NamedFunction.new(o.name, args)
|
104
|
-
end
|
105
|
-
|
106
|
-
# Visit an Operation node. Each operand will be accepted or
|
107
|
-
# contextualized if appropriate. Keep in mind that this occurs with
|
108
|
-
# the current parent within the context.
|
109
|
-
#
|
110
|
-
# @param [Nodes::Operation] o The operation node to visit
|
111
|
-
# @param parent The node's parent within the context
|
112
|
-
def visit_Squeel_Nodes_Operation(o, parent)
|
113
|
-
args = o.args.map do |arg|
|
114
|
-
case arg
|
115
|
-
when Nodes::Function, Nodes::KeyPath, Nodes::As, Nodes::Literal, Nodes::Grouping
|
116
|
-
visit(arg, parent)
|
117
|
-
when Symbol, Nodes::Stub
|
118
|
-
Arel.sql(arel_visitor.accept contextualize(parent)[arg.to_s])
|
119
|
-
else
|
120
|
-
quote arg
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
op = case o.operator
|
125
|
-
when :+
|
126
|
-
Arel::Nodes::Addition.new(args[0], args[1])
|
127
|
-
when :-
|
128
|
-
Arel::Nodes::Subtraction.new(args[0], args[1])
|
129
|
-
when :*
|
130
|
-
Arel::Nodes::Multiplication.new(args[0], args[1])
|
131
|
-
when :/
|
132
|
-
Arel::Nodes::Division.new(args[0], args[1])
|
133
|
-
else
|
134
|
-
Arel.sql("#{arel_visitor.accept(args[0])} #{o.operator} #{arel_visitor.accept(args[1])}")
|
135
|
-
end
|
136
|
-
|
137
|
-
op
|
138
|
-
end
|
139
|
-
|
140
|
-
# Visit a Squeel Grouping node, resulting in am ARel Grouping node.
|
141
|
-
#
|
142
|
-
# @param [Nodes::Grouping] The Grouping node to visit
|
143
|
-
# @param parent The parent object in the context
|
144
|
-
# @return [Arel::Nodes::Grouping] The resulting grouping node.
|
145
|
-
def visit_Squeel_Nodes_Grouping(o, parent)
|
146
|
-
Arel::Nodes::Grouping.new(visit(o.expr, parent))
|
147
|
-
end
|
148
|
-
|
149
|
-
# Visit a Squeel As node, resulting in am ARel As node.
|
150
|
-
#
|
151
|
-
# @param [Nodes::As] The As node to visit
|
152
|
-
# @param parent The parent object in the context
|
153
|
-
# @return [Arel::Nodes::As] The resulting as node.
|
154
|
-
def visit_Squeel_Nodes_As(o, parent)
|
155
|
-
visit(o.left, parent).as(o.right)
|
156
|
-
end
|
157
|
-
|
158
|
-
# Visit an ActiveRecord Relation, returning an Arel::SelectManager
|
159
|
-
# @param [ActiveRecord::Relation] o The Relation to visit
|
160
|
-
# @param parent The parent object in the context
|
161
|
-
# @return [Arel::SelectManager] The ARel select manager that represents
|
162
|
-
# the relation's query
|
163
|
-
def visit_ActiveRecord_Relation(o, parent)
|
164
|
-
o.arel
|
165
|
-
end
|
166
|
-
|
167
|
-
# @return [Boolean] Whether the given value implies a context change
|
168
|
-
# @param v The value to consider
|
169
|
-
def implies_hash_context_shift?(v)
|
170
|
-
can_visit?(v)
|
171
|
-
end
|
172
|
-
|
173
|
-
# Change context (by setting the new parent to the result of a #find or
|
174
|
-
# #traverse on the key), then accept the given value.
|
175
|
-
#
|
176
|
-
# @param k The hash key
|
177
|
-
# @param v The hash value
|
178
|
-
# @param parent The current parent object in the context
|
179
|
-
# @return The visited value
|
180
|
-
def visit_with_hash_context_shift(k, v, parent)
|
181
|
-
@hash_context_depth += 1
|
182
|
-
|
183
|
-
parent = case k
|
184
|
-
when Nodes::KeyPath
|
185
|
-
traverse(k, parent, true)
|
186
|
-
else
|
187
|
-
find(k, parent)
|
188
|
-
end
|
189
|
-
|
190
|
-
if Array === v
|
191
|
-
v.map {|val| visit(val, parent || k)}
|
192
|
-
else
|
193
|
-
can_visit?(v) ? visit(v, parent || k) : v
|
194
|
-
end
|
195
|
-
ensure
|
196
|
-
@hash_context_depth -= 1
|
197
|
-
end
|
198
|
-
|
199
|
-
# If there is no context change, we'll just return the value unchanged,
|
200
|
-
# currently. Is this really the right behavior? I don't think so, but
|
201
|
-
# it works in this case.
|
202
|
-
#
|
203
|
-
# @param k The hash key
|
204
|
-
# @param v The hash value
|
205
|
-
# @param parent The current parent object in the context
|
206
|
-
# @return The same value we just received. Yeah, this method's pretty pointless,
|
207
|
-
# for now, and only here for consistency's sake.
|
208
|
-
def visit_without_hash_context_shift(k, v, parent)
|
209
|
-
v
|
210
|
-
end
|
211
|
-
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|