activerecord 4.2.0 → 4.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +657 -1
- data/lib/active_record.rb +3 -0
- data/lib/active_record/aggregations.rb +6 -3
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +5 -4
- data/lib/active_record/associations/association.rb +15 -3
- data/lib/active_record/associations/association_scope.rb +1 -0
- data/lib/active_record/associations/belongs_to_association.rb +13 -5
- data/lib/active_record/associations/builder/association.rb +1 -1
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -4
- data/lib/active_record/associations/collection_association.rb +35 -15
- data/lib/active_record/associations/collection_proxy.rb +15 -9
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +30 -15
- data/lib/active_record/associations/has_many_through_association.rb +11 -2
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency.rb +8 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +7 -1
- data/lib/active_record/associations/preloader.rb +4 -4
- data/lib/active_record/associations/preloader/association.rb +5 -1
- data/lib/active_record/associations/singular_association.rb +2 -8
- data/lib/active_record/associations/through_association.rb +11 -6
- data/lib/active_record/attribute.rb +15 -1
- data/lib/active_record/attribute_assignment.rb +2 -2
- data/lib/active_record/attribute_methods.rb +4 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +5 -0
- data/lib/active_record/attribute_methods/dirty.rb +14 -4
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +5 -1
- data/lib/active_record/attribute_methods/write.rb +1 -1
- data/lib/active_record/attribute_set.rb +4 -0
- data/lib/active_record/attribute_set/builder.rb +32 -12
- data/lib/active_record/attributes.rb +8 -0
- data/lib/active_record/autosave_association.rb +24 -9
- data/lib/active_record/base.rb +4 -5
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +12 -6
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +23 -3
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -16
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +87 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +2 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +25 -7
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -10
- data/lib/active_record/connection_adapters/column.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -21
- data/lib/active_record/connection_adapters/mysql_adapter.rb +10 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +21 -13
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +12 -12
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +12 -28
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +28 -15
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/enum.rb +2 -3
- data/lib/active_record/errors.rb +6 -5
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +9 -7
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +16 -14
- data/lib/active_record/migration.rb +38 -10
- data/lib/active_record/model_schema.rb +4 -2
- data/lib/active_record/nested_attributes.rb +13 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +7 -4
- data/lib/active_record/railtie.rb +5 -3
- data/lib/active_record/railties/databases.rake +17 -24
- data/lib/active_record/reflection.rb +40 -28
- data/lib/active_record/relation.rb +3 -2
- data/lib/active_record/relation/calculations.rb +10 -3
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +4 -16
- data/lib/active_record/relation/merger.rb +24 -1
- data/lib/active_record/relation/predicate_builder.rb +32 -3
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +29 -27
- data/lib/active_record/relation/spawn_methods.rb +7 -3
- data/lib/active_record/schema_dumper.rb +1 -1
- data/lib/active_record/schema_migration.rb +1 -4
- data/lib/active_record/scoping/default.rb +1 -0
- data/lib/active_record/tasks/database_tasks.rb +5 -2
- data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
- data/lib/active_record/transactions.rb +21 -11
- data/lib/active_record/type/boolean.rb +1 -0
- data/lib/active_record/type/date.rb +4 -0
- data/lib/active_record/type/date_time.rb +14 -3
- data/lib/active_record/type/decimal.rb +27 -3
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/integer.rb +9 -5
- data/lib/active_record/type/numeric.rb +1 -1
- data/lib/active_record/type/serialized.rb +7 -1
- data/lib/active_record/type/string.rb +4 -0
- data/lib/active_record/type/value.rb +9 -0
- data/lib/active_record/validations/uniqueness.rb +16 -6
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -6
- metadata +9 -7
data/lib/active_record.rb
CHANGED
@@ -52,6 +52,7 @@ module ActiveRecord
|
|
52
52
|
autoload :QueryCache
|
53
53
|
autoload :Querying
|
54
54
|
autoload :ReadonlyAttributes
|
55
|
+
autoload :RecordInvalid, 'active_record/validations'
|
55
56
|
autoload :Reflection
|
56
57
|
autoload :RuntimeRegistry
|
57
58
|
autoload :Sanitization
|
@@ -78,6 +79,8 @@ module ActiveRecord
|
|
78
79
|
autoload :AttributeMethods
|
79
80
|
autoload :AutosaveAssociation
|
80
81
|
|
82
|
+
autoload :LegacyYamlAdapter
|
83
|
+
|
81
84
|
autoload :Relation
|
82
85
|
autoload :AssociationRelation
|
83
86
|
autoload :NullRelation
|
@@ -244,14 +244,17 @@ module ActiveRecord
|
|
244
244
|
def writer_method(name, class_name, mapping, allow_nil, converter)
|
245
245
|
define_method("#{name}=") do |part|
|
246
246
|
klass = class_name.constantize
|
247
|
-
if part.is_a?(Hash)
|
248
|
-
part = klass.new(*part.values)
|
249
|
-
end
|
250
247
|
|
251
248
|
unless part.is_a?(klass) || converter.nil? || part.nil?
|
252
249
|
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
|
253
250
|
end
|
254
251
|
|
252
|
+
hash_from_multiparameter_assignment = part.is_a?(Hash) &&
|
253
|
+
part.each_key.all? { |k| k.is_a?(Integer) }
|
254
|
+
if hash_from_multiparameter_assignment
|
255
|
+
part = klass.new(*part.values)
|
256
|
+
end
|
257
|
+
|
255
258
|
if part.nil? && allow_nil
|
256
259
|
mapping.each { |key, _| self[key] = nil }
|
257
260
|
@aggregation_cache[name] = nil
|
@@ -13,6 +13,19 @@ module ActiveRecord
|
|
13
13
|
other == to_a
|
14
14
|
end
|
15
15
|
|
16
|
+
def build(*args, &block)
|
17
|
+
scoping { @association.build(*args, &block) }
|
18
|
+
end
|
19
|
+
alias new build
|
20
|
+
|
21
|
+
def create(*args, &block)
|
22
|
+
scoping { @association.create(*args, &block) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def create!(*args, &block)
|
26
|
+
scoping { @association.create!(*args, &block) }
|
27
|
+
end
|
28
|
+
|
16
29
|
private
|
17
30
|
|
18
31
|
def exec_queries
|
@@ -116,6 +116,7 @@ module ActiveRecord
|
|
116
116
|
autoload :Association, 'active_record/associations/association'
|
117
117
|
autoload :SingularAssociation, 'active_record/associations/singular_association'
|
118
118
|
autoload :CollectionAssociation, 'active_record/associations/collection_association'
|
119
|
+
autoload :ForeignAssociation, 'active_record/associations/foreign_association'
|
119
120
|
autoload :CollectionProxy, 'active_record/associations/collection_proxy'
|
120
121
|
|
121
122
|
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
|
@@ -1011,7 +1012,7 @@ module ActiveRecord
|
|
1011
1012
|
# record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
|
1012
1013
|
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
|
1013
1014
|
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
|
1014
|
-
# The default strategy is
|
1015
|
+
# The default strategy is to do nothing (leave the foreign keys with the parent ids set), except for
|
1015
1016
|
# +has_many+ <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
|
1016
1017
|
# the join records, without running their callbacks).
|
1017
1018
|
#
|
@@ -1377,7 +1378,7 @@ module ActiveRecord
|
|
1377
1378
|
# has_one :last_comment, -> { order 'posted_on' }, class_name: "Comment"
|
1378
1379
|
# has_one :project_manager, -> { where role: 'project_manager' }, class_name: "Person"
|
1379
1380
|
# has_one :attachment, as: :attachable
|
1380
|
-
# has_one :boss, readonly
|
1381
|
+
# has_one :boss, -> { readonly }
|
1381
1382
|
# has_one :club, through: :membership
|
1382
1383
|
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
|
1383
1384
|
# has_one :credit_card, required: true
|
@@ -1505,7 +1506,7 @@ module ActiveRecord
|
|
1505
1506
|
# belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
|
1506
1507
|
# class_name: "Coupon", foreign_key: "coupon_id"
|
1507
1508
|
# belongs_to :attachable, polymorphic: true
|
1508
|
-
# belongs_to :project, readonly
|
1509
|
+
# belongs_to :project, -> { readonly }
|
1509
1510
|
# belongs_to :post, counter_cache: true
|
1510
1511
|
# belongs_to :company, touch: true
|
1511
1512
|
# belongs_to :company, touch: :employees_last_updated_at
|
@@ -1712,7 +1713,7 @@ module ActiveRecord
|
|
1712
1713
|
hm_options[:through] = middle_reflection.name
|
1713
1714
|
hm_options[:source] = join_model.right_reflection.name
|
1714
1715
|
|
1715
|
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k|
|
1716
|
+
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name, :extend].each do |k|
|
1716
1717
|
hm_options[k] = options[k] if options.key? k
|
1717
1718
|
end
|
1718
1719
|
|
@@ -211,9 +211,12 @@ module ActiveRecord
|
|
211
211
|
# the kind of the class of the associated objects. Meant to be used as
|
212
212
|
# a sanity check when you are about to assign an associated record.
|
213
213
|
def raise_on_type_mismatch!(record)
|
214
|
-
unless record.is_a?(reflection.klass)
|
215
|
-
|
216
|
-
|
214
|
+
unless record.is_a?(reflection.klass)
|
215
|
+
fresh_class = reflection.class_name.safe_constantize
|
216
|
+
unless fresh_class && record.is_a?(fresh_class)
|
217
|
+
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
218
|
+
raise ActiveRecord::AssociationTypeMismatch, message
|
219
|
+
end
|
217
220
|
end
|
218
221
|
end
|
219
222
|
|
@@ -248,6 +251,15 @@ module ActiveRecord
|
|
248
251
|
initialize_attributes(record)
|
249
252
|
end
|
250
253
|
end
|
254
|
+
|
255
|
+
# Returns true if statement cache should be skipped on the association reader.
|
256
|
+
def skip_statement_cache?
|
257
|
+
reflection.scope_chain.any?(&:any?) ||
|
258
|
+
scope.eager_loading? ||
|
259
|
+
klass.current_scope ||
|
260
|
+
klass.default_scopes.any? ||
|
261
|
+
reflection.source_reflection.active_record.default_scopes.any?
|
262
|
+
end
|
251
263
|
end
|
252
264
|
end
|
253
265
|
end
|
@@ -68,16 +68,19 @@ module ActiveRecord
|
|
68
68
|
def increment_counter(counter_cache_name)
|
69
69
|
if foreign_key_present?
|
70
70
|
klass.increment_counter(counter_cache_name, target_id)
|
71
|
+
if target && !stale_target? && counter_cache_available_in_memory?(counter_cache_name)
|
72
|
+
target.increment(counter_cache_name)
|
73
|
+
end
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
74
77
|
# Checks whether record is different to the current target, without loading it
|
75
78
|
def different_target?(record)
|
76
|
-
record.id != owner
|
79
|
+
record.id != owner._read_attribute(reflection.foreign_key)
|
77
80
|
end
|
78
81
|
|
79
82
|
def replace_keys(record)
|
80
|
-
owner[reflection.foreign_key] = record
|
83
|
+
owner[reflection.foreign_key] = record._read_attribute(reflection.association_primary_key(record.class))
|
81
84
|
end
|
82
85
|
|
83
86
|
def remove_keys
|
@@ -85,7 +88,7 @@ module ActiveRecord
|
|
85
88
|
end
|
86
89
|
|
87
90
|
def foreign_key_present?
|
88
|
-
owner
|
91
|
+
owner._read_attribute(reflection.foreign_key)
|
89
92
|
end
|
90
93
|
|
91
94
|
# NOTE - for now, we're only supporting inverse setting from belongs_to back onto
|
@@ -99,12 +102,17 @@ module ActiveRecord
|
|
99
102
|
if options[:primary_key]
|
100
103
|
owner.send(reflection.name).try(:id)
|
101
104
|
else
|
102
|
-
owner
|
105
|
+
owner._read_attribute(reflection.foreign_key)
|
103
106
|
end
|
104
107
|
end
|
105
108
|
|
106
109
|
def stale_state
|
107
|
-
|
110
|
+
result = owner._read_attribute(reflection.foreign_key)
|
111
|
+
result && result.to_s
|
112
|
+
end
|
113
|
+
|
114
|
+
def counter_cache_available_in_memory?(counter_cache_name)
|
115
|
+
target.respond_to?(counter_cache_name)
|
108
116
|
end
|
109
117
|
end
|
110
118
|
end
|
@@ -21,7 +21,7 @@ module ActiveRecord::Associations::Builder
|
|
21
21
|
end
|
22
22
|
self.extensions = []
|
23
23
|
|
24
|
-
self.valid_options = [:class_name, :
|
24
|
+
self.valid_options = [:class_name, :anonymous_class, :foreign_key, :validate]
|
25
25
|
|
26
26
|
attr_reader :name, :scope, :options
|
27
27
|
|
@@ -82,7 +82,11 @@ module ActiveRecord::Associations::Builder
|
|
82
82
|
|
83
83
|
def wrap_scope(scope, mod)
|
84
84
|
if scope
|
85
|
-
|
85
|
+
if scope.arity > 0
|
86
|
+
proc { |owner| instance_exec(owner, &scope).extending(mod) }
|
87
|
+
else
|
88
|
+
proc { instance_exec(&scope).extending(mod) }
|
89
|
+
end
|
86
90
|
else
|
87
91
|
proc { extending(mod) }
|
88
92
|
end
|
@@ -46,7 +46,7 @@ module ActiveRecord::Associations::Builder
|
|
46
46
|
|
47
47
|
join_model = Class.new(ActiveRecord::Base) {
|
48
48
|
class << self;
|
49
|
-
attr_accessor :
|
49
|
+
attr_accessor :left_model
|
50
50
|
attr_accessor :name
|
51
51
|
attr_accessor :table_name_resolver
|
52
52
|
attr_accessor :left_reflection
|
@@ -58,7 +58,7 @@ module ActiveRecord::Associations::Builder
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def self.compute_type(class_name)
|
61
|
-
|
61
|
+
left_model.compute_type class_name
|
62
62
|
end
|
63
63
|
|
64
64
|
def self.add_left_association(name, options)
|
@@ -72,13 +72,17 @@ module ActiveRecord::Associations::Builder
|
|
72
72
|
self.right_reflection = _reflect_on_association(rhs_name)
|
73
73
|
end
|
74
74
|
|
75
|
+
def self.retrieve_connection
|
76
|
+
left_model.retrieve_connection
|
77
|
+
end
|
78
|
+
|
75
79
|
}
|
76
80
|
|
77
81
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
78
82
|
join_model.table_name_resolver = habtm
|
79
|
-
join_model.
|
83
|
+
join_model.left_model = lhs_model
|
80
84
|
|
81
|
-
join_model.add_left_association :left_side,
|
85
|
+
join_model.add_left_association :left_side, anonymous_class: lhs_model
|
82
86
|
join_model.add_right_association association_name, belongs_to_options(options)
|
83
87
|
join_model
|
84
88
|
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
|
-
|
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
|
@@ -129,6 +140,16 @@ module ActiveRecord
|
|
129
140
|
first_nth_or_last(:last, *args)
|
130
141
|
end
|
131
142
|
|
143
|
+
def take(n = nil)
|
144
|
+
if loaded?
|
145
|
+
n ? target.take(n) : target.first
|
146
|
+
else
|
147
|
+
scope.take(n).tap do |record|
|
148
|
+
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
132
153
|
def build(attributes = {}, &block)
|
133
154
|
if attributes.is_a?(Array)
|
134
155
|
attributes.collect { |attr| build(attr, &block) }
|
@@ -254,7 +275,7 @@ module ActiveRecord
|
|
254
275
|
_options = records.extract_options!
|
255
276
|
dependent = _options[:dependent] || options[:dependent]
|
256
277
|
|
257
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
278
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
258
279
|
delete_or_destroy(records, dependent)
|
259
280
|
end
|
260
281
|
|
@@ -265,7 +286,7 @@ module ActiveRecord
|
|
265
286
|
# +:dependent+ option.
|
266
287
|
def destroy(*records)
|
267
288
|
return if records.empty?
|
268
|
-
records = find(records) if records.any? { |record| record.kind_of?(
|
289
|
+
records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
|
269
290
|
delete_or_destroy(records, :destroy)
|
270
291
|
end
|
271
292
|
|
@@ -421,13 +442,7 @@ module ActiveRecord
|
|
421
442
|
|
422
443
|
private
|
423
444
|
def get_records
|
424
|
-
if
|
425
|
-
scope.eager_loading? ||
|
426
|
-
klass.current_scope ||
|
427
|
-
klass.default_scopes.any?
|
428
|
-
|
429
|
-
return scope.to_a
|
430
|
-
end
|
445
|
+
return scope.to_a if skip_statement_cache?
|
431
446
|
|
432
447
|
conn = klass.connection
|
433
448
|
sc = reflection.association_scope_cache(conn, owner) do
|
@@ -597,8 +612,13 @@ module ActiveRecord
|
|
597
612
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
598
613
|
assoc = owner.association(reflection.through_reflection.name)
|
599
614
|
assoc.reader.any? { |source|
|
600
|
-
|
601
|
-
|
615
|
+
target_association = source.send(reflection.source_reflection.name)
|
616
|
+
|
617
|
+
if target_association.respond_to?(:include?)
|
618
|
+
target_association.include?(record)
|
619
|
+
else
|
620
|
+
target_association == record
|
621
|
+
end
|
602
622
|
} || target.include?(record)
|
603
623
|
else
|
604
624
|
target.include?(record)
|
@@ -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
|
@@ -226,6 +227,10 @@ module ActiveRecord
|
|
226
227
|
@association.last(*args)
|
227
228
|
end
|
228
229
|
|
230
|
+
def take(n = nil)
|
231
|
+
@association.take(n)
|
232
|
+
end
|
233
|
+
|
229
234
|
# Returns a new object of the collection type that has been instantiated
|
230
235
|
# with +attributes+ and linked to this object, but have not yet been saved.
|
231
236
|
# You can pass an array of attributes hashes, this will return an array
|
@@ -465,15 +470,16 @@ module ActiveRecord
|
|
465
470
|
@association.destroy_all
|
466
471
|
end
|
467
472
|
|
468
|
-
# Deletes the +records+ supplied
|
469
|
-
#
|
470
|
-
#
|
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
|
471
476
|
# deleted records.
|
472
477
|
#
|
473
|
-
#
|
474
|
-
#
|
475
|
-
#
|
476
|
-
# 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+.
|
477
483
|
#
|
478
484
|
# class Person < ActiveRecord::Base
|
479
485
|
# has_many :pets # dependent: :nullify option by default
|
@@ -556,7 +562,7 @@ module ActiveRecord
|
|
556
562
|
# Pet.find(1)
|
557
563
|
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
|
558
564
|
#
|
559
|
-
# You can pass +
|
565
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
560
566
|
# responding to the +id+ and executes delete on them.
|
561
567
|
#
|
562
568
|
# class Person < ActiveRecord::Base
|
@@ -620,7 +626,7 @@ module ActiveRecord
|
|
620
626
|
#
|
621
627
|
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
|
622
628
|
#
|
623
|
-
# You can pass +
|
629
|
+
# You can pass +Integer+ or +String+ values, it finds the records
|
624
630
|
# responding to the +id+ and then deletes them from the database.
|
625
631
|
#
|
626
632
|
# person.pets.size # => 3
|
@@ -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]
|
@@ -79,12 +80,23 @@ module ActiveRecord
|
|
79
80
|
[association_scope.limit_value, count].compact.min
|
80
81
|
end
|
81
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.
|
82
88
|
def has_cached_counter?(reflection = reflection())
|
83
|
-
|
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
|
84
92
|
end
|
85
93
|
|
86
94
|
def cached_counter_attribute_name(reflection = reflection())
|
87
|
-
options[:counter_cache]
|
95
|
+
if reflection.options[:counter_cache]
|
96
|
+
reflection.options[:counter_cache].to_s
|
97
|
+
else
|
98
|
+
"#{reflection.name}_count"
|
99
|
+
end
|
88
100
|
end
|
89
101
|
|
90
102
|
def update_counter(difference, reflection = reflection())
|
@@ -100,7 +112,7 @@ module ActiveRecord
|
|
100
112
|
end
|
101
113
|
|
102
114
|
def update_counter_in_memory(difference, reflection = reflection())
|
103
|
-
if
|
115
|
+
if counter_must_be_updated_by_has_many?(reflection)
|
104
116
|
counter = cached_counter_attribute_name(reflection)
|
105
117
|
owner[counter] += difference
|
106
118
|
owner.send(:clear_attribute_changes, counter) # eww
|
@@ -117,17 +129,28 @@ module ActiveRecord
|
|
117
129
|
# it will be decremented twice.
|
118
130
|
#
|
119
131
|
# Hence this method.
|
120
|
-
def
|
132
|
+
def inverse_which_updates_counter_cache(reflection = reflection())
|
121
133
|
counter_name = cached_counter_attribute_name(reflection)
|
122
|
-
|
134
|
+
inverse_which_updates_counter_named(counter_name, reflection)
|
123
135
|
end
|
136
|
+
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
124
137
|
|
125
|
-
def
|
126
|
-
reflection.klass._reflections.values.
|
138
|
+
def inverse_which_updates_counter_named(counter_name, reflection)
|
139
|
+
reflection.klass._reflections.values.find { |inverse_reflection|
|
127
140
|
inverse_reflection.belongs_to? &&
|
128
141
|
inverse_reflection.counter_cache_column == counter_name
|
129
142
|
}
|
130
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
|
131
154
|
|
132
155
|
def delete_count(method, scope)
|
133
156
|
if method == :delete_all
|
@@ -153,14 +176,6 @@ module ActiveRecord
|
|
153
176
|
end
|
154
177
|
end
|
155
178
|
|
156
|
-
def foreign_key_present?
|
157
|
-
if reflection.klass.primary_key
|
158
|
-
owner.attribute_present?(reflection.association_primary_key)
|
159
|
-
else
|
160
|
-
false
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
179
|
def concat_records(records, *)
|
165
180
|
update_counter_if_success(super, records.length)
|
166
181
|
end
|