activerecord 4.1.16 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def valid_options
|
8
|
-
valid = super + [:
|
8
|
+
valid = super + [:as, :foreign_type]
|
9
9
|
valid += [:through, :source, :source_type] if options[:through]
|
10
10
|
valid
|
11
11
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord::Associations::Builder
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def self.
|
19
|
+
def self.add_destroy_callbacks(model, reflection)
|
20
20
|
super unless reflection.options[:through]
|
21
21
|
end
|
22
22
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord::Associations::Builder
|
4
4
|
class SingularAssociation < Association #:nodoc:
|
5
5
|
def valid_options
|
6
|
-
super + [:
|
6
|
+
super + [:dependent, :primary_key, :inverse_of, :required]
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.define_accessors(model, reflection)
|
@@ -27,5 +27,12 @@ module ActiveRecord::Associations::Builder
|
|
27
27
|
end
|
28
28
|
CODE
|
29
29
|
end
|
30
|
+
|
31
|
+
def self.define_validations(model, reflection)
|
32
|
+
super
|
33
|
+
if reflection.options[:required]
|
34
|
+
model.validates_presence_of reflection.name
|
35
|
+
end
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|
@@ -61,10 +61,21 @@ module ActiveRecord
|
|
61
61
|
|
62
62
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
63
|
def ids_writer(ids)
|
64
|
-
pk_column = reflection.
|
65
|
-
|
66
|
-
ids.map
|
67
|
-
|
64
|
+
pk_column = reflection.association_primary_key
|
65
|
+
pk_type = klass.type_for_attribute(pk_column)
|
66
|
+
ids = Array(ids).reject(&:blank?).map do |i|
|
67
|
+
pk_type.type_cast_from_user(i)
|
68
|
+
end
|
69
|
+
|
70
|
+
objs = klass.where(pk_column => ids).index_by do |r|
|
71
|
+
r.send(pk_column)
|
72
|
+
end.values_at(*ids).compact
|
73
|
+
|
74
|
+
if objs.size == ids.size
|
75
|
+
replace(objs.index_by { |r| r.send(pk_column) }.values_at(*ids))
|
76
|
+
else
|
77
|
+
klass.all.raise_record_not_found_exception!(ids, objs.size, ids.size)
|
78
|
+
end
|
68
79
|
end
|
69
80
|
|
70
81
|
def reset
|
@@ -161,9 +172,8 @@ module ActiveRecord
|
|
161
172
|
# be chained. Since << flattens its argument list and inserts each record,
|
162
173
|
# +push+ and +concat+ behave identically.
|
163
174
|
def concat(*records)
|
164
|
-
load_target if owner.new_record?
|
165
|
-
|
166
175
|
if owner.new_record?
|
176
|
+
load_target
|
167
177
|
concat_records(records)
|
168
178
|
else
|
169
179
|
transaction { concat_records(records) }
|
@@ -186,8 +196,8 @@ module ActiveRecord
|
|
186
196
|
end
|
187
197
|
|
188
198
|
# Removes all records from the association without calling callbacks
|
189
|
-
# on the associated records. It honors the
|
190
|
-
# if the
|
199
|
+
# on the associated records. It honors the +:dependent+ option. However
|
200
|
+
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
191
201
|
# deletion strategy for the association is applied.
|
192
202
|
#
|
193
203
|
# You can force a particular deletion strategy by passing a parameter.
|
@@ -199,11 +209,11 @@ module ActiveRecord
|
|
199
209
|
#
|
200
210
|
# See delete for more info.
|
201
211
|
def delete_all(dependent = nil)
|
202
|
-
if dependent
|
212
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
203
213
|
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
204
214
|
end
|
205
215
|
|
206
|
-
dependent = if dependent
|
216
|
+
dependent = if dependent
|
207
217
|
dependent
|
208
218
|
elsif options[:dependent] == :destroy
|
209
219
|
:delete_all
|
@@ -211,7 +221,7 @@ module ActiveRecord
|
|
211
221
|
options[:dependent]
|
212
222
|
end
|
213
223
|
|
214
|
-
|
224
|
+
delete_or_nullify_all_records(dependent).tap do
|
215
225
|
reset
|
216
226
|
loaded!
|
217
227
|
end
|
@@ -261,19 +271,12 @@ module ActiveRecord
|
|
261
271
|
# are actually removed from the database, that depends precisely on
|
262
272
|
# +delete_records+. They are in any case removed from the collection.
|
263
273
|
def delete(*records)
|
274
|
+
return if records.empty?
|
264
275
|
_options = records.extract_options!
|
265
276
|
dependent = _options[:dependent] || options[:dependent]
|
266
277
|
|
267
|
-
if records.
|
268
|
-
|
269
|
-
delete_or_destroy(load_target, dependent)
|
270
|
-
else
|
271
|
-
delete_records(:all, dependent)
|
272
|
-
end
|
273
|
-
else
|
274
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
275
|
-
delete_or_destroy(records, dependent)
|
276
|
-
end
|
278
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
279
|
+
delete_or_destroy(records, dependent)
|
277
280
|
end
|
278
281
|
|
279
282
|
# Deletes the +records+ and removes them from this association calling
|
@@ -282,7 +285,8 @@ module ActiveRecord
|
|
282
285
|
# Note that this method removes records from the database ignoring the
|
283
286
|
# +:dependent+ option.
|
284
287
|
def destroy(*records)
|
285
|
-
|
288
|
+
return if records.empty?
|
289
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
286
290
|
delete_or_destroy(records, :destroy)
|
287
291
|
end
|
288
292
|
|
@@ -375,7 +379,10 @@ module ActiveRecord
|
|
375
379
|
if owner.new_record?
|
376
380
|
replace_records(other_array, original_target)
|
377
381
|
else
|
378
|
-
|
382
|
+
replace_common_records_in_memory(other_array, original_target)
|
383
|
+
if other_array != original_target
|
384
|
+
transaction { replace_records(other_array, original_target) }
|
385
|
+
end
|
379
386
|
end
|
380
387
|
end
|
381
388
|
|
@@ -384,7 +391,7 @@ module ActiveRecord
|
|
384
391
|
if record.new_record?
|
385
392
|
include_in_memory?(record)
|
386
393
|
else
|
387
|
-
loaded? ? target.include?(record) : scope.exists?(record)
|
394
|
+
loaded? ? target.include?(record) : scope.exists?(record.id)
|
388
395
|
end
|
389
396
|
else
|
390
397
|
false
|
@@ -400,11 +407,18 @@ module ActiveRecord
|
|
400
407
|
target
|
401
408
|
end
|
402
409
|
|
403
|
-
def add_to_target(record, skip_callbacks = false)
|
410
|
+
def add_to_target(record, skip_callbacks = false, &block)
|
411
|
+
if association_scope.distinct_value
|
412
|
+
index = @target.index(record)
|
413
|
+
end
|
414
|
+
replace_on_target(record, index, skip_callbacks, &block)
|
415
|
+
end
|
416
|
+
|
417
|
+
def replace_on_target(record, index, skip_callbacks)
|
404
418
|
callback(:before_add, record) unless skip_callbacks
|
405
419
|
yield(record) if block_given?
|
406
420
|
|
407
|
-
if
|
421
|
+
if index
|
408
422
|
@target[index] = record
|
409
423
|
else
|
410
424
|
@target << record
|
@@ -427,9 +441,23 @@ module ActiveRecord
|
|
427
441
|
end
|
428
442
|
|
429
443
|
private
|
444
|
+
def get_records
|
445
|
+
return scope.to_a if skip_statement_cache?
|
446
|
+
|
447
|
+
conn = klass.connection
|
448
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
449
|
+
StatementCache.create(conn) { |params|
|
450
|
+
as = AssociationScope.create { params.bind }
|
451
|
+
target_scope.merge as.scope(self, conn)
|
452
|
+
}
|
453
|
+
end
|
454
|
+
|
455
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
456
|
+
sc.execute binds, klass, klass.connection
|
457
|
+
end
|
430
458
|
|
431
459
|
def find_target
|
432
|
-
records =
|
460
|
+
records = get_records
|
433
461
|
records.each { |record| set_inverse_instance(record) }
|
434
462
|
records
|
435
463
|
end
|
@@ -529,6 +557,14 @@ module ActiveRecord
|
|
529
557
|
target
|
530
558
|
end
|
531
559
|
|
560
|
+
def replace_common_records_in_memory(new_target, original_target)
|
561
|
+
common_records = new_target & original_target
|
562
|
+
common_records.each do |record|
|
563
|
+
skip_callbacks = true
|
564
|
+
replace_on_target(record, @target.index(record), skip_callbacks)
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
532
568
|
def concat_records(records, should_raise = false)
|
533
569
|
result = true
|
534
570
|
|
@@ -29,6 +29,7 @@ module ActiveRecord
|
|
29
29
|
# instantiation of the actual post records.
|
30
30
|
class CollectionProxy < Relation
|
31
31
|
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
|
32
|
+
delegate :find_nth, to: :scope
|
32
33
|
|
33
34
|
def initialize(klass, association) #:nodoc:
|
34
35
|
@association = association
|
@@ -359,14 +360,15 @@ module ActiveRecord
|
|
359
360
|
@association.replace(other_array)
|
360
361
|
end
|
361
362
|
|
362
|
-
# Deletes all the records from the collection
|
363
|
-
#
|
364
|
-
#
|
363
|
+
# Deletes all the records from the collection according to the strategy
|
364
|
+
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
365
|
+
# then it will follow the default strategy.
|
365
366
|
#
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
# the default strategy is
|
367
|
+
# For +has_many :through+ associations, the default deletion strategy is
|
368
|
+
# +:delete_all+.
|
369
|
+
#
|
370
|
+
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
371
|
+
# This sets the foreign keys to +NULL+.
|
370
372
|
#
|
371
373
|
# class Person < ActiveRecord::Base
|
372
374
|
# has_many :pets # dependent: :nullify option by default
|
@@ -397,9 +399,9 @@ module ActiveRecord
|
|
397
399
|
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
|
398
400
|
# # ]
|
399
401
|
#
|
400
|
-
#
|
401
|
-
#
|
402
|
-
#
|
402
|
+
# Both +has_many+ and +has_many :through+ dependencies default to the
|
403
|
+
# +:delete_all+ strategy if the +:dependent+ option is set to +:destroy+.
|
404
|
+
# Records are not instantiated and callbacks will not be fired.
|
403
405
|
#
|
404
406
|
# class Person < ActiveRecord::Base
|
405
407
|
# has_many :pets, dependent: :destroy
|
@@ -414,11 +416,6 @@ module ActiveRecord
|
|
414
416
|
# # ]
|
415
417
|
#
|
416
418
|
# person.pets.delete_all
|
417
|
-
# # => [
|
418
|
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
419
|
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
420
|
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
421
|
-
# # ]
|
422
419
|
#
|
423
420
|
# Pet.find(1, 2, 3)
|
424
421
|
# # => ActiveRecord::RecordNotFound
|
@@ -439,11 +436,6 @@ module ActiveRecord
|
|
439
436
|
# # ]
|
440
437
|
#
|
441
438
|
# person.pets.delete_all
|
442
|
-
# # => [
|
443
|
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
|
444
|
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
|
445
|
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
|
446
|
-
# # ]
|
447
439
|
#
|
448
440
|
# Pet.find(1, 2, 3)
|
449
441
|
# # => ActiveRecord::RecordNotFound
|
@@ -452,8 +444,9 @@ module ActiveRecord
|
|
452
444
|
end
|
453
445
|
|
454
446
|
# Deletes the records of the collection directly from the database
|
455
|
-
# ignoring the +:dependent+ option.
|
456
|
-
# +after_remove+ , +before_destroy+ and
|
447
|
+
# ignoring the +:dependent+ option. Records are instantiated and it
|
448
|
+
# invokes +before_remove+, +after_remove+ , +before_destroy+ and
|
449
|
+
# +after_destroy+ callbacks.
|
457
450
|
#
|
458
451
|
# class Person < ActiveRecord::Base
|
459
452
|
# has_many :pets
|
@@ -477,15 +470,16 @@ module ActiveRecord
|
|
477
470
|
@association.destroy_all
|
478
471
|
end
|
479
472
|
|
480
|
-
# Deletes the +records+ supplied
|
481
|
-
#
|
482
|
-
#
|
473
|
+
# Deletes the +records+ supplied from the collection according to the strategy
|
474
|
+
# specified by the +:dependent+ option. If no +:dependent+ option is given,
|
475
|
+
# then it will follow the default strategy. Returns an array with the
|
483
476
|
# deleted records.
|
484
477
|
#
|
485
|
-
#
|
486
|
-
#
|
487
|
-
#
|
488
|
-
# strategy is
|
478
|
+
# For +has_many :through+ associations, the default deletion strategy is
|
479
|
+
# +:delete_all+.
|
480
|
+
#
|
481
|
+
# For +has_many+ associations, the default deletion strategy is +:nullify+.
|
482
|
+
# This sets the foreign keys to +NULL+.
|
489
483
|
#
|
490
484
|
# class Person < ActiveRecord::Base
|
491
485
|
# has_many :pets # dependent: :nullify option by default
|
@@ -568,7 +562,7 @@ module ActiveRecord
|
|
568
562
|
# Pet.find(1)
|
569
563
|
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
|
570
564
|
#
|
571
|
-
# You can pass +
|
565
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
572
566
|
# responding to the +id+ and executes delete on them.
|
573
567
|
#
|
574
568
|
# class Person < ActiveRecord::Base
|
@@ -632,7 +626,7 @@ module ActiveRecord
|
|
632
626
|
#
|
633
627
|
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
|
634
628
|
#
|
635
|
-
# You can pass +
|
629
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
636
630
|
# responding to the +id+ and then deletes them from the database.
|
637
631
|
#
|
638
632
|
# person.pets.size # => 3
|
@@ -792,7 +786,7 @@ module ActiveRecord
|
|
792
786
|
# person.pets.count # => 0
|
793
787
|
# person.pets.any? # => true
|
794
788
|
#
|
795
|
-
# You can also pass a block to define criteria. The behavior
|
789
|
+
# You can also pass a +block+ to define criteria. The behavior
|
796
790
|
# is the same, it returns true if the collection based on the
|
797
791
|
# criteria is not empty.
|
798
792
|
#
|
@@ -826,7 +820,7 @@ module ActiveRecord
|
|
826
820
|
# person.pets.count # => 2
|
827
821
|
# person.pets.many? # => true
|
828
822
|
#
|
829
|
-
# You can also pass a block to define criteria. The
|
823
|
+
# You can also pass a +block+ to define criteria. The
|
830
824
|
# behavior is the same, it returns true if the collection
|
831
825
|
# based on the criteria has more than one record.
|
832
826
|
#
|
@@ -850,7 +844,7 @@ module ActiveRecord
|
|
850
844
|
@association.many?(&block)
|
851
845
|
end
|
852
846
|
|
853
|
-
# Returns +true+ if the given
|
847
|
+
# Returns +true+ if the given +record+ is present in the collection.
|
854
848
|
#
|
855
849
|
# class Person < ActiveRecord::Base
|
856
850
|
# has_many :pets
|
@@ -888,7 +882,7 @@ module ActiveRecord
|
|
888
882
|
|
889
883
|
# Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
|
890
884
|
# contain the same number of elements and if each element is equal
|
891
|
-
# to the corresponding element in the other array, otherwise returns
|
885
|
+
# to the corresponding element in the +other+ array, otherwise returns
|
892
886
|
# +false+.
|
893
887
|
#
|
894
888
|
# class Person < ActiveRecord::Base
|
@@ -6,6 +6,7 @@ module ActiveRecord
|
|
6
6
|
# If the association has a <tt>:through</tt> option further specialization
|
7
7
|
# is provided by its child HasManyThroughAssociation.
|
8
8
|
class HasManyAssociation < CollectionAssociation #:nodoc:
|
9
|
+
include ForeignAssociation
|
9
10
|
|
10
11
|
def handle_dependency
|
11
12
|
case options[:dependent]
|
@@ -41,6 +42,14 @@ module ActiveRecord
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
45
|
+
def empty?
|
46
|
+
if has_cached_counter?
|
47
|
+
size.zero?
|
48
|
+
else
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
44
53
|
private
|
45
54
|
|
46
55
|
# Returns the number of records in this collection.
|
@@ -58,7 +67,7 @@ module ActiveRecord
|
|
58
67
|
# the loaded flag is set to true as well.
|
59
68
|
def count_records
|
60
69
|
count = if has_cached_counter?
|
61
|
-
owner.
|
70
|
+
owner._read_attribute cached_counter_attribute_name
|
62
71
|
else
|
63
72
|
scope.count
|
64
73
|
end
|
@@ -71,20 +80,42 @@ module ActiveRecord
|
|
71
80
|
[association_scope.limit_value, count].compact.min
|
72
81
|
end
|
73
82
|
|
83
|
+
|
84
|
+
# Returns whether a counter cache should be used for this association.
|
85
|
+
#
|
86
|
+
# The counter_cache option must be given on either the owner or inverse
|
87
|
+
# association, and the column must be present on the owner.
|
74
88
|
def has_cached_counter?(reflection = reflection())
|
75
|
-
|
89
|
+
if reflection.options[:counter_cache] || (inverse = inverse_which_updates_counter_cache(reflection)) && inverse.options[:counter_cache]
|
90
|
+
owner.attribute_present?(cached_counter_attribute_name(reflection))
|
91
|
+
end
|
76
92
|
end
|
77
93
|
|
78
94
|
def cached_counter_attribute_name(reflection = reflection())
|
79
|
-
options[:counter_cache]
|
95
|
+
if reflection.options[:counter_cache]
|
96
|
+
reflection.options[:counter_cache].to_s
|
97
|
+
else
|
98
|
+
"#{reflection.name}_count"
|
99
|
+
end
|
80
100
|
end
|
81
101
|
|
82
102
|
def update_counter(difference, reflection = reflection())
|
103
|
+
update_counter_in_database(difference, reflection)
|
104
|
+
update_counter_in_memory(difference, reflection)
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_counter_in_database(difference, reflection = reflection())
|
83
108
|
if has_cached_counter?(reflection)
|
84
109
|
counter = cached_counter_attribute_name(reflection)
|
85
110
|
owner.class.update_counters(owner.id, counter => difference)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def update_counter_in_memory(difference, reflection = reflection())
|
115
|
+
if counter_must_be_updated_by_has_many?(reflection)
|
116
|
+
counter = cached_counter_attribute_name(reflection)
|
86
117
|
owner[counter] += difference
|
87
|
-
owner.
|
118
|
+
owner.send(:clear_attribute_changes, counter) # eww
|
88
119
|
end
|
89
120
|
end
|
90
121
|
|
@@ -98,13 +129,41 @@ module ActiveRecord
|
|
98
129
|
# it will be decremented twice.
|
99
130
|
#
|
100
131
|
# Hence this method.
|
101
|
-
def
|
132
|
+
def inverse_which_updates_counter_cache(reflection = reflection())
|
102
133
|
counter_name = cached_counter_attribute_name(reflection)
|
103
|
-
reflection
|
104
|
-
|
134
|
+
inverse_which_updates_counter_named(counter_name, reflection)
|
135
|
+
end
|
136
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
137
|
+
|
138
|
+
def inverse_which_updates_counter_named(counter_name, reflection)
|
139
|
+
reflection.klass._reflections.values.find { |inverse_reflection|
|
140
|
+
inverse_reflection.belongs_to? &&
|
105
141
|
inverse_reflection.counter_cache_column == counter_name
|
106
142
|
}
|
107
143
|
end
|
144
|
+
alias inverse_updates_counter_named? inverse_which_updates_counter_named
|
145
|
+
|
146
|
+
def inverse_updates_counter_in_memory?(reflection)
|
147
|
+
inverse = inverse_which_updates_counter_cache(reflection)
|
148
|
+
inverse && inverse == reflection.inverse_of
|
149
|
+
end
|
150
|
+
|
151
|
+
def counter_must_be_updated_by_has_many?(reflection)
|
152
|
+
!inverse_updates_counter_in_memory?(reflection) && has_cached_counter?(reflection)
|
153
|
+
end
|
154
|
+
|
155
|
+
def delete_count(method, scope)
|
156
|
+
if method == :delete_all
|
157
|
+
scope.delete_all
|
158
|
+
else
|
159
|
+
scope.update_all(reflection.foreign_key => nil)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def delete_or_nullify_all_records(method)
|
164
|
+
count = delete_count(method, self.scope)
|
165
|
+
update_counter(-count)
|
166
|
+
end
|
108
167
|
|
109
168
|
# Deletes the records according to the <tt>:dependent</tt> option.
|
110
169
|
def delete_records(records, method)
|
@@ -112,26 +171,28 @@ module ActiveRecord
|
|
112
171
|
records.each(&:destroy!)
|
113
172
|
update_counter(-records.length) unless inverse_updates_counter_cache?
|
114
173
|
else
|
115
|
-
|
116
|
-
|
117
|
-
else
|
118
|
-
scope = self.scope.where(reflection.klass.primary_key => records)
|
119
|
-
end
|
120
|
-
|
121
|
-
if method == :delete_all
|
122
|
-
update_counter(-scope.delete_all)
|
123
|
-
else
|
124
|
-
update_counter(-scope.update_all(reflection.foreign_key => nil))
|
125
|
-
end
|
174
|
+
scope = self.scope.where(reflection.klass.primary_key => records)
|
175
|
+
update_counter(-delete_count(method, scope))
|
126
176
|
end
|
127
177
|
end
|
128
178
|
|
129
|
-
def
|
130
|
-
|
131
|
-
|
179
|
+
def concat_records(records, *)
|
180
|
+
update_counter_if_success(super, records.length)
|
181
|
+
end
|
182
|
+
|
183
|
+
def _create_record(attributes, *)
|
184
|
+
if attributes.is_a?(Array)
|
185
|
+
super
|
132
186
|
else
|
133
|
-
|
187
|
+
update_counter_if_success(super, 1)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def update_counter_if_success(saved_successfully, difference)
|
192
|
+
if saved_successfully
|
193
|
+
update_counter_in_memory(difference)
|
134
194
|
end
|
195
|
+
saved_successfully
|
135
196
|
end
|
136
197
|
end
|
137
198
|
end
|