squeel 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)
@@ -1,3 +1,3 @@
1
1
  module Squeel
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.1"
3
3
  end
@@ -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
@@ -37,6 +37,7 @@ module Squeel
37
37
  table.name.should eq 'notes'
38
38
  table.table_alias.should be_nil
39
39
  end
40
+
40
41
  end
41
42
  end
42
43
  end
@@ -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
@@ -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.0
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 00:00:00 Z
13
+ date: 2011-05-16 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord