activerecord 6.1.3.2 → 7.0.0.alpha2
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 +734 -1058
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +35 -7
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +16 -6
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +24 -25
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/preloader/association.rb +161 -49
- data/lib/active_record/associations/preloader/batch.rb +51 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +37 -11
- data/lib/active_record/associations/preloader.rb +46 -110
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +76 -81
- data/lib/active_record/asynchronous_queries_tracker.rb +57 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +41 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +6 -9
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +3 -18
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +11 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +31 -558
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +1 -3
- data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
- data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +8 -5
- data/lib/active_record/connection_handling.rb +20 -38
- data/lib/active_record/core.rb +129 -117
- data/lib/active_record/database_configurations/database_config.rb +12 -0
- data/lib/active_record/database_configurations/hash_config.rb +27 -1
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +18 -9
- data/lib/active_record/delegated_type.rb +33 -11
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +61 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +208 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +80 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +44 -46
- data/lib/active_record/errors.rb +66 -3
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +40 -5
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +16 -11
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +39 -6
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +3 -5
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/log_subscriber.rb +6 -2
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +46 -32
- data/lib/active_record/nested_attributes.rb +3 -3
- data/lib/active_record/no_touching.rb +2 -2
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +134 -45
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +203 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +117 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +83 -58
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +45 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +42 -25
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +32 -23
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +233 -50
- data/lib/active_record/relation/record_fetch_warning.rb +2 -2
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +22 -15
- data/lib/active_record/relation.rb +170 -87
- data/lib/active_record/result.rb +17 -2
- data/lib/active_record/runtime_registry.rb +2 -4
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +3 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +62 -15
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +40 -22
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/tasks/database_tasks.rb +107 -23
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +45 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +1 -1
- data/lib/active_record.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/composite.rb +3 -3
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +18 -22
- data/lib/arel/delete_manager.rb +2 -4
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +8 -13
- data/lib/arel/nodes/homogeneous_in.rb +4 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +3 -2
- data/lib/arel/predications.rb +3 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +2 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +6 -1
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +44 -3
- data/lib/arel.rb +1 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- metadata +55 -16
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -140,7 +140,7 @@ This would also define the following accessors: <tt>Product#name</tt> and
|
|
140
140
|
|
141
141
|
* Database agnostic schema management with Migrations.
|
142
142
|
|
143
|
-
class AddSystemSettings < ActiveRecord::Migration[
|
143
|
+
class AddSystemSettings < ActiveRecord::Migration[7.0]
|
144
144
|
def up
|
145
145
|
create_table :system_settings do |t|
|
146
146
|
t.string :name
|
@@ -264,7 +264,7 @@ module ActiveRecord
|
|
264
264
|
end
|
265
265
|
|
266
266
|
hash_from_multiparameter_assignment = part.is_a?(Hash) &&
|
267
|
-
part.
|
267
|
+
part.keys.all?(Integer)
|
268
268
|
if hash_from_multiparameter_assignment
|
269
269
|
raise ArgumentError unless part.size == part.each_key.max
|
270
270
|
part = klass.new(*part.sort.map(&:last))
|
@@ -27,16 +27,6 @@ module ActiveRecord
|
|
27
27
|
RUBY
|
28
28
|
end
|
29
29
|
|
30
|
-
def build(attributes = nil, &block)
|
31
|
-
if attributes.is_a?(Array)
|
32
|
-
attributes.collect { |attr| build(attr, &block) }
|
33
|
-
else
|
34
|
-
block = current_scope_restoring_block(&block)
|
35
|
-
scoping { _new(attributes, &block) }
|
36
|
-
end
|
37
|
-
end
|
38
|
-
alias new build
|
39
|
-
|
40
30
|
private
|
41
31
|
def _new(attributes, &block)
|
42
32
|
@association.build(attributes, &block)
|
@@ -32,8 +32,8 @@ module ActiveRecord
|
|
32
32
|
# The association of <tt>blog.posts</tt> has the object +blog+ as its
|
33
33
|
# <tt>owner</tt>, the collection of its posts as <tt>target</tt>, and
|
34
34
|
# the <tt>reflection</tt> object represents a <tt>:has_many</tt> macro.
|
35
|
-
class Association
|
36
|
-
attr_reader :owner, :target, :reflection
|
35
|
+
class Association # :nodoc:
|
36
|
+
attr_reader :owner, :target, :reflection, :disable_joins
|
37
37
|
|
38
38
|
delegate :options, to: :reflection
|
39
39
|
|
@@ -41,6 +41,7 @@ module ActiveRecord
|
|
41
41
|
reflection.check_validity!
|
42
42
|
|
43
43
|
@owner, @reflection = owner, reflection
|
44
|
+
@disable_joins = @reflection.options[:disable_joins] || false
|
44
45
|
|
45
46
|
reset
|
46
47
|
reset_scope
|
@@ -97,8 +98,12 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
|
99
100
|
def scope
|
100
|
-
if
|
101
|
+
if disable_joins
|
102
|
+
DisableJoinsAssociationScope.create.scope(self)
|
103
|
+
elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self
|
101
104
|
scope.spawn
|
105
|
+
elsif scope = klass.global_current_scope
|
106
|
+
target_scope.merge!(association_scope).merge!(scope)
|
102
107
|
else
|
103
108
|
target_scope.merge!(association_scope)
|
104
109
|
end
|
@@ -191,7 +196,7 @@ module ActiveRecord
|
|
191
196
|
@reflection = @owner.class._reflect_on_association(reflection_name)
|
192
197
|
end
|
193
198
|
|
194
|
-
def initialize_attributes(record, except_from_scope_attributes = nil)
|
199
|
+
def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:
|
195
200
|
except_from_scope_attributes ||= {}
|
196
201
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
197
202
|
assigned_keys = record.changed_attribute_names_to_save
|
@@ -210,8 +215,14 @@ module ActiveRecord
|
|
210
215
|
end
|
211
216
|
|
212
217
|
private
|
218
|
+
# Reader and writer methods call this so that consistent errors are presented
|
219
|
+
# when the association target class does not exist.
|
220
|
+
def ensure_klass_exists!
|
221
|
+
klass
|
222
|
+
end
|
223
|
+
|
213
224
|
def find_target
|
214
|
-
if
|
225
|
+
if violates_strict_loading? && owner.validation_context.nil?
|
215
226
|
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
216
227
|
end
|
217
228
|
|
@@ -224,7 +235,20 @@ module ActiveRecord
|
|
224
235
|
end
|
225
236
|
|
226
237
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
227
|
-
sc.execute(binds, klass.connection)
|
238
|
+
sc.execute(binds, klass.connection) do |record|
|
239
|
+
set_inverse_instance(record)
|
240
|
+
if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
|
241
|
+
record.strict_loading!
|
242
|
+
else
|
243
|
+
record.strict_loading!(false, mode: owner.strict_loading_mode)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def violates_strict_loading?
|
249
|
+
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
250
|
+
|
251
|
+
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
|
228
252
|
end
|
229
253
|
|
230
254
|
# The scope for this association.
|
@@ -235,7 +259,11 @@ module ActiveRecord
|
|
235
259
|
# actually gets built.
|
236
260
|
def association_scope
|
237
261
|
if klass
|
238
|
-
@association_scope ||=
|
262
|
+
@association_scope ||= if disable_joins
|
263
|
+
DisableJoinsAssociationScope.scope(self)
|
264
|
+
else
|
265
|
+
AssociationScope.scope(self)
|
266
|
+
end
|
239
267
|
end
|
240
268
|
end
|
241
269
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
-
class AssociationScope
|
5
|
+
class AssociationScope # :nodoc:
|
6
6
|
def self.scope(association)
|
7
7
|
INSTANCE.scope(association)
|
8
8
|
end
|
@@ -123,8 +123,6 @@ module ActiveRecord
|
|
123
123
|
|
124
124
|
chain_head = chain.first
|
125
125
|
chain.reverse_each do |reflection|
|
126
|
-
# Exclude the scope of the association itself, because that
|
127
|
-
# was already merged in the #scope method.
|
128
126
|
reflection.constraints.each do |scope_chain_item|
|
129
127
|
item = eval_scope(reflection, scope_chain_item, owner)
|
130
128
|
|
@@ -3,14 +3,13 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Belongs To Association
|
6
|
-
class BelongsToAssociation < SingularAssociation
|
6
|
+
class BelongsToAssociation < SingularAssociation # :nodoc:
|
7
7
|
def handle_dependency
|
8
8
|
return unless load_target
|
9
9
|
|
10
10
|
case options[:dependent]
|
11
11
|
when :destroy
|
12
|
-
target.destroy
|
13
|
-
raise ActiveRecord::Rollback unless target.destroyed?
|
12
|
+
raise ActiveRecord::Rollback unless target.destroy
|
14
13
|
when :destroy_async
|
15
14
|
id = owner.public_send(reflection.foreign_key.to_sym)
|
16
15
|
primary_key_column = reflection.active_record_primary_key.to_sym
|
@@ -56,7 +55,8 @@ module ActiveRecord
|
|
56
55
|
|
57
56
|
def decrement_counters_before_last_save
|
58
57
|
if reflection.polymorphic?
|
59
|
-
|
58
|
+
model_type_was = owner.attribute_before_last_save(reflection.foreign_type)
|
59
|
+
model_was = owner.class.polymorphic_class_for(model_type_was) if model_type_was
|
60
60
|
else
|
61
61
|
model_was = klass
|
62
62
|
end
|
@@ -69,6 +69,14 @@ module ActiveRecord
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def target_changed?
|
72
|
+
owner.attribute_changed?(reflection.foreign_key) || (!foreign_key_present? && target&.new_record?)
|
73
|
+
end
|
74
|
+
|
75
|
+
def target_previously_changed?
|
76
|
+
owner.attribute_previously_changed?(reflection.foreign_key)
|
77
|
+
end
|
78
|
+
|
79
|
+
def saved_change_to_target?
|
72
80
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
73
81
|
end
|
74
82
|
|
@@ -78,6 +86,8 @@ module ActiveRecord
|
|
78
86
|
raise_on_type_mismatch!(record)
|
79
87
|
set_inverse_instance(record)
|
80
88
|
@updated = true
|
89
|
+
elsif target
|
90
|
+
remove_inverse_instance(target)
|
81
91
|
end
|
82
92
|
|
83
93
|
replace_keys(record, force: true)
|
@@ -111,7 +121,7 @@ module ActiveRecord
|
|
111
121
|
def replace_keys(record, force: false)
|
112
122
|
target_key = record ? record._read_attribute(primary_key(record.class)) : nil
|
113
123
|
|
114
|
-
if force || owner
|
124
|
+
if force || owner._read_attribute(reflection.foreign_key) != target_key
|
115
125
|
owner[reflection.foreign_key] = target_key
|
116
126
|
end
|
117
127
|
end
|
@@ -126,7 +136,7 @@ module ActiveRecord
|
|
126
136
|
|
127
137
|
def invertible_for?(record)
|
128
138
|
inverse = inverse_reflection_for(record)
|
129
|
-
inverse && (inverse.has_one? ||
|
139
|
+
inverse && (inverse.has_one? || inverse.klass.has_many_inversing)
|
130
140
|
end
|
131
141
|
|
132
142
|
def stale_state
|
@@ -3,13 +3,21 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Belongs To Polymorphic Association
|
6
|
-
class BelongsToPolymorphicAssociation < BelongsToAssociation
|
6
|
+
class BelongsToPolymorphicAssociation < BelongsToAssociation # :nodoc:
|
7
7
|
def klass
|
8
8
|
type = owner[reflection.foreign_type]
|
9
9
|
type.presence && owner.class.polymorphic_class_for(type)
|
10
10
|
end
|
11
11
|
|
12
12
|
def target_changed?
|
13
|
+
super || owner.attribute_changed?(reflection.foreign_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def target_previously_changed?
|
17
|
+
super || owner.attribute_previously_changed?(reflection.foreign_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def saved_change_to_target?
|
13
21
|
super || owner.saved_change_to_attribute?(reflection.foreign_type)
|
14
22
|
end
|
15
23
|
|
@@ -19,7 +27,7 @@ module ActiveRecord
|
|
19
27
|
|
20
28
|
target_type = record ? record.class.polymorphic_name : nil
|
21
29
|
|
22
|
-
if force || owner
|
30
|
+
if force || owner._read_attribute(reflection.foreign_type) != target_type
|
23
31
|
owner[reflection.foreign_type] = target_type
|
24
32
|
end
|
25
33
|
end
|
@@ -12,7 +12,7 @@
|
|
12
12
|
# - HasManyAssociation
|
13
13
|
|
14
14
|
module ActiveRecord::Associations::Builder # :nodoc:
|
15
|
-
class Association
|
15
|
+
class Association # :nodoc:
|
16
16
|
class << self
|
17
17
|
attr_accessor :extensions
|
18
18
|
end
|
@@ -33,6 +33,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
33
33
|
define_accessors model, reflection
|
34
34
|
define_callbacks model, reflection
|
35
35
|
define_validations model, reflection
|
36
|
+
define_change_tracking_methods model, reflection
|
36
37
|
reflection
|
37
38
|
end
|
38
39
|
|
@@ -117,6 +118,10 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
117
118
|
# noop
|
118
119
|
end
|
119
120
|
|
121
|
+
def self.define_change_tracking_methods(model, reflection)
|
122
|
+
# noop
|
123
|
+
end
|
124
|
+
|
120
125
|
def self.valid_dependent_options
|
121
126
|
raise NotImplementedError
|
122
127
|
end
|
@@ -158,6 +163,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
158
163
|
|
159
164
|
private_class_method :build_scope, :macro, :valid_options, :validate_options, :define_extensions,
|
160
165
|
:define_callbacks, :define_accessors, :define_readers, :define_writers, :define_validations,
|
161
|
-
:
|
166
|
+
:define_change_tracking_methods, :valid_dependent_options, :check_dependent_options,
|
167
|
+
:add_destroy_callbacks, :add_after_commit_jobs_callback
|
162
168
|
end
|
163
169
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class BelongsTo < SingularAssociation
|
4
|
+
class BelongsTo < SingularAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:belongs_to
|
7
7
|
end
|
@@ -30,7 +30,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
30
|
model.after_update lambda { |record|
|
31
31
|
association = association(reflection.name)
|
32
32
|
|
33
|
-
if association.
|
33
|
+
if association.saved_change_to_target?
|
34
34
|
association.increment_counters
|
35
35
|
association.decrement_counters_before_last_save
|
36
36
|
end
|
@@ -49,7 +49,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
49
49
|
if reflection.polymorphic?
|
50
50
|
foreign_type = reflection.foreign_type
|
51
51
|
klass = changes[foreign_type] && changes[foreign_type].first || o.public_send(foreign_type)
|
52
|
-
klass = klass
|
52
|
+
klass = o.class.polymorphic_class_for(klass)
|
53
53
|
else
|
54
54
|
klass = association.klass
|
55
55
|
end
|
@@ -87,7 +87,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
87
87
|
if reflection.counter_cache_column
|
88
88
|
touch_callback = callback.(:saved_changes)
|
89
89
|
update_callback = lambda { |record|
|
90
|
-
instance_exec(record, &touch_callback) unless association(reflection.name).
|
90
|
+
instance_exec(record, &touch_callback) unless association(reflection.name).saved_change_to_target?
|
91
91
|
}
|
92
92
|
model.after_update update_callback, if: :saved_changes?
|
93
93
|
else
|
@@ -127,7 +127,20 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
|
131
|
-
|
130
|
+
def self.define_change_tracking_methods(model, reflection)
|
131
|
+
model.generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
132
|
+
def #{reflection.name}_changed?
|
133
|
+
association(:#{reflection.name}).target_changed?
|
134
|
+
end
|
135
|
+
|
136
|
+
def #{reflection.name}_previously_changed?
|
137
|
+
association(:#{reflection.name}).target_previously_changed?
|
138
|
+
end
|
139
|
+
CODE
|
140
|
+
end
|
141
|
+
|
142
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks,
|
143
|
+
:define_validations, :define_change_tracking_methods, :add_counter_cache_callbacks,
|
144
|
+
:add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
|
132
145
|
end
|
133
146
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require "active_record/associations"
|
4
4
|
|
5
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
6
|
-
class CollectionAssociation < Association
|
6
|
+
class CollectionAssociation < Association # :nodoc:
|
7
7
|
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
@@ -1,16 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class HasMany < CollectionAssociation
|
4
|
+
class HasMany < CollectionAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:has_many
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.valid_options(options)
|
10
|
-
valid = super + [:counter_cache, :join_table, :index_errors
|
10
|
+
valid = super + [:counter_cache, :join_table, :index_errors]
|
11
11
|
valid += [:as, :foreign_type] if options[:as]
|
12
12
|
valid += [:through, :source, :source_type] if options[:through]
|
13
13
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
14
|
+
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
14
15
|
valid
|
15
16
|
end
|
16
17
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord::Associations::Builder # :nodoc:
|
4
|
-
class HasOne < SingularAssociation
|
4
|
+
class HasOne < SingularAssociation # :nodoc:
|
5
5
|
def self.macro
|
6
6
|
:has_one
|
7
7
|
end
|
@@ -11,6 +11,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
11
11
|
valid += [:as, :foreign_type] if options[:as]
|
12
12
|
valid += [:ensuring_owner_was] if options[:dependent] == :destroy_async
|
13
13
|
valid += [:through, :source, :source_type] if options[:through]
|
14
|
+
valid += [:disable_joins] if options[:disable_joins] && options[:through]
|
14
15
|
valid
|
15
16
|
end
|
16
17
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# This class is inherited by the has_one and belongs_to association classes
|
4
4
|
|
5
5
|
module ActiveRecord::Associations::Builder # :nodoc:
|
6
|
-
class SingularAssociation < Association
|
6
|
+
class SingularAssociation < Association # :nodoc:
|
7
7
|
def self.valid_options(options)
|
8
8
|
super + [:required, :touch]
|
9
9
|
end
|
@@ -13,7 +13,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
13
13
|
mixin = model.generated_association_methods
|
14
14
|
name = reflection.name
|
15
15
|
|
16
|
-
define_constructors(mixin, name)
|
16
|
+
define_constructors(mixin, name) unless reflection.polymorphic?
|
17
17
|
|
18
18
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
19
19
|
def reload_#{name}
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Associations
|
5
7
|
# = Active Record Association Collection
|
@@ -25,9 +27,11 @@ module ActiveRecord
|
|
25
27
|
#
|
26
28
|
# If you need to work on all current children, new and existing records,
|
27
29
|
# +load_target+ and the +loaded+ flag are your friends.
|
28
|
-
class CollectionAssociation < Association
|
30
|
+
class CollectionAssociation < Association # :nodoc:
|
29
31
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
30
32
|
def reader
|
33
|
+
ensure_klass_exists!
|
34
|
+
|
31
35
|
if stale_target?
|
32
36
|
reload
|
33
37
|
end
|
@@ -75,6 +79,7 @@ module ActiveRecord
|
|
75
79
|
def reset
|
76
80
|
super
|
77
81
|
@target = []
|
82
|
+
@replaced_or_added_targets = Set.new
|
78
83
|
@association_ids = nil
|
79
84
|
end
|
80
85
|
|
@@ -121,21 +126,6 @@ module ActiveRecord
|
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
124
|
-
# Starts a transaction in the association class's database connection.
|
125
|
-
#
|
126
|
-
# class Author < ActiveRecord::Base
|
127
|
-
# has_many :books
|
128
|
-
# end
|
129
|
-
#
|
130
|
-
# Author.first.books.transaction do
|
131
|
-
# # same effect as calling Book.transaction
|
132
|
-
# end
|
133
|
-
def transaction(*args)
|
134
|
-
reflection.klass.transaction(*args) do
|
135
|
-
yield
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
129
|
# Removes all records from the association without calling callbacks
|
140
130
|
# on the associated records. It honors the +:dependent+ option. However
|
141
131
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
@@ -279,20 +269,19 @@ module ActiveRecord
|
|
279
269
|
end
|
280
270
|
|
281
271
|
def add_to_target(record, skip_callbacks: false, replace: false, &block)
|
282
|
-
|
283
|
-
index = @target.index(record)
|
284
|
-
end
|
285
|
-
replace_on_target(record, index, skip_callbacks, &block)
|
272
|
+
replace_on_target(record, skip_callbacks, replace: replace || association_scope.distinct_value, &block)
|
286
273
|
end
|
287
274
|
|
288
275
|
def target=(record)
|
289
|
-
return super unless
|
276
|
+
return super unless reflection.klass.has_many_inversing
|
290
277
|
|
291
278
|
case record
|
279
|
+
when nil
|
280
|
+
# It's not possible to remove the record from the inverse association.
|
292
281
|
when Array
|
293
282
|
super
|
294
283
|
else
|
295
|
-
|
284
|
+
replace_on_target(record, true, replace: true, inversing: true)
|
296
285
|
end
|
297
286
|
end
|
298
287
|
|
@@ -315,6 +304,10 @@ module ActiveRecord
|
|
315
304
|
end
|
316
305
|
|
317
306
|
private
|
307
|
+
def transaction(&block)
|
308
|
+
reflection.klass.transaction(&block)
|
309
|
+
end
|
310
|
+
|
318
311
|
# We have some records loaded from the database (persisted) and some that are
|
319
312
|
# in-memory (memory). The same record may be represented in the persisted array
|
320
313
|
# and in the memory array.
|
@@ -347,7 +340,7 @@ module ActiveRecord
|
|
347
340
|
|
348
341
|
def _create_record(attributes, raise = false, &block)
|
349
342
|
unless owner.persisted?
|
350
|
-
raise ActiveRecord::RecordNotSaved
|
343
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
351
344
|
end
|
352
345
|
|
353
346
|
if attributes.is_a?(Array)
|
@@ -425,7 +418,7 @@ module ActiveRecord
|
|
425
418
|
common_records = intersection(new_target, original_target)
|
426
419
|
common_records.each do |record|
|
427
420
|
skip_callbacks = true
|
428
|
-
replace_on_target(record,
|
421
|
+
replace_on_target(record, skip_callbacks, replace: true)
|
429
422
|
end
|
430
423
|
end
|
431
424
|
|
@@ -448,7 +441,11 @@ module ActiveRecord
|
|
448
441
|
records
|
449
442
|
end
|
450
443
|
|
451
|
-
def replace_on_target(record,
|
444
|
+
def replace_on_target(record, skip_callbacks, replace:, inversing: false)
|
445
|
+
if replace && (!record.new_record? || @replaced_or_added_targets.include?(record))
|
446
|
+
index = @target.index(record)
|
447
|
+
end
|
448
|
+
|
452
449
|
catch(:abort) do
|
453
450
|
callback(:before_add, record)
|
454
451
|
end || return unless skip_callbacks
|
@@ -459,6 +456,8 @@ module ActiveRecord
|
|
459
456
|
|
460
457
|
yield(record) if block_given?
|
461
458
|
|
459
|
+
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
460
|
+
|
462
461
|
if index
|
463
462
|
target[index] = record
|
464
463
|
elsif @_was_loaded || !loaded?
|