activerecord 3.1.12 → 3.2.22.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +804 -338
- data/README.rdoc +3 -3
- data/examples/performance.rb +20 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +3 -6
- data/lib/active_record/associations/association.rb +13 -45
- data/lib/active_record/associations/association_scope.rb +3 -15
- data/lib/active_record/associations/belongs_to_association.rb +1 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -4
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +65 -32
- data/lib/active_record/associations/collection_proxy.rb +8 -41
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_association.rb +11 -7
- data/lib/active_record/associations/has_many_through_association.rb +19 -9
- data/lib/active_record/associations/has_one_association.rb +23 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
- data/lib/active_record/associations/join_dependency.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +14 -10
- data/lib/active_record/associations/through_association.rb +8 -4
- data/lib/active_record/associations.rb +92 -76
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +21 -11
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +73 -83
- data/lib/active_record/attribute_methods/serialization.rb +120 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods/write.rb +32 -6
- data/lib/active_record/attribute_methods.rb +231 -30
- data/lib/active_record/autosave_association.rb +44 -26
- data/lib/active_record/base.rb +227 -1708
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +39 -28
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
- data/lib/active_record/connection_adapters/column.rb +37 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +133 -581
- data/lib/active_record/connection_adapters/mysql_adapter.rb +136 -693
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +209 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +62 -35
- data/lib/active_record/counter_cache.rb +9 -4
- data/lib/active_record/dynamic_finder_match.rb +12 -0
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +86 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +57 -86
- data/lib/active_record/identity_map.rb +3 -4
- data/lib/active_record/inheritance.rb +174 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locking/optimistic.rb +33 -26
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +8 -4
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +68 -35
- data/lib/active_record/model_schema.rb +368 -0
- data/lib/active_record/nested_attributes.rb +60 -24
- data/lib/active_record/persistence.rb +57 -11
- data/lib/active_record/query_cache.rb +6 -6
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +37 -29
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +213 -117
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +7 -4
- data/lib/active_record/relation/calculations.rb +55 -16
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +16 -11
- data/lib/active_record/relation/predicate_builder.rb +8 -6
- data/lib/active_record/relation/query_methods.rb +75 -9
- data/lib/active_record/relation/spawn_methods.rb +48 -7
- data/lib/active_record/relation.rb +78 -32
- data/lib/active_record/result.rb +10 -4
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +12 -5
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +200 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +4 -45
- data/lib/active_record/session_store.rb +18 -16
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +17 -3
- data/lib/active_record/transactions.rb +27 -6
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +3 -3
- data/lib/active_record.rb +38 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +49 -28
- data/lib/active_record/named_scope.rb +0 -200
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute'
|
2
|
-
require 'active_support/core_ext/module/deprecation'
|
3
2
|
require 'active_support/core_ext/object/inclusion'
|
4
3
|
|
5
4
|
module ActiveRecord
|
@@ -125,7 +124,7 @@ module ActiveRecord
|
|
125
124
|
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
|
126
125
|
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
|
127
126
|
def class_name
|
128
|
-
@class_name ||= options[:class_name] || derive_class_name
|
127
|
+
@class_name ||= (options[:class_name] || derive_class_name).to_s
|
129
128
|
end
|
130
129
|
|
131
130
|
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
|
@@ -178,15 +177,9 @@ module ActiveRecord
|
|
178
177
|
@collection = macro.in?([:has_many, :has_and_belongs_to_many])
|
179
178
|
end
|
180
179
|
|
181
|
-
# This is a hack so that we can tell if build_association was overridden, in order to
|
182
|
-
# provide an appropriate deprecation if the overridden method ignored the &block. Please
|
183
|
-
# see Association#build_record for details.
|
184
|
-
attr_accessor :original_build_association_called # :nodoc
|
185
|
-
|
186
180
|
# Returns a new, unsaved instance of the associated class. +options+ will
|
187
181
|
# be passed to the class's constructor.
|
188
182
|
def build_association(*options, &block)
|
189
|
-
@original_build_association_called = true
|
190
183
|
klass.new(*options, &block)
|
191
184
|
end
|
192
185
|
|
@@ -202,11 +195,6 @@ module ActiveRecord
|
|
202
195
|
@foreign_key ||= options[:foreign_key] || derive_foreign_key
|
203
196
|
end
|
204
197
|
|
205
|
-
def primary_key_name
|
206
|
-
foreign_key
|
207
|
-
end
|
208
|
-
deprecate :primary_key_name => :foreign_key
|
209
|
-
|
210
198
|
def foreign_type
|
211
199
|
@foreign_type ||= options[:foreign_type] || "#{name}_type"
|
212
200
|
end
|
@@ -274,6 +262,10 @@ module ActiveRecord
|
|
274
262
|
[self]
|
275
263
|
end
|
276
264
|
|
265
|
+
def nested?
|
266
|
+
false
|
267
|
+
end
|
268
|
+
|
277
269
|
# An array of arrays of conditions. Each item in the outside array corresponds to a reflection
|
278
270
|
# in the #chain. The inside arrays are simply conditions (and each condition may itself be
|
279
271
|
# a hash, array, arel predicate, etc...)
|
@@ -381,7 +373,7 @@ module ActiveRecord
|
|
381
373
|
delegate :foreign_key, :foreign_type, :association_foreign_key,
|
382
374
|
:active_record_primary_key, :type, :to => :source_reflection
|
383
375
|
|
384
|
-
# Gets the source of the through reflection.
|
376
|
+
# Gets the source of the through reflection. It checks both a singularized
|
385
377
|
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
|
386
378
|
#
|
387
379
|
# class Post < ActiveRecord::Base
|
@@ -469,7 +461,7 @@ module ActiveRecord
|
|
469
461
|
source_reflection.source_macro
|
470
462
|
end
|
471
463
|
|
472
|
-
# A through association is nested
|
464
|
+
# A through association is nested if there would be more than one join table
|
473
465
|
def nested?
|
474
466
|
chain.length > 2 || through_reflection.macro == :has_and_belongs_to_many
|
475
467
|
end
|
@@ -59,18 +59,21 @@ module ActiveRecord
|
|
59
59
|
relation = apply_finder_options(finder_options)
|
60
60
|
end
|
61
61
|
|
62
|
-
start = options.delete(:start)
|
62
|
+
start = options.delete(:start)
|
63
63
|
batch_size = options.delete(:batch_size) || 1000
|
64
64
|
|
65
65
|
relation = relation.reorder(batch_order).limit(batch_size)
|
66
|
-
records = relation.where(table[primary_key].gteq(start)).
|
66
|
+
records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
|
67
67
|
|
68
68
|
while records.any?
|
69
|
+
records_size = records.size
|
70
|
+
primary_key_offset = records.last.id
|
71
|
+
|
69
72
|
yield records
|
70
73
|
|
71
|
-
break if
|
74
|
+
break if records_size < batch_size
|
72
75
|
|
73
|
-
if primary_key_offset
|
76
|
+
if primary_key_offset
|
74
77
|
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
|
75
78
|
else
|
76
79
|
raise "Primary key not included in the custom select clause"
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
66
66
|
calculate(:average, column_name, options)
|
67
67
|
end
|
68
68
|
|
69
|
-
# Calculates the minimum value on a given column.
|
69
|
+
# Calculates the minimum value on a given column. The value is returned
|
70
70
|
# with the same data type of the column, or +nil+ if there's no row. See
|
71
71
|
# +calculate+ for examples with options.
|
72
72
|
#
|
@@ -89,11 +89,15 @@ module ActiveRecord
|
|
89
89
|
# +calculate+ for examples with options.
|
90
90
|
#
|
91
91
|
# Person.sum('age') # => 4562
|
92
|
-
def sum(
|
93
|
-
|
92
|
+
def sum(*args)
|
93
|
+
if block_given?
|
94
|
+
self.to_a.sum(*args) {|*block_args| yield(*block_args)}
|
95
|
+
else
|
96
|
+
calculate(:sum, *args)
|
97
|
+
end
|
94
98
|
end
|
95
99
|
|
96
|
-
# This calculates aggregate values in the given column.
|
100
|
+
# This calculates aggregate values in the given column. Methods for count, sum, average,
|
97
101
|
# minimum, and maximum have been added as shortcuts. Options such as <tt>:conditions</tt>,
|
98
102
|
# <tt>:order</tt>, <tt>:group</tt>, <tt>:having</tt>, and <tt>:joins</tt> can be passed to customize the query.
|
99
103
|
#
|
@@ -101,7 +105,7 @@ module ActiveRecord
|
|
101
105
|
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
|
102
106
|
# for AVG, and the given column's type for everything else.
|
103
107
|
# * Grouped values: This returns an ordered hash of the values and groups them by the
|
104
|
-
# <tt>:group</tt> option.
|
108
|
+
# <tt>:group</tt> option. It takes either a column name, or the name of a belongs_to association.
|
105
109
|
#
|
106
110
|
# values = Person.maximum(:age, :group => 'last_name')
|
107
111
|
# puts values["Drake"]
|
@@ -119,7 +123,7 @@ module ActiveRecord
|
|
119
123
|
# Options:
|
120
124
|
# * <tt>:conditions</tt> - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ].
|
121
125
|
# See conditions in the intro to ActiveRecord::Base.
|
122
|
-
# * <tt>:include</tt>: Eager loading, see Associations for details.
|
126
|
+
# * <tt>:include</tt>: Eager loading, see Associations for details. Since calculations don't load anything,
|
123
127
|
# the purpose of this is to access fields on joined tables in your conditions, order, or group clauses.
|
124
128
|
# * <tt>:joins</tt> - An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id".
|
125
129
|
# (Rarely needed).
|
@@ -162,12 +166,36 @@ module ActiveRecord
|
|
162
166
|
0
|
163
167
|
end
|
164
168
|
|
169
|
+
# This method is designed to perform select by a single column as direct SQL query
|
170
|
+
# Returns <tt>Array</tt> with values of the specified column name
|
171
|
+
# The values has same data type as column.
|
172
|
+
#
|
173
|
+
# Examples:
|
174
|
+
#
|
175
|
+
# Person.pluck(:id) # SELECT people.id FROM people
|
176
|
+
# Person.uniq.pluck(:role) # SELECT DISTINCT role FROM people
|
177
|
+
# Person.where(:confirmed => true).limit(5).pluck(:id)
|
178
|
+
#
|
179
|
+
def pluck(column_name)
|
180
|
+
if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s)
|
181
|
+
column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
|
182
|
+
end
|
183
|
+
|
184
|
+
result = klass.connection.exec_query(select(column_name).to_sql)
|
185
|
+
last_column = result.columns.last
|
186
|
+
|
187
|
+
result.map do |attributes|
|
188
|
+
klass.type_cast_attribute(last_column, klass.initialize_attributes(attributes))
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
165
192
|
private
|
166
193
|
|
167
194
|
def perform_calculation(operation, column_name, options = {})
|
168
195
|
operation = operation.to_s.downcase
|
169
196
|
|
170
|
-
distinct
|
197
|
+
# If #count is used in conjuction with #uniq it is considered distinct. (eg. relation.uniq.count)
|
198
|
+
distinct = options[:distinct] || self.uniq_value
|
171
199
|
|
172
200
|
if operation == "count"
|
173
201
|
column_name ||= (select_for_count || :all)
|
@@ -223,10 +251,16 @@ module ActiveRecord
|
|
223
251
|
end
|
224
252
|
|
225
253
|
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
254
|
+
group_attrs = @group_values
|
255
|
+
|
256
|
+
if group_attrs.first.respond_to?(:to_sym)
|
257
|
+
association = @klass.reflect_on_association(group_attrs.first.to_sym)
|
258
|
+
associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
|
259
|
+
group_fields = Array(associated ? association.foreign_key : group_attrs)
|
260
|
+
else
|
261
|
+
group_fields = group_attrs
|
262
|
+
end
|
263
|
+
|
230
264
|
group_aliases = group_fields.map { |field| column_alias_for(field) }
|
231
265
|
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
|
232
266
|
[aliaz, column_for(field)]
|
@@ -249,10 +283,14 @@ module ActiveRecord
|
|
249
283
|
select_values += @select_values unless @having_values.empty?
|
250
284
|
|
251
285
|
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
|
252
|
-
|
286
|
+
if field.respond_to?(:as)
|
287
|
+
field.as(aliaz)
|
288
|
+
else
|
289
|
+
"#{field} AS #{aliaz}"
|
290
|
+
end
|
253
291
|
}
|
254
292
|
|
255
|
-
relation = except(:group).group(group
|
293
|
+
relation = except(:group).group(group)
|
256
294
|
relation.select_values = select_values
|
257
295
|
|
258
296
|
calculated_data = @klass.connection.select_all(relation)
|
@@ -264,10 +302,10 @@ module ActiveRecord
|
|
264
302
|
end
|
265
303
|
|
266
304
|
ActiveSupport::OrderedHash[calculated_data.map do |row|
|
267
|
-
key
|
305
|
+
key = group_columns.map { |aliaz, column|
|
268
306
|
type_cast_calculated_value(row[aliaz], column)
|
269
307
|
}
|
270
|
-
key
|
308
|
+
key = key.first if key.size == 1
|
271
309
|
key = key_records[key] if associated
|
272
310
|
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
|
273
311
|
end]
|
@@ -282,6 +320,7 @@ module ActiveRecord
|
|
282
320
|
# column_alias_for("count(*)") # => "count_all"
|
283
321
|
# column_alias_for("count", "id") # => "count_id"
|
284
322
|
def column_alias_for(*keys)
|
323
|
+
keys.map! {|k| k.respond_to?(:to_sql) ? k.to_sql : k}
|
285
324
|
table_name = keys.join(' ')
|
286
325
|
table_name.downcase!
|
287
326
|
table_name.gsub!(/\*/, 'all')
|
@@ -293,7 +332,7 @@ module ActiveRecord
|
|
293
332
|
end
|
294
333
|
|
295
334
|
def column_for(field)
|
296
|
-
field_name = field.to_s.split('.').last
|
335
|
+
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
|
297
336
|
@klass.columns.detect { |c| c.name.to_s == field_name }
|
298
337
|
end
|
299
338
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Delegation
|
5
|
+
# Set up common delegations for performance (avoids method_missing)
|
6
|
+
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a
|
7
|
+
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
|
8
|
+
:connection, :columns_hash, :auto_explain_threshold_in_seconds, :to => :klass
|
9
|
+
|
10
|
+
def self.delegate_to_scoped_klass(method)
|
11
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
12
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
13
|
+
def #{method}(*args, &block)
|
14
|
+
scoping { @klass.#{method}(*args, &block) }
|
15
|
+
end
|
16
|
+
RUBY
|
17
|
+
else
|
18
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
19
|
+
def #{method}(*args, &block)
|
20
|
+
scoping { @klass.send(#{method.inspect}, *args, &block) }
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to?(method, include_private = false)
|
27
|
+
super || Array.method_defined?(method) ||
|
28
|
+
@klass.respond_to?(method, include_private) ||
|
29
|
+
arel.respond_to?(method, include_private)
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def method_missing(method, *args, &block)
|
35
|
+
if @klass.respond_to?(method)
|
36
|
+
::ActiveRecord::Delegation.delegate_to_scoped_klass(method)
|
37
|
+
scoping { @klass.send(method, *args, &block) }
|
38
|
+
elsif Array.method_defined?(method)
|
39
|
+
::ActiveRecord::Delegation.delegate method, :to => :to_a
|
40
|
+
to_a.send(method, *args, &block)
|
41
|
+
elsif arel.respond_to?(method)
|
42
|
+
::ActiveRecord::Delegation.delegate method, :to => :arel
|
43
|
+
arel.send(method, *args, &block)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -83,7 +83,7 @@ module ActiveRecord
|
|
83
83
|
#
|
84
84
|
# Example for find with a lock: Imagine two concurrent transactions:
|
85
85
|
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
|
86
|
-
# in two saves of <tt>person.visits = 3</tt>.
|
86
|
+
# in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
|
87
87
|
# transaction has to wait until the first is finished; we get the
|
88
88
|
# expected <tt>person.visits == 4</tt>.
|
89
89
|
#
|
@@ -134,8 +134,8 @@ module ActiveRecord
|
|
134
134
|
def last(*args)
|
135
135
|
if args.any?
|
136
136
|
if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash))
|
137
|
-
if order_values.empty? &&
|
138
|
-
order("#{
|
137
|
+
if order_values.empty? && primary_key
|
138
|
+
order("#{quoted_table_name}.#{quoted_primary_key} DESC").limit(*args).reverse
|
139
139
|
else
|
140
140
|
to_a.last(*args)
|
141
141
|
end
|
@@ -185,13 +185,12 @@ module ActiveRecord
|
|
185
185
|
# Person.exists?(['name LIKE ?', "%#{query}%"])
|
186
186
|
# Person.exists?
|
187
187
|
def exists?(id = false)
|
188
|
-
return false if id.nil?
|
189
|
-
|
190
188
|
id = id.id if ActiveRecord::Base === id
|
189
|
+
return false if id.nil?
|
191
190
|
|
192
191
|
join_dependency = construct_join_dependency_for_association_find
|
193
192
|
relation = construct_relation_for_association_find(join_dependency)
|
194
|
-
relation = relation.except(:select).select("1").limit(1)
|
193
|
+
relation = relation.except(:select, :order).select("1 AS one").limit(1)
|
195
194
|
|
196
195
|
case id
|
197
196
|
when Array, Hash
|
@@ -200,7 +199,9 @@ module ActiveRecord
|
|
200
199
|
relation = relation.where(table[primary_key].eq(id)) if id
|
201
200
|
end
|
202
201
|
|
203
|
-
connection.select_value(relation) ? true : false
|
202
|
+
connection.select_value(relation, "#{name} Exists") ? true : false
|
203
|
+
rescue ThrowResult
|
204
|
+
false
|
204
205
|
end
|
205
206
|
|
206
207
|
protected
|
@@ -252,9 +253,12 @@ module ActiveRecord
|
|
252
253
|
orders = relation.order_values.map { |val| val.presence }.compact
|
253
254
|
values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
|
254
255
|
|
255
|
-
relation = relation.dup
|
256
|
+
relation = relation.dup.select(values)
|
257
|
+
relation.uniq_value = nil
|
258
|
+
|
259
|
+
id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
|
260
|
+
ids_array = id_rows.map {|row| row[primary_key]}
|
256
261
|
|
257
|
-
ids_array = relation.select(values).collect {|row| row[primary_key]}
|
258
262
|
ids_array.empty? ? raise(ThrowResult) : table[primary_key].in(ids_array)
|
259
263
|
end
|
260
264
|
|
@@ -262,9 +266,10 @@ module ActiveRecord
|
|
262
266
|
conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
|
263
267
|
result = where(conditions).send(match.finder)
|
264
268
|
|
265
|
-
if match.bang? && result.
|
269
|
+
if match.bang? && result.nil?
|
266
270
|
raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}"
|
267
271
|
else
|
272
|
+
yield(result) if block_given?
|
268
273
|
result
|
269
274
|
end
|
270
275
|
end
|
@@ -289,7 +294,7 @@ module ActiveRecord
|
|
289
294
|
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
|
290
295
|
end
|
291
296
|
yield(record) if block_given?
|
292
|
-
record.
|
297
|
+
record.send(match.save_method) if match.save_record?
|
293
298
|
end
|
294
299
|
|
295
300
|
record
|
@@ -27,21 +27,23 @@ module ActiveRecord
|
|
27
27
|
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
28
28
|
attribute.in(value.arel.ast)
|
29
29
|
when Array, ActiveRecord::Associations::CollectionProxy
|
30
|
-
values = value.to_a.map {
|
31
|
-
|
32
|
-
|
30
|
+
values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x}
|
31
|
+
ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)}
|
32
|
+
|
33
|
+
array_predicates = ranges.map {|range| attribute.in(range)}
|
33
34
|
|
34
35
|
if values.include?(nil)
|
35
36
|
values = values.compact
|
36
37
|
if values.empty?
|
37
|
-
attribute.eq
|
38
|
+
array_predicates << attribute.eq(nil)
|
38
39
|
else
|
39
|
-
attribute.in(values.compact).or
|
40
|
+
array_predicates << attribute.in(values.compact).or(attribute.eq(nil))
|
40
41
|
end
|
41
42
|
else
|
42
|
-
attribute.in(values)
|
43
|
+
array_predicates << attribute.in(values)
|
43
44
|
end
|
44
45
|
|
46
|
+
array_predicates.inject {|composite, predicate| composite.or(predicate)}
|
45
47
|
when Range, Arel::Relation
|
46
48
|
attribute.in(value)
|
47
49
|
when ActiveRecord::Base
|
@@ -9,7 +9,8 @@ module ActiveRecord
|
|
9
9
|
:select_values, :group_values, :order_values, :joins_values,
|
10
10
|
:where_values, :having_values, :bind_values,
|
11
11
|
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
|
12
|
-
:from_value, :
|
12
|
+
:from_value, :reordering_value, :reverse_order_value,
|
13
|
+
:uniq_value
|
13
14
|
|
14
15
|
def includes(*args)
|
15
16
|
args.reject! {|a| a.blank? }
|
@@ -38,7 +39,7 @@ module ActiveRecord
|
|
38
39
|
end
|
39
40
|
|
40
41
|
# Works in two unique ways.
|
41
|
-
#
|
42
|
+
#
|
42
43
|
# First: takes a block so it can be used just like Array#select.
|
43
44
|
#
|
44
45
|
# Model.scoped.select { |m| m.field == value }
|
@@ -56,16 +57,16 @@ module ActiveRecord
|
|
56
57
|
# array, it actually returns a relation object and can have other query
|
57
58
|
# methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
|
58
59
|
#
|
59
|
-
#
|
60
|
+
# The argument to the method can also be an array of fields.
|
60
61
|
#
|
61
|
-
# >> Model.select(:field, :other_field, :and_one_more)
|
62
|
+
# >> Model.select([:field, :other_field, :and_one_more])
|
62
63
|
# => [#<Model field: "value", other_field: "value", and_one_more: "value">]
|
63
64
|
#
|
64
65
|
# Any attributes that do not have fields retrieved by a select
|
65
|
-
# will
|
66
|
+
# will raise a ActiveModel::MissingAttributeError when the getter method for that attribute is used:
|
66
67
|
#
|
67
68
|
# >> Model.select(:field).first.other_field
|
68
|
-
# =>
|
69
|
+
# => ActiveModel::MissingAttributeError: missing attribute: other_field
|
69
70
|
def select(value = Proc.new)
|
70
71
|
if block_given?
|
71
72
|
to_a.select {|*block_args| value.call(*block_args) }
|
@@ -92,11 +93,22 @@ module ActiveRecord
|
|
92
93
|
relation
|
93
94
|
end
|
94
95
|
|
96
|
+
# Replaces any existing order defined on the relation with the specified order.
|
97
|
+
#
|
98
|
+
# User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
|
99
|
+
#
|
100
|
+
# Subsequent calls to order on the same relation will be appended. For example:
|
101
|
+
#
|
102
|
+
# User.order('email DESC').reorder('id ASC').order('name ASC')
|
103
|
+
#
|
104
|
+
# generates a query with 'ORDER BY id ASC, name ASC'.
|
105
|
+
#
|
95
106
|
def reorder(*args)
|
96
107
|
return self if args.blank?
|
97
108
|
|
98
109
|
relation = clone
|
99
|
-
relation.
|
110
|
+
relation.reordering_value = true
|
111
|
+
relation.order_values = args.flatten
|
100
112
|
relation
|
101
113
|
end
|
102
114
|
|
@@ -176,6 +188,58 @@ module ActiveRecord
|
|
176
188
|
relation
|
177
189
|
end
|
178
190
|
|
191
|
+
# Specifies whether the records should be unique or not. For example:
|
192
|
+
#
|
193
|
+
# User.select(:name)
|
194
|
+
# # => Might return two records with the same name
|
195
|
+
#
|
196
|
+
# User.select(:name).uniq
|
197
|
+
# # => Returns 1 record per unique name
|
198
|
+
#
|
199
|
+
# User.select(:name).uniq.uniq(false)
|
200
|
+
# # => You can also remove the uniqueness
|
201
|
+
def uniq(value = true)
|
202
|
+
relation = clone
|
203
|
+
relation.uniq_value = value
|
204
|
+
relation
|
205
|
+
end
|
206
|
+
|
207
|
+
# Used to extend a scope with additional methods, either through
|
208
|
+
# a module or through a block provided.
|
209
|
+
#
|
210
|
+
# The object returned is a relation, which can be further extended.
|
211
|
+
#
|
212
|
+
# === Using a module
|
213
|
+
#
|
214
|
+
# module Pagination
|
215
|
+
# def page(number)
|
216
|
+
# # pagination code goes here
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# scope = Model.scoped.extending(Pagination)
|
221
|
+
# scope.page(params[:page])
|
222
|
+
#
|
223
|
+
# You can also pass a list of modules:
|
224
|
+
#
|
225
|
+
# scope = Model.scoped.extending(Pagination, SomethingElse)
|
226
|
+
#
|
227
|
+
# === Using a block
|
228
|
+
#
|
229
|
+
# scope = Model.scoped.extending do
|
230
|
+
# def page(number)
|
231
|
+
# # pagination code goes here
|
232
|
+
# end
|
233
|
+
# end
|
234
|
+
# scope.page(params[:page])
|
235
|
+
#
|
236
|
+
# You can also use a block and a module list:
|
237
|
+
#
|
238
|
+
# scope = Model.scoped.extending(Pagination) do
|
239
|
+
# def per_page(number)
|
240
|
+
# # pagination code goes here
|
241
|
+
# end
|
242
|
+
# end
|
179
243
|
def extending(*modules)
|
180
244
|
modules << Module.new(&Proc.new) if block_given?
|
181
245
|
|
@@ -210,12 +274,13 @@ module ActiveRecord
|
|
210
274
|
|
211
275
|
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
|
212
276
|
|
213
|
-
order = @
|
277
|
+
order = @order_values
|
214
278
|
order = reverse_sql_order(order) if @reverse_order_value
|
215
279
|
arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
|
216
280
|
|
217
281
|
build_select(arel, @select_values.uniq)
|
218
282
|
|
283
|
+
arel.distinct(@uniq_value)
|
219
284
|
arel.from(@from_value) if @from_value
|
220
285
|
arel.lock(@lock_value) if @lock_value
|
221
286
|
|
@@ -331,10 +396,11 @@ module ActiveRecord
|
|
331
396
|
|
332
397
|
order_query.map do |o|
|
333
398
|
case o
|
334
|
-
when Arel::Nodes::
|
399
|
+
when Arel::Nodes::Ordering
|
335
400
|
o.reverse
|
336
401
|
when String, Symbol
|
337
402
|
o.to_s.split(',').collect do |s|
|
403
|
+
s.strip!
|
338
404
|
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
339
405
|
end
|
340
406
|
else
|
@@ -17,26 +17,27 @@ module ActiveRecord
|
|
17
17
|
if method == :includes
|
18
18
|
merged_relation = merged_relation.includes(value)
|
19
19
|
else
|
20
|
-
merged_relation
|
20
|
+
merge_relation_method(merged_relation, method, value)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
(Relation::MULTI_VALUE_METHODS - [:joins, :where]).each do |method|
|
25
|
+
(Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method|
|
26
26
|
value = r.send(:"#{method}_values")
|
27
|
-
merged_relation
|
27
|
+
merge_relation_method(merged_relation, method, value) if value.present?
|
28
28
|
end
|
29
29
|
|
30
|
-
merged_relation
|
30
|
+
merge_joins(merged_relation, r)
|
31
31
|
|
32
32
|
merged_wheres = @where_values + r.where_values
|
33
33
|
|
34
34
|
unless @where_values.empty?
|
35
|
-
# Remove
|
35
|
+
# Remove duplicate ARel attributes. Last one wins.
|
36
36
|
seen = Hash.new { |h,table| h[table] = {} }
|
37
37
|
merged_wheres = merged_wheres.reverse.reject { |w|
|
38
38
|
nuke = false
|
39
|
-
if w.respond_to?(:operator) && w.operator == :==
|
39
|
+
if w.respond_to?(:operator) && w.operator == :== &&
|
40
|
+
w.left.respond_to?(:relation)
|
40
41
|
name = w.left.name
|
41
42
|
table = w.left.relation.name
|
42
43
|
nuke = seen[table][name]
|
@@ -48,7 +49,7 @@ module ActiveRecord
|
|
48
49
|
|
49
50
|
merged_relation.where_values = merged_wheres
|
50
51
|
|
51
|
-
(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with]).each do |method|
|
52
|
+
(Relation::SINGLE_VALUE_METHODS - [:lock, :create_with, :reordering]).each do |method|
|
52
53
|
value = r.send(:"#{method}_value")
|
53
54
|
merged_relation.send(:"#{method}_value=", value) unless value.nil?
|
54
55
|
end
|
@@ -57,6 +58,15 @@ module ActiveRecord
|
|
57
58
|
|
58
59
|
merged_relation = merged_relation.create_with(r.create_with_value) unless r.create_with_value.empty?
|
59
60
|
|
61
|
+
if (r.reordering_value)
|
62
|
+
# override any order specified in the original relation
|
63
|
+
merged_relation.reordering_value = true
|
64
|
+
merged_relation.order_values = r.order_values
|
65
|
+
else
|
66
|
+
# merge in order_values from r
|
67
|
+
merged_relation.order_values += r.order_values
|
68
|
+
end
|
69
|
+
|
60
70
|
# Apply scope extension modules
|
61
71
|
merged_relation.send :apply_modules, r.extensions
|
62
72
|
|
@@ -135,5 +145,36 @@ module ActiveRecord
|
|
135
145
|
relation
|
136
146
|
end
|
137
147
|
|
148
|
+
private
|
149
|
+
|
150
|
+
def merge_joins(relation, other)
|
151
|
+
values = other.joins_values
|
152
|
+
return if values.blank?
|
153
|
+
|
154
|
+
if other.klass >= relation.klass
|
155
|
+
relation.joins_values += values
|
156
|
+
else
|
157
|
+
joins_dependency, rest = values.partition do |join|
|
158
|
+
case join
|
159
|
+
when Hash, Symbol, Array
|
160
|
+
true
|
161
|
+
else
|
162
|
+
false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
167
|
+
other.klass,
|
168
|
+
joins_dependency,
|
169
|
+
[]
|
170
|
+
)
|
171
|
+
|
172
|
+
relation.joins_values += join_dependency.join_associations + rest
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def merge_relation_method(relation, method, value)
|
177
|
+
relation.send(:"#{method}_values=", relation.send(:"#{method}_values") + value)
|
178
|
+
end
|
138
179
|
end
|
139
180
|
end
|