squeel 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +5 -2
- data/lib/squeel/adapters/active_record.rb +8 -0
- data/lib/squeel/adapters/active_record/3.1/context.rb +76 -0
- data/lib/squeel/adapters/active_record/3.1/preloader_extensions.rb +21 -0
- data/lib/squeel/adapters/active_record/3.1/relation_extensions.rb +360 -0
- data/lib/squeel/adapters/active_record/context.rb +1 -76
- data/lib/squeel/adapters/active_record/preloader_extensions.rb +1 -21
- data/lib/squeel/adapters/active_record/relation_extensions.rb +5 -323
- data/lib/squeel/configuration.rb +2 -2
- data/lib/{core_ext → squeel/core_ext}/hash.rb +0 -0
- data/lib/{core_ext → squeel/core_ext}/symbol.rb +0 -0
- data/lib/squeel/version.rb +1 -1
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +4 -0
- data/spec/support/schema.rb +2 -0
- metadata +21 -18
data/.travis.yml
CHANGED
@@ -15,6 +15,14 @@ when 3
|
|
15
15
|
ActiveRecord::Relation.send :include, Squeel::Adapters::ActiveRecord::RelationExtensions
|
16
16
|
ActiveRecord::Associations::ClassMethods::JoinDependency.send :include, Squeel::Adapters::ActiveRecord::JoinDependencyExtensions
|
17
17
|
ActiveRecord::Base.extend Squeel::Adapters::ActiveRecord::AssociationPreloadExtensions
|
18
|
+
when 1
|
19
|
+
require 'squeel/adapters/active_record/3.1/relation_extensions'
|
20
|
+
require 'squeel/adapters/active_record/3.1/preloader_extensions'
|
21
|
+
require 'squeel/adapters/active_record/3.1/context'
|
22
|
+
|
23
|
+
ActiveRecord::Relation.send :include, Squeel::Adapters::ActiveRecord::RelationExtensions
|
24
|
+
ActiveRecord::Associations::JoinDependency.send :include, Squeel::Adapters::ActiveRecord::JoinDependencyExtensions
|
25
|
+
ActiveRecord::Associations::Preloader.send :include, Squeel::Adapters::ActiveRecord::PreloaderExtensions
|
18
26
|
else
|
19
27
|
require 'squeel/adapters/active_record/relation_extensions'
|
20
28
|
require 'squeel/adapters/active_record/preloader_extensions'
|
@@ -0,0 +1,76 @@
|
|
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
|
+
object = object.to_sym if String === object
|
21
|
+
case object
|
22
|
+
when Symbol, Nodes::Stub
|
23
|
+
@object.join_associations.detect { |j|
|
24
|
+
j.reflection.name == object.to_sym && 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.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_sym, :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
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Squeel
|
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::SymbolVisitor.new.accept(associations).each { |association| preload(association) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,360 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module Squeel
|
4
|
+
module Adapters
|
5
|
+
module ActiveRecord
|
6
|
+
module RelationExtensions
|
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, because
|
17
|
+
# the default #reset already does this, despite never setting it anywhere that
|
18
|
+
# 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
|
+
def predicate_visitor
|
24
|
+
Visitors::PredicateVisitor.new(
|
25
|
+
Context.new(join_dependency)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def attribute_visitor
|
30
|
+
Visitors::AttributeVisitor.new(
|
31
|
+
Context.new(join_dependency)
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
# We need to be able to support merging two relations that have a different
|
36
|
+
# base class. Stock ActiveRecord doesn't have to do anything too special, because
|
37
|
+
# it's already created predicates out of the where_values by now, and they're
|
38
|
+
# already bound to the proper table.
|
39
|
+
#
|
40
|
+
# Squeel, on the other hand, needs to do its best to ensure the predicates are still
|
41
|
+
# winding up against the proper table. The most "correct" way I can think of to do
|
42
|
+
# this is to try to emulate the default AR behavior -- that is, de-squeelifying
|
43
|
+
# the *_values, erm... values by visiting them and converting them to ARel nodes
|
44
|
+
# before merging. Merging relations is a nifty little trick, but it's another
|
45
|
+
# little corner of ActiveRecord where the magic quickly fades. :(
|
46
|
+
def merge(r)
|
47
|
+
if relation_with_different_base?(r)
|
48
|
+
r = r.clone.visit!
|
49
|
+
end
|
50
|
+
|
51
|
+
super(r)
|
52
|
+
end
|
53
|
+
|
54
|
+
def relation_with_different_base?(r)
|
55
|
+
::ActiveRecord::Relation === r &&
|
56
|
+
base_class.name != r.klass.base_class.name
|
57
|
+
end
|
58
|
+
|
59
|
+
def prepare_relation_for_association_merge!(r, association_name)
|
60
|
+
r.where_values.map! {|w| Squeel::Visitors::PredicateVisitor.can_visit?(w) ? {association_name => w} : w}
|
61
|
+
r.having_values.map! {|h| Squeel::Visitors::PredicateVisitor.can_visit?(h) ? {association_name => h} : h}
|
62
|
+
r.group_values.map! {|g| Squeel::Visitors::AttributeVisitor.can_visit?(g) ? {association_name => g} : g}
|
63
|
+
r.order_values.map! {|o| Squeel::Visitors::AttributeVisitor.can_visit?(o) ? {association_name => o} : o}
|
64
|
+
r.select_values.map! {|s| Squeel::Visitors::AttributeVisitor.can_visit?(s) ? {association_name => s} : s}
|
65
|
+
r.joins_values.map! {|j| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(j.class) ? {association_name => j} : j}
|
66
|
+
r.includes_values.map! {|i| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(i.class) ? {association_name => i} : i}
|
67
|
+
end
|
68
|
+
|
69
|
+
def visit!
|
70
|
+
predicate_viz = predicate_visitor
|
71
|
+
attribute_viz = attribute_visitor
|
72
|
+
|
73
|
+
@where_values = predicate_viz.accept((@where_values - ['']).uniq)
|
74
|
+
@having_values = predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})
|
75
|
+
@group_values = attribute_viz.accept(@group_values.uniq.reject{|g| g.blank?})
|
76
|
+
@order_values = attribute_viz.accept(@order_values.uniq.reject{|o| o.blank?})
|
77
|
+
@select_values = attribute_viz.accept(@select_values.uniq)
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_arel
|
83
|
+
arel = table.from table
|
84
|
+
|
85
|
+
build_join_dependency(arel, @joins_values) unless @joins_values.empty?
|
86
|
+
|
87
|
+
predicate_viz = predicate_visitor
|
88
|
+
attribute_viz = attribute_visitor
|
89
|
+
|
90
|
+
collapse_wheres(arel, predicate_viz.accept((@where_values - ['']).uniq))
|
91
|
+
|
92
|
+
arel.having(*predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})) unless @having_values.empty?
|
93
|
+
|
94
|
+
arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
|
95
|
+
arel.skip(@offset_value) if @offset_value
|
96
|
+
|
97
|
+
arel.group(*attribute_viz.accept(@group_values.uniq.reject{|g| g.blank?})) unless @group_values.empty?
|
98
|
+
|
99
|
+
order = @reorder_value ? @reorder_value : @order_values
|
100
|
+
order = attribute_viz.accept(order.uniq.reject{|o| o.blank?})
|
101
|
+
order = reverse_sql_order(attrs_to_orderings(order)) if @reverse_order_value
|
102
|
+
arel.order(*order) unless order.empty?
|
103
|
+
|
104
|
+
build_select(arel, attribute_viz.accept(@select_values.uniq))
|
105
|
+
|
106
|
+
arel.from(@from_value) if @from_value
|
107
|
+
arel.lock(@lock_value) if @lock_value
|
108
|
+
|
109
|
+
arel
|
110
|
+
end
|
111
|
+
|
112
|
+
# reverse_sql_order will reverse the order of strings or Orderings,
|
113
|
+
# but not attributes
|
114
|
+
def attrs_to_orderings(order)
|
115
|
+
order.map do |o|
|
116
|
+
Arel::Attribute === o ? o.asc : o
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_join_dependency(manager, joins)
|
121
|
+
buckets = joins.group_by do |join|
|
122
|
+
case join
|
123
|
+
when String
|
124
|
+
'string_join'
|
125
|
+
when Hash, Symbol, Array, Nodes::Stub, Nodes::Join, Nodes::KeyPath
|
126
|
+
'association_join'
|
127
|
+
when JoinAssociation
|
128
|
+
'stashed_join'
|
129
|
+
when Arel::Nodes::Join
|
130
|
+
'join_node'
|
131
|
+
else
|
132
|
+
raise 'unknown class: %s' % join.class.name
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
association_joins = buckets['association_join'] || []
|
137
|
+
stashed_association_joins = buckets['stashed_join'] || []
|
138
|
+
join_nodes = buckets['join_node'] || []
|
139
|
+
string_joins = (buckets['string_join'] || []).map { |x|
|
140
|
+
x.strip
|
141
|
+
}.uniq
|
142
|
+
|
143
|
+
join_list = custom_join_ast(manager, string_joins)
|
144
|
+
|
145
|
+
# All of this duplication just to add
|
146
|
+
self.join_dependency = JoinDependency.new(
|
147
|
+
@klass,
|
148
|
+
association_joins,
|
149
|
+
join_list
|
150
|
+
)
|
151
|
+
|
152
|
+
join_nodes.each do |join|
|
153
|
+
join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase)
|
154
|
+
end
|
155
|
+
|
156
|
+
join_dependency.graft(*stashed_association_joins)
|
157
|
+
|
158
|
+
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
|
159
|
+
|
160
|
+
join_dependency.join_associations.each do |association|
|
161
|
+
association.join_to(manager)
|
162
|
+
end
|
163
|
+
|
164
|
+
manager.join_sources.concat join_nodes.uniq
|
165
|
+
manager.join_sources.concat join_list
|
166
|
+
|
167
|
+
manager
|
168
|
+
end
|
169
|
+
|
170
|
+
def includes(*args)
|
171
|
+
if block_given? && args.empty?
|
172
|
+
super(DSL.eval &Proc.new)
|
173
|
+
else
|
174
|
+
super
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def preload(*args)
|
179
|
+
if block_given? && args.empty?
|
180
|
+
super(DSL.eval &Proc.new)
|
181
|
+
else
|
182
|
+
super
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def eager_load(*args)
|
187
|
+
if block_given? && args.empty?
|
188
|
+
super(DSL.eval &Proc.new)
|
189
|
+
else
|
190
|
+
super
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def select(value = Proc.new)
|
195
|
+
if block_given? && Proc === value
|
196
|
+
if value.arity > 0
|
197
|
+
to_a.select {|*block_args| value.call(*block_args)}
|
198
|
+
else
|
199
|
+
relation = clone
|
200
|
+
relation.select_values += Array.wrap(DSL.eval &value)
|
201
|
+
relation
|
202
|
+
end
|
203
|
+
else
|
204
|
+
super
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def group(*args)
|
209
|
+
if block_given? && args.empty?
|
210
|
+
super(DSL.eval &Proc.new)
|
211
|
+
else
|
212
|
+
super
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def order(*args)
|
217
|
+
if block_given? && args.empty?
|
218
|
+
super(DSL.eval &Proc.new)
|
219
|
+
else
|
220
|
+
super
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def reorder(*args)
|
225
|
+
if block_given? && args.empty?
|
226
|
+
super(DSL.eval &Proc.new)
|
227
|
+
else
|
228
|
+
super
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def joins(*args)
|
233
|
+
if block_given? && args.empty?
|
234
|
+
super(DSL.eval &Proc.new)
|
235
|
+
else
|
236
|
+
super
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def where(opts = Proc.new, *rest)
|
241
|
+
if block_given? && Proc === opts
|
242
|
+
super(DSL.eval &opts)
|
243
|
+
else
|
244
|
+
super
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def having(*args)
|
249
|
+
if block_given? && args.empty?
|
250
|
+
super(DSL.eval &Proc.new)
|
251
|
+
else
|
252
|
+
super
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def build_where(opts, other = [])
|
257
|
+
case opts
|
258
|
+
when String, Array
|
259
|
+
super
|
260
|
+
else # Let's prevent PredicateBuilder from doing its thing
|
261
|
+
[opts, *other].map do |arg|
|
262
|
+
case arg
|
263
|
+
when Array # Just in case there's an array in there somewhere
|
264
|
+
@klass.send(:sanitize_sql, arg)
|
265
|
+
when Hash
|
266
|
+
@klass.send(:expand_hash_conditions_for_aggregates, arg)
|
267
|
+
else
|
268
|
+
arg
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def collapse_wheres(arel, wheres)
|
275
|
+
wheres = Array(wheres)
|
276
|
+
binaries = wheres.grep(Arel::Nodes::Binary)
|
277
|
+
|
278
|
+
groups = binaries.group_by {|b| [b.class, b.left]}
|
279
|
+
|
280
|
+
groups.each do |_, bins|
|
281
|
+
arel.where(Arel::Nodes::And.new(bins))
|
282
|
+
end
|
283
|
+
|
284
|
+
(wheres - binaries).each do |where|
|
285
|
+
where = Arel.sql(where) if String === where
|
286
|
+
arel.where(Arel::Nodes::Grouping.new(where))
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def find_equality_predicates(nodes)
|
291
|
+
nodes.map { |node|
|
292
|
+
case node
|
293
|
+
when Arel::Nodes::Equality
|
294
|
+
node if node.left.relation.name == table_name
|
295
|
+
when Arel::Nodes::Grouping
|
296
|
+
find_equality_predicates([node.expr])
|
297
|
+
when Arel::Nodes::And
|
298
|
+
find_equality_predicates(node.children)
|
299
|
+
else
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
}.compact.flatten
|
303
|
+
end
|
304
|
+
|
305
|
+
# Simulate the logic that occurs in #to_a
|
306
|
+
#
|
307
|
+
# This will let us get a dump of the SQL that will be run against the
|
308
|
+
# DB for debug purposes without actually running the query.
|
309
|
+
def debug_sql
|
310
|
+
if eager_loading?
|
311
|
+
including = (@eager_load_values + @includes_values).uniq
|
312
|
+
join_dependency = JoinDependency.new(@klass, including, [])
|
313
|
+
construct_relation_for_association_find(join_dependency).to_sql
|
314
|
+
else
|
315
|
+
arel.to_sql
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
### ZOMG ALIAS_METHOD_CHAIN IS BELOW. HIDE YOUR EYES!
|
320
|
+
# ...
|
321
|
+
# ...
|
322
|
+
# ...
|
323
|
+
# Since you're still looking, let me explain this horrible
|
324
|
+
# transgression you see before you.
|
325
|
+
# You see, Relation#where_values_hash is defined on the
|
326
|
+
# ActiveRecord::Relation class. Since it's defined there, but
|
327
|
+
# I would very much like to modify its behavior, I have three
|
328
|
+
# choices.
|
329
|
+
#
|
330
|
+
# 1. Inherit from ActiveRecord::Relation in a Squeel::Relation
|
331
|
+
# class, and make an attempt to usurp all of the various calls
|
332
|
+
# to methods on ActiveRecord::Relation by doing some really
|
333
|
+
# evil stuff with constant reassignment, all for the sake of
|
334
|
+
# being able to use super().
|
335
|
+
#
|
336
|
+
# 2. Submit a patch to Rails core, breaking this method off into
|
337
|
+
# another module, all for my own selfish desire to use super()
|
338
|
+
# while mucking about in Rails internals.
|
339
|
+
#
|
340
|
+
# 3. Use alias_method_chain, and say 10 hail Hanssons as penance.
|
341
|
+
#
|
342
|
+
# I opted to go with #3. Except for the hail Hansson thing.
|
343
|
+
# Unless you're DHH, in which case, I totally said them.
|
344
|
+
|
345
|
+
def self.included(base)
|
346
|
+
base.class_eval do
|
347
|
+
alias_method_chain :where_values_hash, :squeel
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def where_values_hash_with_squeel
|
352
|
+
equalities = find_equality_predicates(predicate_visitor.accept(@where_values))
|
353
|
+
|
354
|
+
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
355
|
+
end
|
356
|
+
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
@@ -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
|
-
object = object.to_sym if String === object
|
21
|
-
case object
|
22
|
-
when Symbol, Nodes::Stub
|
23
|
-
@object.join_associations.detect { |j|
|
24
|
-
j.reflection.name == object.to_sym && 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.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_sym, :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/3.1/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::SymbolVisitor.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/3.1/preloader_extensions'
|
@@ -1,84 +1,10 @@
|
|
1
|
-
require 'active_record'
|
1
|
+
require 'squeel/adapters/active_record/3.1/relation_extensions'
|
2
2
|
|
3
3
|
module Squeel
|
4
4
|
module Adapters
|
5
5
|
module ActiveRecord
|
6
6
|
module RelationExtensions
|
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, because
|
17
|
-
# the default #reset already does this, despite never setting it anywhere that
|
18
|
-
# 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
|
-
def predicate_visitor
|
24
|
-
Visitors::PredicateVisitor.new(
|
25
|
-
Context.new(join_dependency)
|
26
|
-
)
|
27
|
-
end
|
28
|
-
|
29
|
-
def attribute_visitor
|
30
|
-
Visitors::AttributeVisitor.new(
|
31
|
-
Context.new(join_dependency)
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
# We need to be able to support merging two relations that have a different
|
36
|
-
# base class. Stock ActiveRecord doesn't have to do anything too special, because
|
37
|
-
# it's already created predicates out of the where_values by now, and they're
|
38
|
-
# already bound to the proper table.
|
39
|
-
#
|
40
|
-
# Squeel, on the other hand, needs to do its best to ensure the predicates are still
|
41
|
-
# winding up against the proper table. The most "correct" way I can think of to do
|
42
|
-
# this is to try to emulate the default AR behavior -- that is, de-squeelifying
|
43
|
-
# the *_values, erm... values by visiting them and converting them to ARel nodes
|
44
|
-
# before merging. Merging relations is a nifty little trick, but it's another
|
45
|
-
# little corner of ActiveRecord where the magic quickly fades. :(
|
46
|
-
def merge(r)
|
47
|
-
if relation_with_different_base?(r)
|
48
|
-
r = r.clone.visit!
|
49
|
-
end
|
50
|
-
|
51
|
-
super(r)
|
52
|
-
end
|
53
|
-
|
54
|
-
def relation_with_different_base?(r)
|
55
|
-
::ActiveRecord::Relation === r &&
|
56
|
-
base_class.name != r.klass.base_class.name
|
57
|
-
end
|
58
|
-
|
59
|
-
def prepare_relation_for_association_merge!(r, association_name)
|
60
|
-
r.where_values.map! {|w| Squeel::Visitors::PredicateVisitor.can_visit?(w) ? {association_name => w} : w}
|
61
|
-
r.having_values.map! {|h| Squeel::Visitors::PredicateVisitor.can_visit?(h) ? {association_name => h} : h}
|
62
|
-
r.group_values.map! {|g| Squeel::Visitors::AttributeVisitor.can_visit?(g) ? {association_name => g} : g}
|
63
|
-
r.order_values.map! {|o| Squeel::Visitors::AttributeVisitor.can_visit?(o) ? {association_name => o} : o}
|
64
|
-
r.select_values.map! {|s| Squeel::Visitors::AttributeVisitor.can_visit?(s) ? {association_name => s} : s}
|
65
|
-
r.joins_values.map! {|j| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(j.class) ? {association_name => j} : j}
|
66
|
-
r.includes_values.map! {|i| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(i.class) ? {association_name => i} : i}
|
67
|
-
end
|
68
|
-
|
69
|
-
def visit!
|
70
|
-
predicate_viz = predicate_visitor
|
71
|
-
attribute_viz = attribute_visitor
|
72
|
-
|
73
|
-
@where_values = predicate_viz.accept((@where_values - ['']).uniq)
|
74
|
-
@having_values = predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})
|
75
|
-
@group_values = attribute_viz.accept(@group_values.uniq.reject{|g| g.blank?})
|
76
|
-
@order_values = attribute_viz.accept(@order_values.uniq.reject{|o| o.blank?})
|
77
|
-
@select_values = attribute_viz.accept(@select_values.uniq)
|
78
|
-
|
79
|
-
self
|
80
|
-
end
|
81
|
-
|
7
|
+
|
82
8
|
def build_arel
|
83
9
|
arel = table.from table
|
84
10
|
|
@@ -102,258 +28,14 @@ module Squeel
|
|
102
28
|
arel.order(*order) unless order.empty?
|
103
29
|
|
104
30
|
build_select(arel, attribute_viz.accept(@select_values.uniq))
|
105
|
-
|
31
|
+
|
32
|
+
arel.distinct(@uniq_value)
|
106
33
|
arel.from(@from_value) if @from_value
|
107
34
|
arel.lock(@lock_value) if @lock_value
|
108
35
|
|
109
36
|
arel
|
110
37
|
end
|
111
|
-
|
112
|
-
# reverse_sql_order will reverse the order of strings or Orderings,
|
113
|
-
# but not attributes
|
114
|
-
def attrs_to_orderings(order)
|
115
|
-
order.map do |o|
|
116
|
-
Arel::Attribute === o ? o.asc : o
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def build_join_dependency(manager, joins)
|
121
|
-
buckets = joins.group_by do |join|
|
122
|
-
case join
|
123
|
-
when String
|
124
|
-
'string_join'
|
125
|
-
when Hash, Symbol, Array, Nodes::Stub, Nodes::Join, Nodes::KeyPath
|
126
|
-
'association_join'
|
127
|
-
when JoinAssociation
|
128
|
-
'stashed_join'
|
129
|
-
when Arel::Nodes::Join
|
130
|
-
'join_node'
|
131
|
-
else
|
132
|
-
raise 'unknown class: %s' % join.class.name
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
association_joins = buckets['association_join'] || []
|
137
|
-
stashed_association_joins = buckets['stashed_join'] || []
|
138
|
-
join_nodes = buckets['join_node'] || []
|
139
|
-
string_joins = (buckets['string_join'] || []).map { |x|
|
140
|
-
x.strip
|
141
|
-
}.uniq
|
142
|
-
|
143
|
-
join_list = custom_join_ast(manager, string_joins)
|
144
|
-
|
145
|
-
# All of this duplication just to add
|
146
|
-
self.join_dependency = JoinDependency.new(
|
147
|
-
@klass,
|
148
|
-
association_joins,
|
149
|
-
join_list
|
150
|
-
)
|
151
|
-
|
152
|
-
join_nodes.each do |join|
|
153
|
-
join_dependency.alias_tracker.aliased_name_for(join.left.name.downcase)
|
154
|
-
end
|
155
|
-
|
156
|
-
join_dependency.graft(*stashed_association_joins)
|
157
|
-
|
158
|
-
@implicit_readonly = true unless association_joins.empty? && stashed_association_joins.empty?
|
159
|
-
|
160
|
-
join_dependency.join_associations.each do |association|
|
161
|
-
association.join_to(manager)
|
162
|
-
end
|
163
|
-
|
164
|
-
manager.join_sources.concat join_nodes.uniq
|
165
|
-
manager.join_sources.concat join_list
|
166
|
-
|
167
|
-
manager
|
168
|
-
end
|
169
|
-
|
170
|
-
def includes(*args)
|
171
|
-
if block_given? && args.empty?
|
172
|
-
super(DSL.eval &Proc.new)
|
173
|
-
else
|
174
|
-
super
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def preload(*args)
|
179
|
-
if block_given? && args.empty?
|
180
|
-
super(DSL.eval &Proc.new)
|
181
|
-
else
|
182
|
-
super
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def eager_load(*args)
|
187
|
-
if block_given? && args.empty?
|
188
|
-
super(DSL.eval &Proc.new)
|
189
|
-
else
|
190
|
-
super
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
def select(value = Proc.new)
|
195
|
-
if block_given? && Proc === value
|
196
|
-
if value.arity > 0
|
197
|
-
to_a.select {|*block_args| value.call(*block_args)}
|
198
|
-
else
|
199
|
-
relation = clone
|
200
|
-
relation.select_values += Array.wrap(DSL.eval &value)
|
201
|
-
relation
|
202
|
-
end
|
203
|
-
else
|
204
|
-
super
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def group(*args)
|
209
|
-
if block_given? && args.empty?
|
210
|
-
super(DSL.eval &Proc.new)
|
211
|
-
else
|
212
|
-
super
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
def order(*args)
|
217
|
-
if block_given? && args.empty?
|
218
|
-
super(DSL.eval &Proc.new)
|
219
|
-
else
|
220
|
-
super
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def reorder(*args)
|
225
|
-
if block_given? && args.empty?
|
226
|
-
super(DSL.eval &Proc.new)
|
227
|
-
else
|
228
|
-
super
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def joins(*args)
|
233
|
-
if block_given? && args.empty?
|
234
|
-
super(DSL.eval &Proc.new)
|
235
|
-
else
|
236
|
-
super
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def where(opts = Proc.new, *rest)
|
241
|
-
if block_given? && Proc === opts
|
242
|
-
super(DSL.eval &opts)
|
243
|
-
else
|
244
|
-
super
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
def having(*args)
|
249
|
-
if block_given? && args.empty?
|
250
|
-
super(DSL.eval &Proc.new)
|
251
|
-
else
|
252
|
-
super
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
def build_where(opts, other = [])
|
257
|
-
case opts
|
258
|
-
when String, Array
|
259
|
-
super
|
260
|
-
else # Let's prevent PredicateBuilder from doing its thing
|
261
|
-
[opts, *other].map do |arg|
|
262
|
-
case arg
|
263
|
-
when Array # Just in case there's an array in there somewhere
|
264
|
-
@klass.send(:sanitize_sql, arg)
|
265
|
-
when Hash
|
266
|
-
@klass.send(:expand_hash_conditions_for_aggregates, arg)
|
267
|
-
else
|
268
|
-
arg
|
269
|
-
end
|
270
|
-
end
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
def collapse_wheres(arel, wheres)
|
275
|
-
wheres = Array(wheres)
|
276
|
-
binaries = wheres.grep(Arel::Nodes::Binary)
|
277
|
-
|
278
|
-
groups = binaries.group_by {|b| [b.class, b.left]}
|
279
|
-
|
280
|
-
groups.each do |_, bins|
|
281
|
-
arel.where(Arel::Nodes::And.new(bins))
|
282
|
-
end
|
283
|
-
|
284
|
-
(wheres - binaries).each do |where|
|
285
|
-
where = Arel.sql(where) if String === where
|
286
|
-
arel.where(Arel::Nodes::Grouping.new(where))
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
def find_equality_predicates(nodes)
|
291
|
-
nodes.map { |node|
|
292
|
-
case node
|
293
|
-
when Arel::Nodes::Equality
|
294
|
-
node if node.left.relation.name == table_name
|
295
|
-
when Arel::Nodes::Grouping
|
296
|
-
find_equality_predicates([node.expr])
|
297
|
-
when Arel::Nodes::And
|
298
|
-
find_equality_predicates(node.children)
|
299
|
-
else
|
300
|
-
nil
|
301
|
-
end
|
302
|
-
}.compact.flatten
|
303
|
-
end
|
304
|
-
|
305
|
-
# Simulate the logic that occurs in #to_a
|
306
|
-
#
|
307
|
-
# This will let us get a dump of the SQL that will be run against the
|
308
|
-
# DB for debug purposes without actually running the query.
|
309
|
-
def debug_sql
|
310
|
-
if eager_loading?
|
311
|
-
including = (@eager_load_values + @includes_values).uniq
|
312
|
-
join_dependency = JoinDependency.new(@klass, including, [])
|
313
|
-
construct_relation_for_association_find(join_dependency).to_sql
|
314
|
-
else
|
315
|
-
arel.to_sql
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
### ZOMG ALIAS_METHOD_CHAIN IS BELOW. HIDE YOUR EYES!
|
320
|
-
# ...
|
321
|
-
# ...
|
322
|
-
# ...
|
323
|
-
# Since you're still looking, let me explain this horrible
|
324
|
-
# transgression you see before you.
|
325
|
-
# You see, Relation#where_values_hash is defined on the
|
326
|
-
# ActiveRecord::Relation class. Since it's defined there, but
|
327
|
-
# I would very much like to modify its behavior, I have three
|
328
|
-
# choices.
|
329
|
-
#
|
330
|
-
# 1. Inherit from ActiveRecord::Relation in a Squeel::Relation
|
331
|
-
# class, and make an attempt to usurp all of the various calls
|
332
|
-
# to methods on ActiveRecord::Relation by doing some really
|
333
|
-
# evil stuff with constant reassignment, all for the sake of
|
334
|
-
# being able to use super().
|
335
|
-
#
|
336
|
-
# 2. Submit a patch to Rails core, breaking this method off into
|
337
|
-
# another module, all for my own selfish desire to use super()
|
338
|
-
# while mucking about in Rails internals.
|
339
|
-
#
|
340
|
-
# 3. Use alias_method_chain, and say 10 hail Hanssons as penance.
|
341
|
-
#
|
342
|
-
# I opted to go with #3. Except for the hail Hansson thing.
|
343
|
-
# Unless you're DHH, in which case, I totally said them.
|
344
|
-
|
345
|
-
def self.included(base)
|
346
|
-
base.class_eval do
|
347
|
-
alias_method_chain :where_values_hash, :squeel
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
def where_values_hash_with_squeel
|
352
|
-
equalities = find_equality_predicates(predicate_visitor.accept(@where_values))
|
353
|
-
|
354
|
-
Hash[equalities.map { |where| [where.left.name, where.right] }]
|
355
|
-
end
|
356
|
-
|
38
|
+
|
357
39
|
end
|
358
40
|
end
|
359
41
|
end
|
data/lib/squeel/configuration.rb
CHANGED
@@ -34,7 +34,7 @@ module Squeel
|
|
34
34
|
# @param [Symbol] sym2 :hash or :symbol
|
35
35
|
def load_core_extensions(*exts)
|
36
36
|
exts.each do |ext|
|
37
|
-
require "core_ext/#{ext}"
|
37
|
+
require "squeel/core_ext/#{ext}"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -51,4 +51,4 @@ module Squeel
|
|
51
51
|
end
|
52
52
|
|
53
53
|
end
|
54
|
-
end
|
54
|
+
end
|
File without changes
|
File without changes
|
data/lib/squeel/version.rb
CHANGED
@@ -97,6 +97,10 @@ module Squeel
|
|
97
97
|
relation.join_dependency.join_associations.should have(6).items
|
98
98
|
arel.to_sql.should match /INNER JOIN "people" "parents_people_3" ON "parents_people_3"."id" = "children_people_3"."parent_id"/
|
99
99
|
end
|
100
|
+
|
101
|
+
it 'respects :uniq option on associations' do
|
102
|
+
Article.first.uniq_commenters.length.should eq Article.first.uniq_commenters.count
|
103
|
+
end
|
100
104
|
|
101
105
|
it 'visits wheres with a PredicateVisitor, converting them to ARel nodes' do
|
102
106
|
relation = Person.where(:name.matches => '%bob%')
|
data/spec/support/schema.rb
CHANGED
@@ -48,6 +48,7 @@ class Article < ActiveRecord::Base
|
|
48
48
|
has_and_belongs_to_many :tags
|
49
49
|
has_many :notes, :as => :notable
|
50
50
|
has_many :commenters, :through => :comments, :source => :person
|
51
|
+
has_many :uniq_commenters, :through => :comments, :source => :person, :uniq => true
|
51
52
|
end
|
52
53
|
|
53
54
|
class Comment < ActiveRecord::Base
|
@@ -137,6 +138,7 @@ module Schema
|
|
137
138
|
end
|
138
139
|
|
139
140
|
Comment.make(:body => 'First post!', :article => Article.make(:title => 'Hello, world!'))
|
141
|
+
Comment.make(:body => 'Last post!', :article => Article.first, :person => Article.first.commenters.first)
|
140
142
|
|
141
143
|
end
|
142
144
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: squeel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70129585123660 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70129585123660
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70129585122480 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '3.0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70129585122480
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: polyamorous
|
38
|
-
requirement: &
|
38
|
+
requirement: &70129585121660 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 0.5.0
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70129585121660
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70129585121080 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 2.6.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70129585121080
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: machinist
|
60
|
-
requirement: &
|
60
|
+
requirement: &70129585120560 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ~>
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: 1.0.6
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70129585120560
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: faker
|
71
|
-
requirement: &
|
71
|
+
requirement: &70129585135820 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 0.9.5
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70129585135820
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: sqlite3
|
82
|
-
requirement: &
|
82
|
+
requirement: &70129585134740 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ~>
|
@@ -87,7 +87,7 @@ dependencies:
|
|
87
87
|
version: 1.3.3
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70129585134740
|
91
91
|
description: ! "\n Squeel unlocks the power of ARel in your Rails 3 application
|
92
92
|
with\n a handy block-based syntax. You can write subqueries, access named\n
|
93
93
|
\ functions provided by your RDBMS, and more, all without writing\n SQL
|
@@ -105,14 +105,15 @@ files:
|
|
105
105
|
- LICENSE
|
106
106
|
- README.md
|
107
107
|
- Rakefile
|
108
|
-
- lib/core_ext/hash.rb
|
109
|
-
- lib/core_ext/symbol.rb
|
110
108
|
- lib/squeel.rb
|
111
109
|
- lib/squeel/adapters/active_record.rb
|
112
110
|
- lib/squeel/adapters/active_record/3.0/association_preload_extensions.rb
|
113
111
|
- lib/squeel/adapters/active_record/3.0/compat.rb
|
114
112
|
- lib/squeel/adapters/active_record/3.0/context.rb
|
115
113
|
- lib/squeel/adapters/active_record/3.0/relation_extensions.rb
|
114
|
+
- lib/squeel/adapters/active_record/3.1/context.rb
|
115
|
+
- lib/squeel/adapters/active_record/3.1/preloader_extensions.rb
|
116
|
+
- lib/squeel/adapters/active_record/3.1/relation_extensions.rb
|
116
117
|
- lib/squeel/adapters/active_record/base_extensions.rb
|
117
118
|
- lib/squeel/adapters/active_record/context.rb
|
118
119
|
- lib/squeel/adapters/active_record/join_dependency_extensions.rb
|
@@ -121,6 +122,8 @@ files:
|
|
121
122
|
- lib/squeel/configuration.rb
|
122
123
|
- lib/squeel/constants.rb
|
123
124
|
- lib/squeel/context.rb
|
125
|
+
- lib/squeel/core_ext/hash.rb
|
126
|
+
- lib/squeel/core_ext/symbol.rb
|
124
127
|
- lib/squeel/dsl.rb
|
125
128
|
- lib/squeel/nodes.rb
|
126
129
|
- lib/squeel/nodes/aliasing.rb
|