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.
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