activerecord 7.1.5.2 → 8.1.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
|
@@ -17,11 +17,15 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
17
17
|
|
|
18
18
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
19
19
|
def reload_#{name}
|
|
20
|
-
association(:#{name})
|
|
20
|
+
association = association(:#{name})
|
|
21
|
+
deprecated_associations_api_guard(association, __method__)
|
|
22
|
+
association.force_reload_reader
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def reset_#{name}
|
|
24
|
-
association(:#{name})
|
|
26
|
+
association = association(:#{name})
|
|
27
|
+
deprecated_associations_api_guard(association, __method__)
|
|
28
|
+
association.reset
|
|
25
29
|
end
|
|
26
30
|
CODE
|
|
27
31
|
end
|
|
@@ -30,19 +34,43 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
|
30
34
|
def self.define_constructors(mixin, name)
|
|
31
35
|
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
32
36
|
def build_#{name}(*args, &block)
|
|
33
|
-
association(:#{name})
|
|
37
|
+
association = association(:#{name})
|
|
38
|
+
deprecated_associations_api_guard(association, __method__)
|
|
39
|
+
association.build(*args, &block)
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
def create_#{name}(*args, &block)
|
|
37
|
-
association(:#{name})
|
|
43
|
+
association = association(:#{name})
|
|
44
|
+
deprecated_associations_api_guard(association, __method__)
|
|
45
|
+
association.create(*args, &block)
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
def create_#{name}!(*args, &block)
|
|
41
|
-
association(:#{name})
|
|
49
|
+
association = association(:#{name})
|
|
50
|
+
deprecated_associations_api_guard(association, __method__)
|
|
51
|
+
association.create!(*args, &block)
|
|
42
52
|
end
|
|
43
53
|
CODE
|
|
44
54
|
end
|
|
45
55
|
|
|
56
|
+
def self.define_callbacks(model, reflection)
|
|
57
|
+
super
|
|
58
|
+
|
|
59
|
+
# If the record is saved or destroyed and `:touch` is set, the parent
|
|
60
|
+
# record gets a timestamp updated. We want to know about it, because
|
|
61
|
+
# deleting the association would change that side-effect and perhaps there
|
|
62
|
+
# is code relying on it.
|
|
63
|
+
if reflection.deprecated? && reflection.options[:touch]
|
|
64
|
+
model.before_save do
|
|
65
|
+
report_deprecated_association(reflection, context: ":touch has a side effect here")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
model.before_destroy do
|
|
69
|
+
report_deprecated_association(reflection, context: ":touch has a side effect here")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
46
74
|
private_class_method :valid_options, :define_accessors, :define_constructors
|
|
47
75
|
end
|
|
48
76
|
end
|
|
@@ -28,6 +28,8 @@ module ActiveRecord
|
|
|
28
28
|
# If you need to work on all current children, new and existing records,
|
|
29
29
|
# +load_target+ and the +loaded+ flag are your friends.
|
|
30
30
|
class CollectionAssociation < Association # :nodoc:
|
|
31
|
+
attr_accessor :nested_attributes_target
|
|
32
|
+
|
|
31
33
|
# Implements the reader method, e.g. foo.items for Foo.has_many :items
|
|
32
34
|
def reader
|
|
33
35
|
ensure_klass_exists!
|
|
@@ -92,7 +94,7 @@ module ActiveRecord
|
|
|
92
94
|
def find(*args)
|
|
93
95
|
if options[:inverse_of] && loaded?
|
|
94
96
|
args_flatten = args.flatten
|
|
95
|
-
model = scope.
|
|
97
|
+
model = scope.model
|
|
96
98
|
|
|
97
99
|
if args_flatten.blank?
|
|
98
100
|
error_message = "Couldn't find #{model.name} without an ID"
|
|
@@ -228,7 +230,7 @@ module ActiveRecord
|
|
|
228
230
|
# loaded and you are going to fetch the records anyway it is better to
|
|
229
231
|
# check <tt>collection.length.zero?</tt>.
|
|
230
232
|
def empty?
|
|
231
|
-
if loaded? || @association_ids || reflection.
|
|
233
|
+
if loaded? || @association_ids || reflection.has_active_cached_counter?
|
|
232
234
|
size.zero?
|
|
233
235
|
else
|
|
234
236
|
target.empty? && !scope.exists?
|
|
@@ -254,14 +256,16 @@ module ActiveRecord
|
|
|
254
256
|
end
|
|
255
257
|
|
|
256
258
|
def include?(record)
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
klass = reflection.klass
|
|
260
|
+
return false unless record.is_a?(klass)
|
|
261
|
+
|
|
262
|
+
if loaded?
|
|
263
|
+
target.include?(record)
|
|
264
|
+
elsif record.new_record?
|
|
265
|
+
include_in_memory?(record)
|
|
263
266
|
else
|
|
264
|
-
|
|
267
|
+
record_id = klass.composite_primary_key? ? klass.primary_key.zip(record.id).to_h : record.id
|
|
268
|
+
scope.exists?(record_id)
|
|
265
269
|
end
|
|
266
270
|
end
|
|
267
271
|
|
|
@@ -309,6 +313,10 @@ module ActiveRecord
|
|
|
309
313
|
target.any? { |record| record.new_record? || record.changed? }
|
|
310
314
|
end
|
|
311
315
|
|
|
316
|
+
def collection?
|
|
317
|
+
true
|
|
318
|
+
end
|
|
319
|
+
|
|
312
320
|
private
|
|
313
321
|
def transaction(&block)
|
|
314
322
|
reflection.klass.transaction(&block)
|
|
@@ -110,7 +110,7 @@ module ActiveRecord
|
|
|
110
110
|
# # ]
|
|
111
111
|
|
|
112
112
|
# Finds an object in the collection responding to the +id+. Uses the same
|
|
113
|
-
# rules as ActiveRecord::FinderMethods.find.
|
|
113
|
+
# rules as ActiveRecord::FinderMethods.find. Raises ActiveRecord::RecordNotFound
|
|
114
114
|
# error if the object cannot be found.
|
|
115
115
|
#
|
|
116
116
|
# class Person < ActiveRecord::Base
|
|
@@ -928,7 +928,20 @@ module ActiveRecord
|
|
|
928
928
|
!!@association.include?(record)
|
|
929
929
|
end
|
|
930
930
|
|
|
931
|
-
|
|
931
|
+
# Returns the association object for the collection.
|
|
932
|
+
#
|
|
933
|
+
# class Person < ActiveRecord::Base
|
|
934
|
+
# has_many :pets
|
|
935
|
+
# end
|
|
936
|
+
#
|
|
937
|
+
# person.pets.proxy_association
|
|
938
|
+
# # => #<ActiveRecord::Associations::HasManyAssociation owner="#<Person:0x00>">
|
|
939
|
+
#
|
|
940
|
+
# Returns the same object as <tt>person.association(:pets)</tt>,
|
|
941
|
+
# allowing you to make calls like <tt>person.pets.proxy_association.owner</tt>.
|
|
942
|
+
#
|
|
943
|
+
# See Associations::ClassMethods@Association+extensions for more.
|
|
944
|
+
def proxy_association
|
|
932
945
|
@association
|
|
933
946
|
end
|
|
934
947
|
|
|
@@ -1112,14 +1125,32 @@ module ActiveRecord
|
|
|
1112
1125
|
super
|
|
1113
1126
|
end
|
|
1114
1127
|
|
|
1128
|
+
%w(insert insert_all insert! insert_all! upsert upsert_all).each do |method|
|
|
1129
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
1130
|
+
def #{method}(...)
|
|
1131
|
+
if @association&.target&.any? { |r| r.new_record? }
|
|
1132
|
+
association_name = @association.reflection.name
|
|
1133
|
+
ActiveRecord.deprecator.warn(<<~MSG)
|
|
1134
|
+
Using #{method} on association \#{association_name} with unpersisted records
|
|
1135
|
+
is deprecated and will be removed in Rails 8.2.
|
|
1136
|
+
The unpersisted records will be lost after this operation.
|
|
1137
|
+
Please either persist your records first or store them separately before
|
|
1138
|
+
calling #{method}.
|
|
1139
|
+
MSG
|
|
1140
|
+
scope.#{method}(...)
|
|
1141
|
+
else
|
|
1142
|
+
scope.#{method}(...).tap { reset }
|
|
1143
|
+
end
|
|
1144
|
+
end
|
|
1145
|
+
RUBY
|
|
1146
|
+
end
|
|
1147
|
+
|
|
1115
1148
|
delegate_methods = [
|
|
1116
1149
|
QueryMethods,
|
|
1117
1150
|
SpawnMethods,
|
|
1118
1151
|
].flat_map { |klass|
|
|
1119
1152
|
klass.public_instance_methods(false)
|
|
1120
|
-
} - self.public_instance_methods(false) - [:select] + [
|
|
1121
|
-
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
|
|
1122
|
-
]
|
|
1153
|
+
} - self.public_instance_methods(false) - [ :select ] + [ :scoping, :values, :load_async ]
|
|
1123
1154
|
|
|
1124
1155
|
delegate(*delegate_methods, to: :scope)
|
|
1125
1156
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/notifications"
|
|
4
|
+
require "active_support/core_ext/array/conversions"
|
|
5
|
+
|
|
6
|
+
module ActiveRecord::Associations::Deprecation # :nodoc:
|
|
7
|
+
EVENT = "deprecated_association.active_record"
|
|
8
|
+
private_constant :EVENT
|
|
9
|
+
|
|
10
|
+
MODES = [:warn, :raise, :notify].freeze
|
|
11
|
+
private_constant :MODES
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
attr_reader :mode, :backtrace
|
|
15
|
+
|
|
16
|
+
def mode=(value) # private setter
|
|
17
|
+
unless MODES.include?(value)
|
|
18
|
+
raise ArgumentError, "invalid deprecated associations mode #{value.inspect} (valid modes are #{MODES.map(&:inspect).to_sentence})"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@mode = value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def backtrace=(value)
|
|
25
|
+
@backtrace = !!value
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def guard(reflection)
|
|
29
|
+
report(reflection, context: yield) if reflection.deprecated?
|
|
30
|
+
|
|
31
|
+
if reflection.through_reflection?
|
|
32
|
+
reflection.deprecated_nested_reflections.each do |deprecated_nested_reflection|
|
|
33
|
+
context = "referenced as nested association of the through #{reflection.active_record}##{reflection.name}"
|
|
34
|
+
report(deprecated_nested_reflection, context: context)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def report(reflection, context:)
|
|
40
|
+
reflection = user_facing_reflection(reflection)
|
|
41
|
+
|
|
42
|
+
message = +"The association #{reflection.active_record}##{reflection.name} is deprecated, #{context}"
|
|
43
|
+
message << " (#{backtrace_cleaner.first_clean_frame})"
|
|
44
|
+
|
|
45
|
+
case @mode
|
|
46
|
+
when :warn
|
|
47
|
+
message = [message, *clean_frames].join("\n\t") if @backtrace
|
|
48
|
+
ActiveRecord::Base.logger&.warn(message)
|
|
49
|
+
when :raise
|
|
50
|
+
error = ActiveRecord::DeprecatedAssociationError.new(message)
|
|
51
|
+
if set_backtrace_supports_array_of_locations?
|
|
52
|
+
error.set_backtrace(clean_locations)
|
|
53
|
+
else
|
|
54
|
+
error.set_backtrace(clean_frames)
|
|
55
|
+
end
|
|
56
|
+
raise error
|
|
57
|
+
else
|
|
58
|
+
payload = { reflection: reflection, message: message, location: backtrace_cleaner.first_clean_location }
|
|
59
|
+
payload[:backtrace] = clean_locations if @backtrace
|
|
60
|
+
ActiveSupport::Notifications.instrument(EVENT, payload)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
def backtrace_cleaner
|
|
66
|
+
ActiveRecord::LogSubscriber.backtrace_cleaner
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def clean_frames
|
|
70
|
+
backtrace_cleaner.clean(caller)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def clean_locations
|
|
74
|
+
backtrace_cleaner.clean_locations(caller_locations)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def set_backtrace_supports_array_of_locations?
|
|
78
|
+
@backtrace_supports_array_of_locations ||= Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.4.0")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def user_facing_reflection(reflection)
|
|
82
|
+
reflection.active_record.reflect_on_association(reflection.name)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
self.mode = :warn
|
|
87
|
+
self.backtrace = false
|
|
88
|
+
end
|
|
@@ -47,7 +47,7 @@ module ActiveRecord
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
if scope.order_values.empty? && ordered
|
|
50
|
-
split_scope = DisableJoinsAssociationRelation.create(scope.
|
|
50
|
+
split_scope = DisableJoinsAssociationRelation.create(scope.model, key, join_ids)
|
|
51
51
|
split_scope.where_clause += scope.where_clause
|
|
52
52
|
split_scope
|
|
53
53
|
else
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class AssociationNotFoundError < ConfigurationError # :nodoc:
|
|
5
|
+
attr_reader :record, :association_name
|
|
6
|
+
|
|
7
|
+
def initialize(record = nil, association_name = nil)
|
|
8
|
+
@record = record
|
|
9
|
+
@association_name = association_name
|
|
10
|
+
if record && association_name
|
|
11
|
+
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
|
|
12
|
+
else
|
|
13
|
+
super("Association was not found.")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
18
|
+
include DidYouMean::Correctable
|
|
19
|
+
|
|
20
|
+
def corrections
|
|
21
|
+
if record && association_name
|
|
22
|
+
@corrections ||= begin
|
|
23
|
+
maybe_these = record.class.reflections.keys
|
|
24
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(association_name)
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
[]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class InverseOfAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
34
|
+
attr_reader :reflection, :associated_class
|
|
35
|
+
|
|
36
|
+
def initialize(reflection = nil, associated_class = nil)
|
|
37
|
+
if reflection
|
|
38
|
+
@reflection = reflection
|
|
39
|
+
@associated_class = associated_class.nil? ? reflection.klass : associated_class
|
|
40
|
+
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
|
|
41
|
+
else
|
|
42
|
+
super("Could not find the inverse association.")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
47
|
+
include DidYouMean::Correctable
|
|
48
|
+
|
|
49
|
+
def corrections
|
|
50
|
+
if reflection && associated_class
|
|
51
|
+
@corrections ||= begin
|
|
52
|
+
maybe_these = associated_class.reflections.keys
|
|
53
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:inverse_of].to_s)
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
[]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
class InverseOfAssociationRecursiveError < ActiveRecordError # :nodoc:
|
|
63
|
+
attr_reader :reflection
|
|
64
|
+
def initialize(reflection = nil)
|
|
65
|
+
if reflection
|
|
66
|
+
@reflection = reflection
|
|
67
|
+
super("Inverse association #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{reflection.class_name}) is recursive.")
|
|
68
|
+
else
|
|
69
|
+
super("Inverse association is recursive.")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
class HasManyThroughAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
75
|
+
attr_reader :owner_class, :reflection
|
|
76
|
+
|
|
77
|
+
def initialize(owner_class = nil, reflection = nil)
|
|
78
|
+
if owner_class && reflection
|
|
79
|
+
@owner_class = owner_class
|
|
80
|
+
@reflection = reflection
|
|
81
|
+
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class.name}")
|
|
82
|
+
else
|
|
83
|
+
super("Could not find the association.")
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if defined?(DidYouMean::Correctable) && defined?(DidYouMean::SpellChecker)
|
|
88
|
+
include DidYouMean::Correctable
|
|
89
|
+
|
|
90
|
+
def corrections
|
|
91
|
+
if owner_class && reflection
|
|
92
|
+
@corrections ||= begin
|
|
93
|
+
maybe_these = owner_class.reflections.keys
|
|
94
|
+
maybe_these -= [reflection.name.to_s] # remove failing reflection
|
|
95
|
+
DidYouMean::SpellChecker.new(dictionary: maybe_these).correct(reflection.options[:through].to_s)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError # :nodoc:
|
|
105
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
|
106
|
+
if owner_class_name && reflection && source_reflection
|
|
107
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
|
|
108
|
+
else
|
|
109
|
+
super("Cannot have a has_many :through association.")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
|
115
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
|
116
|
+
if owner_class_name && reflection
|
|
117
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
|
118
|
+
else
|
|
119
|
+
super("Cannot have a has_many :through association.")
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError # :nodoc:
|
|
125
|
+
def initialize(owner_class_name = nil, reflection = nil, source_reflection = nil)
|
|
126
|
+
if owner_class_name && reflection && source_reflection
|
|
127
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
|
|
128
|
+
else
|
|
129
|
+
super("Cannot have a has_many :through association.")
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class HasOneThroughCantAssociateThroughCollection < ActiveRecordError # :nodoc:
|
|
135
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
|
136
|
+
if owner_class_name && reflection && through_reflection
|
|
137
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
|
|
138
|
+
else
|
|
139
|
+
super("Cannot have a has_one :through association.")
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
class HasOneAssociationPolymorphicThroughError < ActiveRecordError # :nodoc:
|
|
145
|
+
def initialize(owner_class_name = nil, reflection = nil)
|
|
146
|
+
if owner_class_name && reflection
|
|
147
|
+
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
|
|
148
|
+
else
|
|
149
|
+
super("Cannot have a has_one :through association.")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError # :nodoc:
|
|
155
|
+
def initialize(reflection = nil)
|
|
156
|
+
if reflection
|
|
157
|
+
through_reflection = reflection.through_reflection
|
|
158
|
+
source_reflection_names = reflection.source_reflection_names
|
|
159
|
+
source_associations = reflection.through_reflection.klass._reflections.keys
|
|
160
|
+
super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}?")
|
|
161
|
+
else
|
|
162
|
+
super("Could not find the source association(s).")
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class HasManyThroughOrderError < ActiveRecordError # :nodoc:
|
|
168
|
+
def initialize(owner_class_name = nil, reflection = nil, through_reflection = nil)
|
|
169
|
+
if owner_class_name && reflection && through_reflection
|
|
170
|
+
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through '#{owner_class_name}##{through_reflection.name}' before the through association is defined.")
|
|
171
|
+
else
|
|
172
|
+
super("Cannot have a has_many :through association before the through association is defined.")
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
class ThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError # :nodoc:
|
|
178
|
+
def initialize(owner = nil, reflection = nil)
|
|
179
|
+
if owner && reflection
|
|
180
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
|
|
181
|
+
else
|
|
182
|
+
super("Cannot modify association.")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
class CompositePrimaryKeyMismatchError < ActiveRecordError # :nodoc:
|
|
188
|
+
attr_reader :reflection
|
|
189
|
+
|
|
190
|
+
def initialize(reflection = nil)
|
|
191
|
+
if reflection
|
|
192
|
+
if reflection.has_one? || reflection.collection?
|
|
193
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.active_record_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
|
194
|
+
else
|
|
195
|
+
super("Association #{reflection.active_record}##{reflection.name} primary key #{reflection.association_primary_key} doesn't match with foreign key #{reflection.foreign_key}. Please specify query_constraints, or primary_key and foreign_key values.")
|
|
196
|
+
end
|
|
197
|
+
else
|
|
198
|
+
super("Association primary key doesn't match with foreign key.")
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
class AmbiguousSourceReflectionForThroughAssociation < ActiveRecordError # :nodoc:
|
|
204
|
+
def initialize(klass, macro, association_name, options, possible_sources)
|
|
205
|
+
example_options = options.dup
|
|
206
|
+
example_options[:source] = possible_sources.first
|
|
207
|
+
|
|
208
|
+
super("Ambiguous source reflection for through association. Please " \
|
|
209
|
+
"specify a :source directive on your declaration like:\n" \
|
|
210
|
+
"\n" \
|
|
211
|
+
" class #{klass} < ActiveRecord::Base\n" \
|
|
212
|
+
" #{macro} :#{association_name}, #{example_options}\n" \
|
|
213
|
+
" end"
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class HasOneThroughCantAssociateThroughHasOneOrManyReflection < ThroughCantAssociateThroughHasOneOrManyReflection # :nodoc:
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
class ThroughNestedAssociationsAreReadonly < ActiveRecordError # :nodoc:
|
|
225
|
+
def initialize(owner = nil, reflection = nil)
|
|
226
|
+
if owner && reflection
|
|
227
|
+
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
|
|
228
|
+
else
|
|
229
|
+
super("Through nested associations are read-only.")
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
class HasManyThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
class HasOneThroughNestedAssociationsAreReadonly < ThroughNestedAssociationsAreReadonly # :nodoc:
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# This error is raised when trying to eager load a polymorphic association using a JOIN.
|
|
241
|
+
# Eager loading polymorphic associations is only possible with
|
|
242
|
+
# {ActiveRecord::Relation#preload}[rdoc-ref:QueryMethods#preload].
|
|
243
|
+
class EagerLoadPolymorphicError < ActiveRecordError
|
|
244
|
+
def initialize(reflection = nil)
|
|
245
|
+
if reflection
|
|
246
|
+
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
|
|
247
|
+
else
|
|
248
|
+
super("Eager load polymorphic error.")
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
|
|
254
|
+
# (has_many, has_one) when there is at least 1 child associated instance.
|
|
255
|
+
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
|
|
256
|
+
class DeleteRestrictionError < ActiveRecordError # :nodoc:
|
|
257
|
+
def initialize(name = nil)
|
|
258
|
+
if name
|
|
259
|
+
super("Cannot delete record because of dependent #{name}")
|
|
260
|
+
else
|
|
261
|
+
super("Delete restriction error.")
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
class DeprecatedAssociationError < ActiveRecordError
|
|
267
|
+
end
|
|
268
|
+
end
|
|
@@ -78,7 +78,7 @@ module ActiveRecord
|
|
|
78
78
|
# If the collection is empty the target is set to an empty array and
|
|
79
79
|
# the loaded flag is set to true as well.
|
|
80
80
|
def count_records
|
|
81
|
-
count = if reflection.
|
|
81
|
+
count = if reflection.has_active_cached_counter?
|
|
82
82
|
owner.read_attribute(reflection.counter_cache_column).to_i
|
|
83
83
|
else
|
|
84
84
|
scope.count(:all)
|
|
@@ -93,7 +93,13 @@ module ActiveRecord
|
|
|
93
93
|
@through_scope = scope
|
|
94
94
|
record = super
|
|
95
95
|
|
|
96
|
-
inverse =
|
|
96
|
+
inverse =
|
|
97
|
+
if source_reflection.polymorphic?
|
|
98
|
+
source_reflection.polymorphic_inverse_of(record.class)
|
|
99
|
+
else
|
|
100
|
+
source_reflection.inverse_of
|
|
101
|
+
end
|
|
102
|
+
|
|
97
103
|
if inverse
|
|
98
104
|
if inverse.collection?
|
|
99
105
|
record.send(inverse.name) << build_through_record(record)
|
|
@@ -140,7 +146,7 @@ module ActiveRecord
|
|
|
140
146
|
|
|
141
147
|
case method
|
|
142
148
|
when :destroy
|
|
143
|
-
if scope.
|
|
149
|
+
if scope.model.primary_key
|
|
144
150
|
count = scope.destroy_all.count(&:destroyed?)
|
|
145
151
|
else
|
|
146
152
|
scope.each(&:_run_destroy_callbacks)
|
|
@@ -216,7 +222,8 @@ module ActiveRecord
|
|
|
216
222
|
end
|
|
217
223
|
end
|
|
218
224
|
|
|
219
|
-
def find_target
|
|
225
|
+
def find_target(async: false)
|
|
226
|
+
raise NotImplementedError, "No async loading for HasManyThroughAssociation yet" if async
|
|
220
227
|
return [] unless target_reflection_has_associated_record?
|
|
221
228
|
return scope.to_a if disable_joins
|
|
222
229
|
super
|
|
@@ -92,7 +92,7 @@ module ActiveRecord
|
|
|
92
92
|
def append_constraints(join, constraints)
|
|
93
93
|
if join.is_a?(Arel::Nodes::StringJoin)
|
|
94
94
|
join_string = Arel::Nodes::And.new(constraints.unshift join.left)
|
|
95
|
-
join.left =
|
|
95
|
+
join.left = join_string
|
|
96
96
|
else
|
|
97
97
|
right = join.right
|
|
98
98
|
right.expr = Arel::Nodes::And.new(constraints.unshift right.expr)
|
|
@@ -103,7 +103,7 @@ module ActiveRecord
|
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def instantiate(result_set, strict_loading_value, &block)
|
|
106
|
-
primary_key = aliases.column_alias(join_root,
|
|
106
|
+
primary_key = Array(join_root.primary_key).map { |column| aliases.column_alias(join_root, column) }
|
|
107
107
|
|
|
108
108
|
seen = Hash.new { |i, parent|
|
|
109
109
|
i[parent] = Hash.new { |j, child_class|
|
|
@@ -141,7 +141,7 @@ module ActiveRecord
|
|
|
141
141
|
|
|
142
142
|
message_bus.instrument("instantiation.active_record", payload) do
|
|
143
143
|
result_set.each { |row_hash|
|
|
144
|
-
parent_key = primary_key ? row_hash
|
|
144
|
+
parent_key = primary_key.empty? ? row_hash : row_hash.values_at(*primary_key)
|
|
145
145
|
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, column_types, &block)
|
|
146
146
|
construct(parent, join_root, row_hash, seen, model_cache, strict_loading_value)
|
|
147
147
|
}
|
|
@@ -235,6 +235,8 @@ module ActiveRecord
|
|
|
235
235
|
raise EagerLoadPolymorphicError.new(reflection)
|
|
236
236
|
end
|
|
237
237
|
|
|
238
|
+
Deprecation.guard(reflection) { "referenced in query to join its table" }
|
|
239
|
+
|
|
238
240
|
JoinAssociation.new(reflection, build(right, reflection.klass))
|
|
239
241
|
end
|
|
240
242
|
end
|