squeel 1.0.11 → 1.0.12
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 +6 -1
- data/lib/squeel/adapters/active_record.rb +3 -29
- data/lib/squeel/adapters/active_record/3.0/association_preload_extensions.rb +2 -0
- data/lib/squeel/adapters/active_record/3.0/context.rb +3 -62
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +5 -303
- data/lib/squeel/adapters/active_record/3.1/compat.rb +1 -22
- data/lib/squeel/adapters/active_record/3.1/context.rb +1 -76
- data/lib/squeel/adapters/active_record/3.1/preloader_extensions.rb +1 -21
- data/lib/squeel/adapters/active_record/3.1/relation_extensions.rb +4 -391
- data/lib/squeel/adapters/active_record/3.2/compat.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/context.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/relation_extensions.rb +40 -0
- data/lib/squeel/adapters/active_record/base_extensions.rb +2 -0
- data/lib/squeel/adapters/active_record/compat.rb +22 -1
- data/lib/squeel/adapters/active_record/context.rb +84 -1
- data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +11 -0
- data/lib/squeel/adapters/active_record/preloader_extensions.rb +23 -1
- data/lib/squeel/adapters/active_record/relation_extensions.rb +376 -17
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors/visitor.rb +0 -1
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +5 -0
- metadata +8 -4
@@ -1,22 +1 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Nodes
|
4
|
-
|
5
|
-
class Grouping < Unary
|
6
|
-
include Arel::Predications
|
7
|
-
end unless Grouping.include?(Arel::Predications)
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
module Visitors
|
12
|
-
|
13
|
-
class DepthFirst < Visitor
|
14
|
-
|
15
|
-
unless method_defined?(:visit_Arel_Nodes_InfixOperation)
|
16
|
-
alias :visit_Arel_Nodes_InfixOperation :binary
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
1
|
+
require 'squeel/adapters/active_record/compat'
|
@@ -1,76 +1 @@
|
|
1
|
-
require 'squeel/context'
|
2
|
-
|
3
|
-
module Squeel
|
4
|
-
module Adapters
|
5
|
-
module ActiveRecord
|
6
|
-
class Context < ::Squeel::Context
|
7
|
-
# Because the AR::Associations namespace is insane
|
8
|
-
JoinPart = ::ActiveRecord::Associations::JoinDependency::JoinPart
|
9
|
-
|
10
|
-
def initialize(object)
|
11
|
-
super
|
12
|
-
@base = object.join_base
|
13
|
-
@engine = @base.arel_engine
|
14
|
-
@arel_visitor = @engine.connection.visitor
|
15
|
-
@default_table = Arel::Table.new(@base.table_name, :as => @base.aliased_table_name, :engine => @engine)
|
16
|
-
end
|
17
|
-
|
18
|
-
def find(object, parent = @base)
|
19
|
-
if JoinPart === parent
|
20
|
-
case object
|
21
|
-
when String, Symbol, Nodes::Stub
|
22
|
-
assoc_name = object.to_s
|
23
|
-
@object.join_associations.detect { |j|
|
24
|
-
j.reflection.name.to_s == assoc_name && j.parent == parent
|
25
|
-
}
|
26
|
-
when Nodes::Join
|
27
|
-
@object.join_associations.detect { |j|
|
28
|
-
j.reflection.name == object.name && j.parent == parent &&
|
29
|
-
(object.polymorphic? ? j.reflection.klass == object._klass : true)
|
30
|
-
}
|
31
|
-
else
|
32
|
-
@object.join_associations.detect { |j|
|
33
|
-
j.reflection == object && j.parent == parent
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def traverse(keypath, parent = @base, include_endpoint = false)
|
40
|
-
parent = @base if keypath.absolute?
|
41
|
-
keypath.path_without_endpoint.each do |key|
|
42
|
-
parent = find(key, parent) || key
|
43
|
-
end
|
44
|
-
parent = find(keypath.endpoint, parent) if include_endpoint
|
45
|
-
|
46
|
-
parent
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def get_table(object)
|
52
|
-
if [Symbol, String, Nodes::Stub].include?(object.class)
|
53
|
-
Arel::Table.new(object.to_s, :engine => @engine)
|
54
|
-
elsif Nodes::Join === object
|
55
|
-
object._klass ? object._klass.arel_table : Arel::Table.new(object._name, :engine => @engine)
|
56
|
-
elsif object.respond_to?(:aliased_table_name)
|
57
|
-
Arel::Table.new(object.table_name, :as => object.aliased_table_name, :engine => @engine)
|
58
|
-
else
|
59
|
-
raise ArgumentError, "Unable to get table for #{object}"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def classify(object)
|
64
|
-
if Class === object
|
65
|
-
object
|
66
|
-
elsif object.respond_to? :active_record
|
67
|
-
object.active_record
|
68
|
-
else
|
69
|
-
raise ArgumentError, "#{object} can't be converted to a class"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
1
|
+
require 'squeel/adapters/active_record/context'
|
@@ -1,21 +1 @@
|
|
1
|
-
|
2
|
-
module Adapters
|
3
|
-
module ActiveRecord
|
4
|
-
module PreloaderExtensions
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
alias_method_chain :run, :squeel
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def run_with_squeel
|
13
|
-
unless records.empty?
|
14
|
-
Visitors::PreloadVisitor.new.accept(associations).each { |association| preload(association) }
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
1
|
+
require 'squeel/adapters/active_record/preloader_extensions'
|
@@ -1,67 +1,10 @@
|
|
1
|
-
require 'active_record'
|
1
|
+
require 'squeel/adapters/active_record/relation_extensions'
|
2
2
|
|
3
3
|
module Squeel
|
4
4
|
module Adapters
|
5
5
|
module ActiveRecord
|
6
6
|
module RelationExtensions
|
7
7
|
|
8
|
-
JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
9
|
-
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
10
|
-
|
11
|
-
attr_writer :join_dependency
|
12
|
-
private :join_dependency=
|
13
|
-
|
14
|
-
# Returns a JoinDependency for the current relation.
|
15
|
-
#
|
16
|
-
# We don't need to clear out @join_dependency by overriding #reset,
|
17
|
-
# because the default #reset already does this, despite never setting
|
18
|
-
# it anywhere that I can find. Serendipity, I say!
|
19
|
-
def join_dependency
|
20
|
-
@join_dependency ||= (build_join_dependency(table.from(table), @joins_values) && @join_dependency)
|
21
|
-
end
|
22
|
-
|
23
|
-
%w(where having group order select from).each do |visitor|
|
24
|
-
define_method "#{visitor}_visit" do |values|
|
25
|
-
Visitors.const_get("#{visitor.capitalize}Visitor").new(
|
26
|
-
Context.new(join_dependency)
|
27
|
-
).accept(values)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# We need to be able to support merging two relations without having
|
32
|
-
# to get our hooks too deeply into ActiveRecord. That proves to be
|
33
|
-
# easier said than done. I hate Relation#merge. If Squeel has a
|
34
|
-
# nemesis, Relation#merge would be it.
|
35
|
-
#
|
36
|
-
# Whatever code you see here currently is my current best attempt at
|
37
|
-
# coexisting peacefully with said nemesis.
|
38
|
-
def merge(r, equalities_resolved = false)
|
39
|
-
if ::ActiveRecord::Relation === r && !equalities_resolved
|
40
|
-
if self.table_name != r.table_name
|
41
|
-
super(r.visited)
|
42
|
-
else
|
43
|
-
merge_resolving_duplicate_squeel_equalities(r)
|
44
|
-
end
|
45
|
-
else
|
46
|
-
super(r)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def visited
|
51
|
-
clone.visit!
|
52
|
-
end
|
53
|
-
|
54
|
-
def visit!
|
55
|
-
@where_values = where_visit((@where_values - ['']).uniq)
|
56
|
-
@having_values = having_visit(@having_values.uniq.reject{|h| h.blank?})
|
57
|
-
# FIXME: AR barfs on ARel attributes in group_values. Workaround?
|
58
|
-
# @group_values = group_visit(@group_values.uniq.reject{|g| g.blank?})
|
59
|
-
@order_values = order_visit(@order_values.uniq.reject{|o| o.blank?})
|
60
|
-
@select_values = select_visit(@select_values.uniq)
|
61
|
-
|
62
|
-
self
|
63
|
-
end
|
64
|
-
|
65
8
|
def build_arel
|
66
9
|
arel = table.from table
|
67
10
|
|
@@ -77,7 +20,7 @@ module Squeel
|
|
77
20
|
arel.group(*group_visit(@group_values.uniq.reject{|g| g.blank?})) unless @group_values.empty?
|
78
21
|
|
79
22
|
order = @reorder_value ? @reorder_value : @order_values
|
80
|
-
order = order_visit(order)
|
23
|
+
order = order_visit(order.uniq)
|
81
24
|
order = reverse_sql_order(attrs_to_orderings(order)) if @reverse_order_value
|
82
25
|
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
|
83
26
|
|
@@ -89,339 +32,9 @@ module Squeel
|
|
89
32
|
arel
|
90
33
|
end
|
91
34
|
|
92
|
-
# reverse_sql_order will reverse the order of strings or Orderings,
|
93
|
-
# but not attributes
|
94
|
-
def attrs_to_orderings(order)
|
95
|
-
order.map do |o|
|
96
|
-
Arel::Attribute === o ? o.asc : o
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# So, building a select for a count query in ActiveRecord is
|
101
|
-
# pretty heavily dependent on select_values containing strings.
|
102
|
-
# I'd initially expected that I could just hack together a fix
|
103
|
-
# to select_for_count and everything would fall in line, but
|
104
|
-
# unfortunately, pretty much everything from that point on
|
105
|
-
# in ActiveRecord::Calculations#perform_calculation expects
|
106
|
-
# the column to be a string, or at worst, a symbol.
|
107
|
-
#
|
108
|
-
# In the long term, I would like to refactor the code in
|
109
|
-
# Rails core, but for now, I'm going to settle for this hack
|
110
|
-
# that tries really hard to coerce things to a string.
|
111
|
-
def select_for_count
|
112
|
-
visited_values = select_visit(select_values.uniq)
|
113
|
-
if visited_values.size == 1
|
114
|
-
select = visited_values.first
|
115
|
-
|
116
|
-
str_select = case select
|
117
|
-
when String
|
118
|
-
select
|
119
|
-
when Symbol
|
120
|
-
select.to_s
|
121
|
-
else
|
122
|
-
select.to_sql if select.respond_to?(:to_sql)
|
123
|
-
end
|
124
|
-
|
125
|
-
str_select if str_select && str_select !~ /[,*]/
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def build_join_dependency(manager, joins)
|
130
|
-
buckets = joins.group_by do |join|
|
131
|
-
case join
|
132
|
-
when String
|
133
|
-
'string_join'
|
134
|
-
when Hash, Symbol, Array, Nodes::Stub, Nodes::Join, Nodes::KeyPath
|
135
|
-
'association_join'
|
136
|
-
when JoinAssociation
|
137
|
-
'stashed_join'
|
138
|
-
when Arel::Nodes::Join
|
139
|
-
'join_node'
|
140
|
-
else
|
141
|
-
raise 'unknown class: %s' % join.class.name
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
association_joins = buckets['association_join'] || []
|
146
|
-
stashed_association_joins = buckets['stashed_join'] || []
|
147
|
-
join_nodes = (buckets['join_node'] || []).uniq
|
148
|
-
string_joins = (buckets['string_join'] || []).map { |x|
|
149
|
-
x.strip
|
150
|
-
}.uniq
|
151
|
-
|
152
|
-
join_list = join_nodes + custom_join_ast(manager, string_joins)
|
153
|
-
|
154
|
-
# All of that duplication just to do this...
|
155
|
-
self.join_dependency = JoinDependency.new(
|
156
|
-
@klass,
|
157
|
-
association_joins,
|
158
|
-
join_list
|
159
|
-
)
|
160
|
-
|
161
|
-
join_dependency.graft(*stashed_association_joins)
|
162
|
-
|
163
|
-
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
|
164
|
-
|
165
|
-
join_dependency.join_associations.each do |association|
|
166
|
-
association.join_to(manager)
|
167
|
-
end
|
168
|
-
|
169
|
-
manager.join_sources.concat join_list
|
170
|
-
|
171
|
-
manager
|
172
|
-
end
|
173
|
-
|
174
|
-
def includes(*args)
|
175
|
-
if block_given? && args.empty?
|
176
|
-
super(DSL.eval &Proc.new)
|
177
|
-
else
|
178
|
-
super
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def preload(*args)
|
183
|
-
if block_given? && args.empty?
|
184
|
-
super(DSL.eval &Proc.new)
|
185
|
-
else
|
186
|
-
super
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def eager_load(*args)
|
191
|
-
if block_given? && args.empty?
|
192
|
-
super(DSL.eval &Proc.new)
|
193
|
-
else
|
194
|
-
super
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def select(value = Proc.new)
|
199
|
-
if block_given? && Proc === value
|
200
|
-
if value.arity > 0
|
201
|
-
to_a.select {|*block_args| value.call(*block_args)}
|
202
|
-
else
|
203
|
-
relation = clone
|
204
|
-
relation.select_values += Array.wrap(DSL.eval &value)
|
205
|
-
relation
|
206
|
-
end
|
207
|
-
else
|
208
|
-
super
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def group(*args)
|
213
|
-
if block_given? && args.empty?
|
214
|
-
super(DSL.eval &Proc.new)
|
215
|
-
else
|
216
|
-
super
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def order(*args)
|
221
|
-
if block_given? && args.empty?
|
222
|
-
super(DSL.eval &Proc.new)
|
223
|
-
else
|
224
|
-
super
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
def reorder(*args)
|
229
|
-
if block_given? && args.empty?
|
230
|
-
super(DSL.eval &Proc.new)
|
231
|
-
else
|
232
|
-
super
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def joins(*args)
|
237
|
-
if block_given? && args.empty?
|
238
|
-
super(DSL.eval &Proc.new)
|
239
|
-
else
|
240
|
-
super
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def where(opts = Proc.new, *rest)
|
245
|
-
if block_given? && Proc === opts
|
246
|
-
super(DSL.eval &opts)
|
247
|
-
else
|
248
|
-
super
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def having(*args)
|
253
|
-
if block_given? && args.empty?
|
254
|
-
super(DSL.eval &Proc.new)
|
255
|
-
else
|
256
|
-
super
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
def from(*args)
|
261
|
-
if block_given? && args.empty?
|
262
|
-
super(DSL.eval &Proc.new)
|
263
|
-
else
|
264
|
-
super
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def build_where(opts, other = [])
|
269
|
-
case opts
|
270
|
-
when String, Array
|
271
|
-
super
|
272
|
-
else # Let's prevent PredicateBuilder from doing its thing
|
273
|
-
[opts, *other].map do |arg|
|
274
|
-
case arg
|
275
|
-
when Array # Just in case there's an array in there somewhere
|
276
|
-
@klass.send(:sanitize_sql, arg)
|
277
|
-
when Hash
|
278
|
-
@klass.send(:expand_hash_conditions_for_aggregates, arg)
|
279
|
-
else
|
280
|
-
arg
|
281
|
-
end
|
282
|
-
end
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
def collapse_wheres(arel, wheres)
|
287
|
-
wheres = Array(wheres)
|
288
|
-
binaries = wheres.grep(Arel::Nodes::Binary)
|
289
|
-
|
290
|
-
groups = binaries.group_by {|b| [b.class, b.left]}
|
291
|
-
|
292
|
-
groups.each do |_, bins|
|
293
|
-
arel.where(Arel::Nodes::And.new(bins))
|
294
|
-
end
|
295
|
-
|
296
|
-
(wheres - binaries).each do |where|
|
297
|
-
where = Arel.sql(where) if String === where
|
298
|
-
arel.where(Arel::Nodes::Grouping.new(where))
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
def find_equality_predicates(nodes)
|
303
|
-
nodes.map { |node|
|
304
|
-
case node
|
305
|
-
when Arel::Nodes::Equality
|
306
|
-
if node.left.respond_to?(:relation) &&
|
307
|
-
node.left.relation.name == table_name
|
308
|
-
node
|
309
|
-
end
|
310
|
-
when Arel::Nodes::Grouping
|
311
|
-
find_equality_predicates([node.expr])
|
312
|
-
when Arel::Nodes::And
|
313
|
-
find_equality_predicates(node.children)
|
314
|
-
else
|
315
|
-
nil
|
316
|
-
end
|
317
|
-
}.compact.flatten
|
318
|
-
end
|
319
|
-
|
320
|
-
def flatten_nodes(nodes)
|
321
|
-
nodes.map { |node|
|
322
|
-
case node
|
323
|
-
when Array
|
324
|
-
flatten_nodes(node)
|
325
|
-
when Nodes::And
|
326
|
-
flatten_nodes(node.children)
|
327
|
-
when Nodes::Grouping
|
328
|
-
flatten_nodes(node.expr)
|
329
|
-
else
|
330
|
-
node
|
331
|
-
end
|
332
|
-
}.flatten
|
333
|
-
end
|
334
|
-
|
335
|
-
def merge_resolving_duplicate_squeel_equalities(r)
|
336
|
-
left = clone
|
337
|
-
right = r.clone
|
338
|
-
left.where_values = flatten_nodes(left.where_values)
|
339
|
-
right.where_values = flatten_nodes(right.where_values)
|
340
|
-
right_equalities = right.where_values.select do |obj|
|
341
|
-
Nodes::Predicate === obj && obj.method_name == :eq
|
342
|
-
end
|
343
|
-
right.where_values -= right_equalities
|
344
|
-
left.where_values = resolve_duplicate_squeel_equalities(
|
345
|
-
left.where_values + right_equalities
|
346
|
-
)
|
347
|
-
left.merge(right, true)
|
348
|
-
end
|
349
|
-
|
350
|
-
def resolve_duplicate_squeel_equalities(wheres)
|
351
|
-
seen = {}
|
352
|
-
wheres.reverse.reject { |n|
|
353
|
-
nuke = false
|
354
|
-
if Nodes::Predicate === n && n.method_name == :eq
|
355
|
-
nuke = seen[n.expr]
|
356
|
-
seen[n.expr] = true
|
357
|
-
end
|
358
|
-
nuke
|
359
|
-
}.reverse
|
360
|
-
end
|
361
|
-
|
362
|
-
# Simulate the logic that occurs in #to_a
|
363
|
-
#
|
364
|
-
# This will let us get a dump of the SQL that will be run against the
|
365
|
-
# DB for debug purposes without actually running the query.
|
366
|
-
def debug_sql
|
367
|
-
if eager_loading?
|
368
|
-
including = (@eager_load_values + @includes_values).uniq
|
369
|
-
join_dependency = JoinDependency.new(@klass, including, [])
|
370
|
-
construct_relation_for_association_find(join_dependency).to_sql
|
371
|
-
else
|
372
|
-
arel.to_sql
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
### ZOMG ALIAS_METHOD_CHAIN IS BELOW. HIDE YOUR EYES!
|
377
|
-
# ...
|
378
|
-
# ...
|
379
|
-
# ...
|
380
|
-
# Since you're still looking, let me explain this horrible
|
381
|
-
# transgression you see before you.
|
382
|
-
#
|
383
|
-
# You see, Relation#where_values_hash is defined on the
|
384
|
-
# ActiveRecord::Relation class, itself.
|
385
|
-
#
|
386
|
-
# Since it's defined there, but I would very much like to modify its
|
387
|
-
# behavior, I have three choices:
|
388
|
-
#
|
389
|
-
# 1. Inherit from ActiveRecord::Relation in a Squeel::Relation
|
390
|
-
# class, and make an attempt to usurp all of the various calls
|
391
|
-
# to methods on ActiveRecord::Relation by doing some really
|
392
|
-
# evil stuff with constant reassignment, all for the sake of
|
393
|
-
# being able to use super().
|
394
|
-
#
|
395
|
-
# 2. Submit a patch to Rails core, breaking this method off into
|
396
|
-
# another module, all for my own selfish desire to use super()
|
397
|
-
# while mucking about in Rails internals.
|
398
|
-
#
|
399
|
-
# 3. Use alias_method_chain, and say 10 hail Hanssons as penance.
|
400
|
-
#
|
401
|
-
# I opted to go with #3. Except for the hail Hansson thing.
|
402
|
-
# Unless you're DHH, in which case, I totally said them.
|
403
|
-
#
|
404
|
-
# If you'd like to read more about alias_method_chain, see
|
405
|
-
# http://erniemiller.org/2011/02/03/when-to-use-alias_method_chain/
|
406
|
-
|
407
|
-
def self.included(base)
|
408
|
-
base.class_eval do
|
409
|
-
alias_method_chain :where_values_hash, :squeel
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
# where_values_hash is used in scope_for_create. It's what allows
|
414
|
-
# new records to be created with any equality values that exist in
|
415
|
-
# your model's default scope. We hijack it in order to dig down into
|
416
|
-
# And and Grouping nodes, which are equivalent to seeing top-level
|
417
|
-
# Equality nodes in stock AR terms.
|
418
|
-
def where_values_hash_with_squeel
|
419
|
-
equalities = find_equality_predicates(where_visit(with_default_scope.where_values))
|
420
|
-
|
421
|
-
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
422
|
-
end
|
423
|
-
|
424
35
|
end
|
425
36
|
end
|
426
37
|
end
|
427
38
|
end
|
39
|
+
|
40
|
+
ActiveRecord::Relation.send :include, Squeel::Adapters::ActiveRecord::RelationExtensions
|