activerecord 4.0.0.beta1 → 4.0.0.rc1
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 +573 -30
- data/README.rdoc +3 -3
- data/lib/active_record.rb +8 -2
- data/lib/active_record/associations.rb +16 -9
- data/lib/active_record/associations/association.rb +8 -6
- data/lib/active_record/associations/association_scope.rb +2 -1
- data/lib/active_record/associations/belongs_to_association.rb +2 -2
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/belongs_to.rb +37 -5
- data/lib/active_record/associations/collection_association.rb +38 -14
- data/lib/active_record/associations/collection_proxy.rb +18 -15
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +3 -3
- data/lib/active_record/associations/has_many_association.rb +4 -3
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +29 -8
- data/lib/active_record/associations/join_dependency/join_association.rb +26 -6
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +6 -6
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +5 -5
- data/lib/active_record/attribute_methods.rb +20 -5
- data/lib/active_record/attribute_methods/dirty.rb +5 -1
- data/lib/active_record/attribute_methods/primary_key.rb +1 -1
- data/lib/active_record/attribute_methods/serialization.rb +9 -2
- data/lib/active_record/autosave_association.rb +19 -5
- data/lib/active_record/base.rb +3 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +8 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +3 -9
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +2 -8
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +60 -61
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +13 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +291 -153
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -1
- data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -1
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +55 -29
- data/lib/active_record/connection_adapters/column.rb +4 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -3
- data/lib/active_record/connection_adapters/mysql_adapter.rb +5 -5
- data/lib/active_record/connection_adapters/postgresql/cast.rb +22 -2
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +50 -9
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +53 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +35 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +13 -5
- data/lib/active_record/connection_handling.rb +7 -7
- data/lib/active_record/core.rb +43 -8
- data/lib/active_record/counter_cache.rb +2 -1
- data/lib/active_record/errors.rb +11 -10
- data/lib/active_record/explain.rb +9 -7
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +3 -2
- data/lib/active_record/fixture_set/file.rb +1 -2
- data/lib/active_record/fixtures.rb +13 -7
- data/lib/active_record/inheritance.rb +12 -4
- data/lib/active_record/integration.rb +3 -3
- data/lib/active_record/locking/optimistic.rb +2 -2
- data/lib/active_record/log_subscriber.rb +2 -2
- data/lib/active_record/migration.rb +69 -21
- data/lib/active_record/model_schema.rb +1 -1
- data/lib/active_record/nested_attributes.rb +98 -46
- data/lib/active_record/persistence.rb +3 -3
- data/lib/active_record/querying.rb +1 -1
- data/lib/active_record/railtie.rb +18 -4
- data/lib/active_record/railties/console_sandbox.rb +3 -2
- data/lib/active_record/railties/controller_runtime.rb +2 -1
- data/lib/active_record/railties/databases.rake +38 -80
- data/lib/active_record/reflection.rb +36 -3
- data/lib/active_record/relation.rb +18 -8
- data/lib/active_record/relation/calculations.rb +10 -5
- data/lib/active_record/relation/delegation.rb +3 -5
- data/lib/active_record/relation/finder_methods.rb +27 -14
- data/lib/active_record/relation/merger.rb +30 -2
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_methods.rb +113 -16
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/schema_dumper.rb +5 -1
- data/lib/active_record/schema_migration.rb +8 -5
- data/lib/active_record/scoping.rb +56 -2
- data/lib/active_record/scoping/default.rb +12 -11
- data/lib/active_record/scoping/named.rb +7 -3
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/tasks/database_tasks.rb +55 -10
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -2
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/timestamp.rb +6 -0
- data/lib/active_record/transactions.rb +7 -3
- data/lib/active_record/validations.rb +1 -2
- data/lib/active_record/validations/uniqueness.rb +7 -3
- data/lib/active_record/version.rb +7 -6
- data/lib/rails/generators/active_record/migration/migration_generator.rb +9 -2
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -1
- metadata +17 -12
- data/examples/associations.png +0 -0
data/README.rdoc
CHANGED
@@ -130,7 +130,7 @@ A short rundown of some of the major features:
|
|
130
130
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
131
131
|
|
132
132
|
|
133
|
-
* Logging support for Log4r[http://log4r.
|
133
|
+
* Logging support for Log4r[http://log4r.rubyforge.org] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
|
134
134
|
|
135
135
|
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
136
136
|
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
@@ -190,7 +190,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
190
190
|
|
191
191
|
% [sudo] gem install activerecord
|
192
192
|
|
193
|
-
Source code can be downloaded as part of the Rails project on GitHub
|
193
|
+
Source code can be downloaded as part of the Rails project on GitHub:
|
194
194
|
|
195
195
|
* https://github.com/rails/rails/tree/master/activerecord
|
196
196
|
|
@@ -204,7 +204,7 @@ Active Record is released under the MIT license:
|
|
204
204
|
|
205
205
|
== Support
|
206
206
|
|
207
|
-
API documentation is at
|
207
|
+
API documentation is at:
|
208
208
|
|
209
209
|
* http://api.rubyonrails.org
|
210
210
|
|
data/lib/active_record.rb
CHANGED
@@ -35,8 +35,8 @@ module ActiveRecord
|
|
35
35
|
autoload :Base
|
36
36
|
autoload :Callbacks
|
37
37
|
autoload :Core
|
38
|
-
autoload :CounterCache
|
39
38
|
autoload :ConnectionHandling
|
39
|
+
autoload :CounterCache
|
40
40
|
autoload :DynamicMatchers
|
41
41
|
autoload :Explain
|
42
42
|
autoload :Inheritance
|
@@ -50,12 +50,14 @@ module ActiveRecord
|
|
50
50
|
autoload :Querying
|
51
51
|
autoload :ReadonlyAttributes
|
52
52
|
autoload :Reflection
|
53
|
+
autoload :RuntimeRegistry
|
53
54
|
autoload :Sanitization
|
54
55
|
autoload :Schema
|
55
56
|
autoload :SchemaDumper
|
56
57
|
autoload :SchemaMigration
|
57
58
|
autoload :Scoping
|
58
59
|
autoload :Serialization
|
60
|
+
autoload :StatementCache
|
59
61
|
autoload :Store
|
60
62
|
autoload :Timestamp
|
61
63
|
autoload :Transactions
|
@@ -69,8 +71,8 @@ module ActiveRecord
|
|
69
71
|
|
70
72
|
autoload :Aggregations
|
71
73
|
autoload :Associations
|
72
|
-
autoload :AttributeMethods
|
73
74
|
autoload :AttributeAssignment
|
75
|
+
autoload :AttributeMethods
|
74
76
|
autoload :AutosaveAssociation
|
75
77
|
|
76
78
|
autoload :Relation
|
@@ -143,6 +145,10 @@ module ActiveRecord
|
|
143
145
|
autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
|
144
146
|
autoload :PostgreSQLDatabaseTasks,
|
145
147
|
'active_record/tasks/postgresql_database_tasks'
|
148
|
+
|
149
|
+
autoload :FirebirdDatabaseTasks, 'active_record/tasks/firebird_database_tasks'
|
150
|
+
autoload :SqlserverDatabaseTasks, 'active_record/tasks/sqlserver_database_tasks'
|
151
|
+
autoload :OracleDatabaseTasks, 'active_record/tasks/oracle_database_tasks'
|
146
152
|
end
|
147
153
|
|
148
154
|
autoload :TestCase
|
@@ -241,6 +241,7 @@ module ActiveRecord
|
|
241
241
|
# others.destroy_all | X | X | X
|
242
242
|
# others.find(*args) | X | X | X
|
243
243
|
# others.exists? | X | X | X
|
244
|
+
# others.distinct | X | X | X
|
244
245
|
# others.uniq | X | X | X
|
245
246
|
# others.reset | X | X | X
|
246
247
|
#
|
@@ -965,7 +966,7 @@ module ActiveRecord
|
|
965
966
|
# For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
|
966
967
|
# cause the records in the join table to be removed.
|
967
968
|
#
|
968
|
-
# For +has_many+, <tt>destroy</tt> and <tt>
|
969
|
+
# For +has_many+, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
|
969
970
|
# record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
|
970
971
|
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
|
971
972
|
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
|
@@ -987,7 +988,7 @@ module ActiveRecord
|
|
987
988
|
# associated objects themselves. So with +has_and_belongs_to_many+ and +has_many+
|
988
989
|
# <tt>:through</tt>, the join records will be deleted, but the associated records won't.
|
989
990
|
#
|
990
|
-
# This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.
|
991
|
+
# This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt>
|
991
992
|
# you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
|
992
993
|
# to be removed from the database.
|
993
994
|
#
|
@@ -1024,7 +1025,7 @@ module ActiveRecord
|
|
1024
1025
|
# [collection<<(object, ...)]
|
1025
1026
|
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
|
1026
1027
|
# Note that this operation instantly fires update sql without waiting for the save or update call on the
|
1027
|
-
# parent object.
|
1028
|
+
# parent object, unless the parent object is a new record.
|
1028
1029
|
# [collection.delete(object, ...)]
|
1029
1030
|
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
|
1030
1031
|
# Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
|
@@ -1072,6 +1073,9 @@ module ActiveRecord
|
|
1072
1073
|
# with +attributes+, linked to this object through a foreign key, and that has already
|
1073
1074
|
# been saved (if it passed the validation). *Note*: This only works if the base model
|
1074
1075
|
# already exists in the DB, not if it is a new (unsaved) record!
|
1076
|
+
# [collection.create!(attributes = {})]
|
1077
|
+
# Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
|
1078
|
+
# if the record is invalid.
|
1075
1079
|
#
|
1076
1080
|
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
|
1077
1081
|
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
|
@@ -1093,6 +1097,7 @@ module ActiveRecord
|
|
1093
1097
|
# * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
|
1094
1098
|
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
|
1095
1099
|
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
|
1100
|
+
# * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
|
1096
1101
|
# The declaration can also include an options hash to specialize the behavior of the association.
|
1097
1102
|
#
|
1098
1103
|
# === Options
|
@@ -1114,11 +1119,11 @@ module ActiveRecord
|
|
1114
1119
|
# similar callbacks may affect the :dependent behavior, and the
|
1115
1120
|
# :dependent behavior may affect other callbacks.
|
1116
1121
|
#
|
1117
|
-
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed
|
1118
|
-
# * <tt>:delete_all</tt> causes all the
|
1122
|
+
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
|
1123
|
+
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
|
1119
1124
|
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
|
1120
|
-
# * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records
|
1121
|
-
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects
|
1125
|
+
# * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
|
1126
|
+
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
|
1122
1127
|
#
|
1123
1128
|
# If using with the <tt>:through</tt> option, the association on the join model must be
|
1124
1129
|
# a +belongs_to+, and the records which get deleted are the join records, rather than
|
@@ -1231,7 +1236,7 @@ module ActiveRecord
|
|
1231
1236
|
# its owner is destroyed:
|
1232
1237
|
#
|
1233
1238
|
# * <tt>:destroy</tt> causes the associated object to also be destroyed
|
1234
|
-
# * <tt>:delete</tt> causes the
|
1239
|
+
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
|
1235
1240
|
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
|
1236
1241
|
# * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
|
1237
1242
|
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
|
@@ -1407,6 +1412,8 @@ module ActiveRecord
|
|
1407
1412
|
# to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
|
1408
1413
|
# but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
|
1409
1414
|
# custom <tt>:join_table</tt> option if you need to.
|
1415
|
+
# If your tables share a common prefix, it will only appear once at the beginning. For example,
|
1416
|
+
# the tables "catalog_categories" and "catalog_products" generate a join table name of "catalog_categories_products".
|
1410
1417
|
#
|
1411
1418
|
# The join table should not have a primary key or a model associated with it. You must manually generate the
|
1412
1419
|
# join table with a migration such as this:
|
@@ -1433,7 +1440,7 @@ module ActiveRecord
|
|
1433
1440
|
# Adds one or more objects to the collection by creating associations in the join table
|
1434
1441
|
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
|
1435
1442
|
# Note that this operation instantly fires update sql without waiting for the save or update call on the
|
1436
|
-
# parent object.
|
1443
|
+
# parent object, unless the parent object is a new record.
|
1437
1444
|
# [collection.delete(object, ...)]
|
1438
1445
|
# Removes one or more objects from the collection by removing their associations from the join table.
|
1439
1446
|
# This does not destroy the objects.
|
@@ -30,7 +30,7 @@ module ActiveRecord
|
|
30
30
|
reset_scope
|
31
31
|
end
|
32
32
|
|
33
|
-
# Returns the name of the table of the
|
33
|
+
# Returns the name of the table of the associated class:
|
34
34
|
#
|
35
35
|
# post.comments.aliased_table_name # => "comments"
|
36
36
|
#
|
@@ -92,7 +92,7 @@ module ActiveRecord
|
|
92
92
|
# The scope for this association.
|
93
93
|
#
|
94
94
|
# Note that the association_scope is merged into the target_scope only when the
|
95
|
-
#
|
95
|
+
# scope method is called. This is because at that point the call may be surrounded
|
96
96
|
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
|
97
97
|
# actually gets built.
|
98
98
|
def association_scope
|
@@ -113,7 +113,7 @@ module ActiveRecord
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
#
|
116
|
+
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
|
117
117
|
# polymorphic_type field on the owner.
|
118
118
|
def klass
|
119
119
|
reflection.klass
|
@@ -146,7 +146,7 @@ module ActiveRecord
|
|
146
146
|
|
147
147
|
def interpolate(sql, record = nil)
|
148
148
|
if sql.respond_to?(:to_proc)
|
149
|
-
owner.
|
149
|
+
owner.instance_exec(record, &sql)
|
150
150
|
else
|
151
151
|
sql
|
152
152
|
end
|
@@ -203,7 +203,7 @@ module ActiveRecord
|
|
203
203
|
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
|
204
204
|
# the kind of the class of the associated objects. Meant to be used as
|
205
205
|
# a sanity check when you are about to assign an associated record.
|
206
|
-
def raise_on_type_mismatch(record)
|
206
|
+
def raise_on_type_mismatch!(record)
|
207
207
|
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
|
208
208
|
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
|
209
209
|
raise ActiveRecord::AssociationTypeMismatch, message
|
@@ -217,7 +217,8 @@ module ActiveRecord
|
|
217
217
|
reflection.inverse_of
|
218
218
|
end
|
219
219
|
|
220
|
-
#
|
220
|
+
# Returns true if inverse association on the given record needs to be set.
|
221
|
+
# This method is redefined by subclasses.
|
221
222
|
def invertible_for?(record)
|
222
223
|
inverse_reflection_for(record)
|
223
224
|
end
|
@@ -235,6 +236,7 @@ module ActiveRecord
|
|
235
236
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
236
237
|
attributes = create_scope.except(*(record.changed - skip_assign))
|
237
238
|
record.assign_attributes(attributes)
|
239
|
+
set_inverse_instance(record)
|
238
240
|
end
|
239
241
|
end
|
240
242
|
end
|
@@ -22,7 +22,7 @@ module ActiveRecord
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def column_for(table_name, column_name)
|
25
|
-
columns = alias_tracker.connection.schema_cache.columns_hash
|
25
|
+
columns = alias_tracker.connection.schema_cache.columns_hash(table_name)
|
26
26
|
columns[column_name]
|
27
27
|
end
|
28
28
|
|
@@ -101,6 +101,7 @@ module ActiveRecord
|
|
101
101
|
|
102
102
|
scope.includes! item.includes_values
|
103
103
|
scope.where_values += item.where_values
|
104
|
+
scope.order_values |= item.order_values
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record Belongs To
|
2
|
+
# = Active Record Belongs To Association
|
3
3
|
module Associations
|
4
4
|
class BelongsToAssociation < SingularAssociation #:nodoc:
|
5
5
|
|
@@ -8,7 +8,7 @@ module ActiveRecord
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def replace(record)
|
11
|
-
raise_on_type_mismatch(record) if record
|
11
|
+
raise_on_type_mismatch!(record) if record
|
12
12
|
|
13
13
|
update_counters(record)
|
14
14
|
replace_keys(record)
|
@@ -21,23 +21,44 @@ module ActiveRecord::Associations::Builder
|
|
21
21
|
|
22
22
|
def add_counter_cache_callbacks(reflection)
|
23
23
|
cache_column = reflection.counter_cache_column
|
24
|
+
foreign_key = reflection.foreign_key
|
24
25
|
|
25
26
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
26
27
|
def belongs_to_counter_cache_after_create_for_#{name}
|
27
|
-
record = #{name}
|
28
|
-
|
28
|
+
if record = #{name}
|
29
|
+
record.class.increment_counter(:#{cache_column}, record.id)
|
30
|
+
@_after_create_counter_called = true
|
31
|
+
end
|
29
32
|
end
|
30
33
|
|
31
34
|
def belongs_to_counter_cache_before_destroy_for_#{name}
|
32
|
-
unless
|
35
|
+
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == #{foreign_key.to_sym.inspect}
|
33
36
|
record = #{name}
|
34
37
|
record.class.decrement_counter(:#{cache_column}, record.id) unless record.nil?
|
35
38
|
end
|
36
39
|
end
|
40
|
+
|
41
|
+
def belongs_to_counter_cache_after_update_for_#{name}
|
42
|
+
if (@_after_create_counter_called ||= false)
|
43
|
+
@_after_create_counter_called = false
|
44
|
+
elsif self.#{foreign_key}_changed? && !new_record? && defined?(#{name.to_s.camelize})
|
45
|
+
model = #{name.to_s.camelize}
|
46
|
+
foreign_key_was = self.#{foreign_key}_was
|
47
|
+
foreign_key = self.#{foreign_key}
|
48
|
+
|
49
|
+
if foreign_key && model.respond_to?(:increment_counter)
|
50
|
+
model.increment_counter(:#{cache_column}, foreign_key)
|
51
|
+
end
|
52
|
+
if foreign_key_was && model.respond_to?(:decrement_counter)
|
53
|
+
model.decrement_counter(:#{cache_column}, foreign_key_was)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
37
57
|
CODE
|
38
58
|
|
39
59
|
model.after_create "belongs_to_counter_cache_after_create_for_#{name}"
|
40
60
|
model.before_destroy "belongs_to_counter_cache_before_destroy_for_#{name}"
|
61
|
+
model.after_update "belongs_to_counter_cache_after_update_for_#{name}"
|
41
62
|
|
42
63
|
klass = reflection.class_name.safe_constantize
|
43
64
|
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
|
@@ -46,9 +67,20 @@ module ActiveRecord::Associations::Builder
|
|
46
67
|
def add_touch_callbacks(reflection)
|
47
68
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
48
69
|
def belongs_to_touch_after_save_or_destroy_for_#{name}
|
49
|
-
|
70
|
+
foreign_key_field = #{reflection.foreign_key.inspect}
|
71
|
+
old_foreign_id = attribute_was(foreign_key_field)
|
72
|
+
|
73
|
+
if old_foreign_id
|
74
|
+
klass = association(#{name.inspect}).klass
|
75
|
+
old_record = klass.find_by(klass.primary_key => old_foreign_id)
|
50
76
|
|
51
|
-
|
77
|
+
if old_record
|
78
|
+
old_record.touch #{options[:touch].inspect if options[:touch] != true}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
record = #{name}
|
83
|
+
unless record.nil? || record.new_record?
|
52
84
|
record.touch #{options[:touch].inspect if options[:touch] != true}
|
53
85
|
end
|
54
86
|
end
|
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
reload
|
35
35
|
end
|
36
36
|
|
37
|
-
CollectionProxy.new(klass, self)
|
37
|
+
@proxy ||= CollectionProxy.new(klass, self)
|
38
38
|
end
|
39
39
|
|
40
40
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -81,6 +81,18 @@ module ActiveRecord
|
|
81
81
|
else
|
82
82
|
if options[:finder_sql]
|
83
83
|
find_by_scan(*args)
|
84
|
+
elsif options[:inverse_of]
|
85
|
+
args = args.flatten
|
86
|
+
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args.blank?
|
87
|
+
|
88
|
+
result = find_by_scan(*args)
|
89
|
+
|
90
|
+
result_size = Array(result).size
|
91
|
+
if !result || result_size != args.size
|
92
|
+
scope.raise_record_not_found_exception!(args, result_size, args.size)
|
93
|
+
else
|
94
|
+
result
|
95
|
+
end
|
84
96
|
else
|
85
97
|
scope.find(*args)
|
86
98
|
end
|
@@ -174,13 +186,14 @@ module ActiveRecord
|
|
174
186
|
|
175
187
|
reflection.klass.count_by_sql(custom_counter_sql)
|
176
188
|
else
|
177
|
-
|
189
|
+
relation = scope
|
190
|
+
if association_scope.distinct_value
|
178
191
|
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
|
179
192
|
column_name ||= reflection.klass.primary_key
|
180
|
-
|
193
|
+
relation = relation.distinct
|
181
194
|
end
|
182
195
|
|
183
|
-
value =
|
196
|
+
value = relation.count(column_name)
|
184
197
|
|
185
198
|
limit = options[:limit]
|
186
199
|
offset = options[:offset]
|
@@ -204,6 +217,15 @@ module ActiveRecord
|
|
204
217
|
dependent = options[:dependent]
|
205
218
|
|
206
219
|
if records.first == :all
|
220
|
+
|
221
|
+
if dependent && dependent == :destroy
|
222
|
+
message = 'In Rails 4.1 delete_all on associations would not fire callbacks. ' \
|
223
|
+
'It means if the :dependent option is :destroy then the associated ' \
|
224
|
+
'records would be deleted without loading and invoking callbacks.'
|
225
|
+
|
226
|
+
ActiveRecord::Base.logger ? ActiveRecord::Base.logger.warn(message) : $stderr.puts(message)
|
227
|
+
end
|
228
|
+
|
207
229
|
if loaded? || dependent == :destroy
|
208
230
|
delete_or_destroy(load_target, dependent)
|
209
231
|
else
|
@@ -237,14 +259,14 @@ module ActiveRecord
|
|
237
259
|
# +count_records+, which is a method descendants have to provide.
|
238
260
|
def size
|
239
261
|
if !find_target? || loaded?
|
240
|
-
if association_scope.
|
262
|
+
if association_scope.distinct_value
|
241
263
|
target.uniq.size
|
242
264
|
else
|
243
265
|
target.size
|
244
266
|
end
|
245
267
|
elsif !loaded? && !association_scope.group_values.empty?
|
246
268
|
load_target.size
|
247
|
-
elsif !loaded? && !association_scope.
|
269
|
+
elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
|
248
270
|
unsaved_records = target.select { |r| r.new_record? }
|
249
271
|
unsaved_records.size + count_records
|
250
272
|
else
|
@@ -297,17 +319,18 @@ module ActiveRecord
|
|
297
319
|
end
|
298
320
|
end
|
299
321
|
|
300
|
-
def
|
322
|
+
def distinct
|
301
323
|
seen = {}
|
302
324
|
load_target.find_all do |record|
|
303
325
|
seen[record.id] = true unless seen.key?(record.id)
|
304
326
|
end
|
305
327
|
end
|
328
|
+
alias uniq distinct
|
306
329
|
|
307
330
|
# Replace this collection with +other_array+. This will perform a diff
|
308
331
|
# and delete/add only records that have changed.
|
309
332
|
def replace(other_array)
|
310
|
-
other_array.each { |val| raise_on_type_mismatch(val) }
|
333
|
+
other_array.each { |val| raise_on_type_mismatch!(val) }
|
311
334
|
original_target = load_target.dup
|
312
335
|
|
313
336
|
if owner.new_record?
|
@@ -343,7 +366,7 @@ module ActiveRecord
|
|
343
366
|
callback(:before_add, record)
|
344
367
|
yield(record) if block_given?
|
345
368
|
|
346
|
-
if association_scope.
|
369
|
+
if association_scope.distinct_value && index = @target.index(record)
|
347
370
|
@target[index] = record
|
348
371
|
else
|
349
372
|
@target << record
|
@@ -454,7 +477,7 @@ module ActiveRecord
|
|
454
477
|
|
455
478
|
def delete_or_destroy(records, method)
|
456
479
|
records = records.flatten
|
457
|
-
records.each { |record| raise_on_type_mismatch(record) }
|
480
|
+
records.each { |record| raise_on_type_mismatch!(record) }
|
458
481
|
existing_records = records.reject { |r| r.new_record? }
|
459
482
|
|
460
483
|
if existing_records.empty?
|
@@ -495,9 +518,9 @@ module ActiveRecord
|
|
495
518
|
result = true
|
496
519
|
|
497
520
|
records.flatten.each do |record|
|
498
|
-
raise_on_type_mismatch(record)
|
499
|
-
add_to_target(record) do |
|
500
|
-
result &&= insert_record(
|
521
|
+
raise_on_type_mismatch!(record)
|
522
|
+
add_to_target(record) do |rec|
|
523
|
+
result &&= insert_record(rec) unless owner.new_record?
|
501
524
|
end
|
502
525
|
end
|
503
526
|
|
@@ -556,7 +579,8 @@ module ActiveRecord
|
|
556
579
|
end
|
557
580
|
end
|
558
581
|
|
559
|
-
# If using a custom finder_sql
|
582
|
+
# If using a custom finder_sql or if the :inverse_of option has been
|
583
|
+
# specified, then #find scans the entire collection.
|
560
584
|
def find_by_scan(*args)
|
561
585
|
expects_array = args.first.kind_of?(Array)
|
562
586
|
ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
|