squeel 1.0.9 → 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +10 -0
  2. data/lib/squeel.rb +5 -0
  3. data/lib/squeel/adapters/active_record/3.0/association_preload_extensions.rb +2 -2
  4. data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +98 -46
  5. data/lib/squeel/adapters/active_record/3.1/preloader_extensions.rb +2 -2
  6. data/lib/squeel/adapters/active_record/3.1/relation_extensions.rb +71 -85
  7. data/lib/squeel/adapters/active_record/base_extensions.rb +0 -19
  8. data/lib/squeel/adapters/active_record/relation_extensions.rb +6 -9
  9. data/lib/squeel/nodes/binary.rb +4 -0
  10. data/lib/squeel/nodes/function.rb +10 -0
  11. data/lib/squeel/nodes/nary.rb +1 -1
  12. data/lib/squeel/nodes/order.rb +11 -1
  13. data/lib/squeel/nodes/predicate.rb +2 -2
  14. data/lib/squeel/nodes/stub.rb +2 -0
  15. data/lib/squeel/nodes/unary.rb +4 -0
  16. data/lib/squeel/version.rb +1 -1
  17. data/lib/squeel/visitors.rb +10 -3
  18. data/lib/squeel/visitors/from_visitor.rb +6 -0
  19. data/lib/squeel/visitors/group_visitor.rb +6 -0
  20. data/lib/squeel/visitors/having_visitor.rb +9 -0
  21. data/lib/squeel/visitors/order_visitor.rb +20 -0
  22. data/lib/squeel/visitors/predicate_visitation.rb +126 -0
  23. data/lib/squeel/visitors/predicate_visitor.rb +3 -326
  24. data/lib/squeel/visitors/{symbol_visitor.rb → preload_visitor.rb} +4 -4
  25. data/lib/squeel/visitors/select_visitor.rb +7 -0
  26. data/lib/squeel/visitors/visitor.rb +244 -12
  27. data/lib/squeel/visitors/where_visitor.rb +8 -0
  28. data/spec/helpers/squeel_helper.rb +14 -1
  29. data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +65 -49
  30. data/spec/squeel/nodes/as_spec.rb +20 -0
  31. data/spec/squeel/nodes/function_spec.rb +6 -0
  32. data/spec/squeel/nodes/grouping_spec.rb +6 -0
  33. data/spec/squeel/nodes/key_path_spec.rb +3 -3
  34. data/spec/squeel/nodes/operation_spec.rb +6 -0
  35. data/spec/squeel/nodes/order_spec.rb +6 -1
  36. data/spec/squeel/nodes/predicate_operators_spec.rb +1 -1
  37. data/spec/squeel/nodes/predicate_spec.rb +14 -1
  38. data/spec/squeel/nodes/sifter_spec.rb +2 -1
  39. data/spec/squeel/nodes/stub_spec.rb +4 -0
  40. data/spec/squeel/visitors/order_visitor_spec.rb +36 -0
  41. data/spec/squeel/visitors/{symbol_visitor_spec.rb → preload_visitor_spec.rb} +4 -3
  42. data/spec/squeel/visitors/select_visitor_spec.rb +26 -0
  43. data/spec/squeel/visitors/{attribute_visitor_spec.rb → visitor_spec.rb} +23 -37
  44. data/spec/support/models.rb +4 -0
  45. metadata +22 -10
  46. 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