squeel 0.7.0 → 0.7.1
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/lib/squeel/adapters/active_record/3.0/context.rb +1 -1
- data/lib/squeel/adapters/active_record/context.rb +1 -1
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors/attribute_visitor.rb +41 -41
- data/lib/squeel/visitors/predicate_visitor.rb +98 -0
- data/spec/squeel/adapters/active_record/context_spec.rb +1 -0
- data/spec/squeel/adapters/active_record/relation_spec.rb +6 -0
- data/spec/support/schema.rb +2 -0
- metadata +2 -2
@@ -49,7 +49,7 @@ module Squeel
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def get_table(object)
|
52
|
-
if [Symbol, Nodes::Stub].include?(object.class)
|
52
|
+
if [Symbol, String, Nodes::Stub].include?(object.class)
|
53
53
|
Arel::Table.new(object.to_sym, :engine => @engine)
|
54
54
|
elsif Nodes::Join === object
|
55
55
|
object._klass ? object._klass.arel_table : Arel::Table.new(object._name, :engine => @engine)
|
@@ -49,7 +49,7 @@ module Squeel
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def get_table(object)
|
52
|
-
if [Symbol, Nodes::Stub].include?(object.class)
|
52
|
+
if [Symbol, String, Nodes::Stub].include?(object.class)
|
53
53
|
Arel::Table.new(object.to_sym, :engine => @engine)
|
54
54
|
elsif Nodes::Join === object
|
55
55
|
object._klass ? object._klass.arel_table : Arel::Table.new(object._name, :engine => @engine)
|
data/lib/squeel/version.rb
CHANGED
@@ -24,47 +24,6 @@ module Squeel
|
|
24
24
|
end.flatten
|
25
25
|
end
|
26
26
|
|
27
|
-
# @return [Boolean] Whether the given value implies a context change
|
28
|
-
# @param v The value to consider
|
29
|
-
def implies_context_change?(v)
|
30
|
-
can_accept?(v)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Change context (by setting the new parent to the result of a #find or
|
34
|
-
# #traverse on the key), then accept the given value.
|
35
|
-
#
|
36
|
-
# @param k The hash key
|
37
|
-
# @param v The hash value
|
38
|
-
# @param parent The current parent object in the context
|
39
|
-
# @return The visited value
|
40
|
-
def visit_with_context_change(k, v, parent)
|
41
|
-
parent = case k
|
42
|
-
when Nodes::KeyPath
|
43
|
-
traverse(k, parent, true)
|
44
|
-
else
|
45
|
-
find(k, parent)
|
46
|
-
end
|
47
|
-
|
48
|
-
if Array === v
|
49
|
-
v.map {|val| accept(val, parent || k)}
|
50
|
-
else
|
51
|
-
can_accept?(v) ? accept(v, parent || k) : v
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# If there is no context change, we'll just return the value unchanged,
|
56
|
-
# currently. Is this really the right behavior? I don't think so, but
|
57
|
-
# it works in this case.
|
58
|
-
#
|
59
|
-
# @param k The hash key
|
60
|
-
# @param v The hash value
|
61
|
-
# @param parent The current parent object in the context
|
62
|
-
# @return The same value we just received. Yeah, this method's pretty pointless,
|
63
|
-
# for now, and only here for consistency's sake.
|
64
|
-
def visit_without_context_change(k, v, parent)
|
65
|
-
v
|
66
|
-
end
|
67
|
-
|
68
27
|
# Visit elements of an array that it's possible to visit -- leave other
|
69
28
|
# elements untouched.
|
70
29
|
#
|
@@ -177,6 +136,47 @@ module Squeel
|
|
177
136
|
o.alias ? op.as(o.alias) : op
|
178
137
|
end
|
179
138
|
|
139
|
+
# @return [Boolean] Whether the given value implies a context change
|
140
|
+
# @param v The value to consider
|
141
|
+
def implies_context_change?(v)
|
142
|
+
can_accept?(v)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Change context (by setting the new parent to the result of a #find or
|
146
|
+
# #traverse on the key), then accept the given value.
|
147
|
+
#
|
148
|
+
# @param k The hash key
|
149
|
+
# @param v The hash value
|
150
|
+
# @param parent The current parent object in the context
|
151
|
+
# @return The visited value
|
152
|
+
def visit_with_context_change(k, v, parent)
|
153
|
+
parent = case k
|
154
|
+
when Nodes::KeyPath
|
155
|
+
traverse(k, parent, true)
|
156
|
+
else
|
157
|
+
find(k, parent)
|
158
|
+
end
|
159
|
+
|
160
|
+
if Array === v
|
161
|
+
v.map {|val| accept(val, parent || k)}
|
162
|
+
else
|
163
|
+
can_accept?(v) ? accept(v, parent || k) : v
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
# If there is no context change, we'll just return the value unchanged,
|
168
|
+
# currently. Is this really the right behavior? I don't think so, but
|
169
|
+
# it works in this case.
|
170
|
+
#
|
171
|
+
# @param k The hash key
|
172
|
+
# @param v The hash value
|
173
|
+
# @param parent The current parent object in the context
|
174
|
+
# @return The same value we just received. Yeah, this method's pretty pointless,
|
175
|
+
# for now, and only here for consistency's sake.
|
176
|
+
def visit_without_context_change(k, v, parent)
|
177
|
+
v
|
178
|
+
end
|
179
|
+
|
180
180
|
end
|
181
181
|
end
|
182
182
|
end
|
@@ -6,6 +6,12 @@ module Squeel
|
|
6
6
|
|
7
7
|
private
|
8
8
|
|
9
|
+
# Visit a Hash. This entails iterating through each key and value and
|
10
|
+
# visiting each value in turn.
|
11
|
+
#
|
12
|
+
# @param [Hash] o The Hash to visit
|
13
|
+
# @param parent The current parent object in the context
|
14
|
+
# @return [Array] An array of values for use in a where or having clause
|
9
15
|
def visit_Hash(o, parent)
|
10
16
|
predicates = o.map do |k, v|
|
11
17
|
if implies_context_change?(v)
|
@@ -24,24 +30,53 @@ module Squeel
|
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
33
|
+
# Visit an array, which involves accepting any values we know how to
|
34
|
+
# accept, and skipping the rest.
|
35
|
+
#
|
36
|
+
# @param [Array] o The Array to visit
|
37
|
+
# @param parent The current parent object in the context
|
38
|
+
# @return [Array] The visited array
|
27
39
|
def visit_Array(o, parent)
|
28
40
|
o.map { |v| can_accept?(v) ? accept(v, parent) : v }.flatten
|
29
41
|
end
|
30
42
|
|
43
|
+
# Visit ActiveRecord::Base objects. These should be converted to their
|
44
|
+
# id before being used in a comparison.
|
45
|
+
#
|
46
|
+
# @param [ActiveRecord::Base] o The AR::Base object to visit
|
47
|
+
# @param parent The current parent object in the context
|
48
|
+
# @return [Fixnum] The id of the object
|
31
49
|
def visit_ActiveRecord_Base(o, parent)
|
32
50
|
o.id
|
33
51
|
end
|
34
52
|
|
53
|
+
# Visit a KeyPath by traversing the path and then visiting the endpoint.
|
54
|
+
#
|
55
|
+
# @param [Nodes::KeyPath] o The KeyPath to visit
|
56
|
+
# @param parent The parent object in the context
|
57
|
+
# @return The visited endpoint, in the context of the KeyPath's path
|
35
58
|
def visit_Squeel_Nodes_KeyPath(o, parent)
|
36
59
|
parent = traverse(o, parent)
|
37
60
|
|
38
61
|
accept(o.endpoint, parent)
|
39
62
|
end
|
40
63
|
|
64
|
+
# Visit a Stub by converting it to an ARel attribute
|
65
|
+
#
|
66
|
+
# @param [Nodes::Stub] o The Stub to visit
|
67
|
+
# @param parent The parent object in the context
|
68
|
+
# @return [Arel::Attribute] An attribute of the parent table with the
|
69
|
+
# Stub's column
|
41
70
|
def visit_Squeel_Nodes_Stub(o, parent)
|
42
71
|
contextualize(parent)[o.symbol]
|
43
72
|
end
|
44
73
|
|
74
|
+
# Visit a Squeel predicate, converting it into an ARel predicate
|
75
|
+
#
|
76
|
+
# @param [Nodes::Predicate] o The predicate to visit
|
77
|
+
# @param parent The parent object in the context
|
78
|
+
# @return An ARel predicate node
|
79
|
+
# (Arel::Nodes::Equality, Arel::Nodes::Matches, etc)
|
45
80
|
def visit_Squeel_Nodes_Predicate(o, parent)
|
46
81
|
value = o.value
|
47
82
|
if Nodes::KeyPath === value
|
@@ -60,6 +95,12 @@ module Squeel
|
|
60
95
|
end
|
61
96
|
end
|
62
97
|
|
98
|
+
# Visit a Squeel function, returning an ARel NamedFunction node.
|
99
|
+
#
|
100
|
+
# @param [Nodes::Function] o The function node to visit
|
101
|
+
# @param parent The parent object in the context
|
102
|
+
# @return [Arel::Nodes::NamedFunction] A named function node. Function
|
103
|
+
# arguments are visited, if necessary, before being passed to the NamedFunction.
|
63
104
|
def visit_Squeel_Nodes_Function(o, parent)
|
64
105
|
args = o.args.map do |arg|
|
65
106
|
case arg
|
@@ -76,10 +117,22 @@ module Squeel
|
|
76
117
|
Arel::Nodes::NamedFunction.new(o.name, args, o.alias)
|
77
118
|
end
|
78
119
|
|
120
|
+
# Visit an ActiveRecord Relation, returning an Arel::SelectManager
|
121
|
+
# @param [ActiveRecord::Relation] o The Relation to visit
|
122
|
+
# @param parent The parent object in the context
|
123
|
+
# @return [Arel::SelectManager] The ARel select manager that represents
|
124
|
+
# the relation's query
|
79
125
|
def visit_ActiveRecord_Relation(o, parent)
|
80
126
|
o.arel
|
81
127
|
end
|
82
128
|
|
129
|
+
# Visit a Squeel operation node, convering it to an ARel InfixOperation
|
130
|
+
# (or subclass, as appropriate)
|
131
|
+
#
|
132
|
+
# @param [Nodes::Operation] o The Operation node to visit
|
133
|
+
# @param parent The parent object in the context
|
134
|
+
# @return [Arel::Nodes::InfixOperation] The InfixOperation (or Addition,
|
135
|
+
# Multiplication, etc) node, with both operands visited, if needed.
|
83
136
|
def visit_Squeel_Nodes_Operation(o, parent)
|
84
137
|
args = o.args.map do |arg|
|
85
138
|
case arg
|
@@ -109,10 +162,23 @@ module Squeel
|
|
109
162
|
o.alias ? op.as(o.alias) : op
|
110
163
|
end
|
111
164
|
|
165
|
+
# Visit a Squeel And node, returning an ARel Grouping containing an
|
166
|
+
# ARel And node.
|
167
|
+
#
|
168
|
+
# @param [Nodes::And] The And node to visit
|
169
|
+
# @param parent The parent object in the context
|
170
|
+
# @return [Arel::Nodes::Grouping] A grouping node, containnig an ARel
|
171
|
+
# And node as its expression. All children will be visited before
|
172
|
+
# being passed to the And.
|
112
173
|
def visit_Squeel_Nodes_And(o, parent)
|
113
174
|
Arel::Nodes::Grouping.new(Arel::Nodes::And.new(accept(o.children, parent)))
|
114
175
|
end
|
115
176
|
|
177
|
+
# Visit a Squeel Or node, returning an ARel Or node.
|
178
|
+
#
|
179
|
+
# @param [Nodes::Or] The Or node to visit
|
180
|
+
# @param parent The parent object in the context
|
181
|
+
# @return [Arel::Nodes::Or] An ARel Or node, with left and ride sides visited
|
116
182
|
def visit_Squeel_Nodes_Or(o, parent)
|
117
183
|
accept(o.left, parent).or(accept(o.right, parent))
|
118
184
|
end
|
@@ -121,6 +187,8 @@ module Squeel
|
|
121
187
|
accept(o.expr, parent).not
|
122
188
|
end
|
123
189
|
|
190
|
+
# @return [Boolean] Whether the given value implies a context change
|
191
|
+
# @param v The value to consider
|
124
192
|
def implies_context_change?(v)
|
125
193
|
case v
|
126
194
|
when Hash, Nodes::Predicate, Nodes::Unary, Nodes::Binary, Nodes::Nary
|
@@ -132,6 +200,13 @@ module Squeel
|
|
132
200
|
end
|
133
201
|
end
|
134
202
|
|
203
|
+
# Change context (by setting the new parent to the result of a #find or
|
204
|
+
# #traverse on the key), then accept the given value.
|
205
|
+
#
|
206
|
+
# @param k The hash key
|
207
|
+
# @param v The hash value
|
208
|
+
# @param parent The current parent object in the context
|
209
|
+
# @return The visited value
|
135
210
|
def visit_with_context_change(k, v, parent)
|
136
211
|
parent = case k
|
137
212
|
when Nodes::KeyPath
|
@@ -154,6 +229,14 @@ module Squeel
|
|
154
229
|
end
|
155
230
|
end
|
156
231
|
|
232
|
+
# Create a predicate for a given key/value pair. If the value is
|
233
|
+
# a Symbol, Stub, or KeyPath, it's converted to a table.column for
|
234
|
+
# the predicate value.
|
235
|
+
#
|
236
|
+
# @param k The hash key
|
237
|
+
# @param v The hash value
|
238
|
+
# @param parent The current parent object in the context
|
239
|
+
# @return An ARel predicate
|
157
240
|
def visit_without_context_change(k, v, parent)
|
158
241
|
case v
|
159
242
|
when Nodes::Stub, Symbol
|
@@ -175,6 +258,13 @@ module Squeel
|
|
175
258
|
end
|
176
259
|
end
|
177
260
|
|
261
|
+
# Determine whether to use IN or equality testing for a predicate,
|
262
|
+
# based on its value class, then return the appropriate predicate.
|
263
|
+
#
|
264
|
+
# @param attribute The ARel attribute (or function/operation) the
|
265
|
+
# predicate will be created for
|
266
|
+
# @param value The value to be compared against
|
267
|
+
# @return [Arel::Nodes::Node] An ARel predicate node
|
178
268
|
def arel_predicate_for(attribute, value, parent)
|
179
269
|
value = can_accept?(value) ? accept(value, parent) : value
|
180
270
|
if [Array, Range, Arel::SelectManager].include?(value.class)
|
@@ -184,6 +274,14 @@ module Squeel
|
|
184
274
|
end
|
185
275
|
end
|
186
276
|
|
277
|
+
# Function nodes require us to do the quoting before the ARel
|
278
|
+
# visitor gets a chance to try, because we want to avoid having our
|
279
|
+
# values quoted as a type of the last visited column. Otherwise, we
|
280
|
+
# can end up with annoyances like having "joe" quoted to 0, if the
|
281
|
+
# last visited column was of an integer type.
|
282
|
+
#
|
283
|
+
# @param node The node we (might) be quoting for
|
284
|
+
# @param v The value to (possibly) quote
|
187
285
|
def quote_for_node(node, v)
|
188
286
|
case node
|
189
287
|
when Nodes::Function
|
@@ -589,6 +589,12 @@ module Squeel
|
|
589
589
|
sql.should match /"articles"."title" = 'Hello world!'/
|
590
590
|
end
|
591
591
|
|
592
|
+
it 'does not break hm:t with conditions' do
|
593
|
+
relation = Person.first.condition_article_comments
|
594
|
+
sql = relation.to_sql
|
595
|
+
sql.should match /"articles"."title" = 'Condition'/
|
596
|
+
end
|
597
|
+
|
592
598
|
end
|
593
599
|
|
594
600
|
describe '#to_a' do
|
data/spec/support/schema.rb
CHANGED
@@ -9,7 +9,9 @@ class Person < ActiveRecord::Base
|
|
9
9
|
belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id
|
10
10
|
has_many :children, :class_name => 'Person', :foreign_key => :parent_id
|
11
11
|
has_many :articles
|
12
|
+
has_many :articles_with_condition, :class_name => 'Article', :conditions => {:title => 'Condition'}
|
12
13
|
has_many :comments
|
14
|
+
has_many :condition_article_comments, :through => :articles_with_condition, :source => :comments
|
13
15
|
has_many :authored_article_comments, :through => :articles,
|
14
16
|
:source => :comments
|
15
17
|
has_many :notes, :as => :notable
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: squeel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.7.
|
5
|
+
version: 0.7.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ernie Miller
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-16 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|