activerecord 6.1.7.4 → 7.0.0
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 +1055 -1170
- 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 +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- 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 +10 -3
- 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 +18 -19
- 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/join_dependency.rb +6 -2
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +90 -82
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -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 +49 -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 +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +2 -14
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -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 +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +43 -82
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +50 -76
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- 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 +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +121 -146
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +52 -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 +28 -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 +90 -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 +49 -42
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +17 -20
- 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 +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- 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/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +89 -10
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +110 -80
- data/lib/active_record/model_schema.rb +45 -58
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- 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 +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -35
- 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 +235 -63
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +169 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/store.rb +1 -6
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +9 -13
- 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/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -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 +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -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 +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -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
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +58 -14
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
|
@@ -51,7 +52,6 @@ module ActiveRecord
|
|
51
52
|
@loaded = false
|
52
53
|
@target = nil
|
53
54
|
@stale_state = nil
|
54
|
-
@inversed = false
|
55
55
|
end
|
56
56
|
|
57
57
|
def reset_negative_cache # :nodoc:
|
@@ -77,7 +77,6 @@ module ActiveRecord
|
|
77
77
|
def loaded!
|
78
78
|
@loaded = true
|
79
79
|
@stale_state = stale_state
|
80
|
-
@inversed = false
|
81
80
|
end
|
82
81
|
|
83
82
|
# The target is stale if the target no longer points to the record(s) that the
|
@@ -87,7 +86,7 @@ module ActiveRecord
|
|
87
86
|
#
|
88
87
|
# Note that if the target has not been loaded, it is not considered stale.
|
89
88
|
def stale_target?
|
90
|
-
|
89
|
+
loaded? && @stale_state != stale_state
|
91
90
|
end
|
92
91
|
|
93
92
|
# Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
|
@@ -97,8 +96,12 @@ module ActiveRecord
|
|
97
96
|
end
|
98
97
|
|
99
98
|
def scope
|
100
|
-
if
|
99
|
+
if disable_joins
|
100
|
+
DisableJoinsAssociationScope.create.scope(self)
|
101
|
+
elsif (scope = klass.current_scope) && scope.try(:proxy_association) == self
|
101
102
|
scope.spawn
|
103
|
+
elsif scope = klass.global_current_scope
|
104
|
+
target_scope.merge!(association_scope).merge!(scope)
|
102
105
|
else
|
103
106
|
target_scope.merge!(association_scope)
|
104
107
|
end
|
@@ -132,15 +135,11 @@ module ActiveRecord
|
|
132
135
|
|
133
136
|
def inversed_from(record)
|
134
137
|
self.target = record
|
135
|
-
@inversed = !!record
|
136
138
|
end
|
137
139
|
|
138
140
|
def inversed_from_queries(record)
|
139
141
|
if inversable?(record)
|
140
142
|
self.target = record
|
141
|
-
@inversed = true
|
142
|
-
else
|
143
|
-
@inversed = false
|
144
143
|
end
|
145
144
|
end
|
146
145
|
|
@@ -191,7 +190,7 @@ module ActiveRecord
|
|
191
190
|
@reflection = @owner.class._reflect_on_association(reflection_name)
|
192
191
|
end
|
193
192
|
|
194
|
-
def initialize_attributes(record, except_from_scope_attributes = nil)
|
193
|
+
def initialize_attributes(record, except_from_scope_attributes = nil) # :nodoc:
|
195
194
|
except_from_scope_attributes ||= {}
|
196
195
|
skip_assign = [reflection.foreign_key, reflection.type].compact
|
197
196
|
assigned_keys = record.changed_attribute_names_to_save
|
@@ -210,8 +209,14 @@ module ActiveRecord
|
|
210
209
|
end
|
211
210
|
|
212
211
|
private
|
212
|
+
# Reader and writer methods call this so that consistent errors are presented
|
213
|
+
# when the association target class does not exist.
|
214
|
+
def ensure_klass_exists!
|
215
|
+
klass
|
216
|
+
end
|
217
|
+
|
213
218
|
def find_target
|
214
|
-
if
|
219
|
+
if violates_strict_loading? && owner.validation_context.nil?
|
215
220
|
Base.strict_loading_violation!(owner: owner.class, reflection: reflection)
|
216
221
|
end
|
217
222
|
|
@@ -224,13 +229,20 @@ module ActiveRecord
|
|
224
229
|
end
|
225
230
|
|
226
231
|
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
227
|
-
sc.execute(binds, klass.connection)
|
232
|
+
sc.execute(binds, klass.connection) do |record|
|
233
|
+
set_inverse_instance(record)
|
234
|
+
if owner.strict_loading_n_plus_one_only? && reflection.macro == :has_many
|
235
|
+
record.strict_loading!
|
236
|
+
else
|
237
|
+
record.strict_loading!(false, mode: owner.strict_loading_mode)
|
238
|
+
end
|
239
|
+
end
|
228
240
|
end
|
229
241
|
|
230
|
-
def
|
242
|
+
def violates_strict_loading?
|
231
243
|
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
232
244
|
|
233
|
-
owner.strict_loading?
|
245
|
+
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
|
234
246
|
end
|
235
247
|
|
236
248
|
# The scope for this association.
|
@@ -241,7 +253,11 @@ module ActiveRecord
|
|
241
253
|
# actually gets built.
|
242
254
|
def association_scope
|
243
255
|
if klass
|
244
|
-
@association_scope ||=
|
256
|
+
@association_scope ||= if disable_joins
|
257
|
+
DisableJoinsAssociationScope.scope(self)
|
258
|
+
else
|
259
|
+
AssociationScope.scope(self)
|
260
|
+
end
|
245
261
|
end
|
246
262
|
end
|
247
263
|
|
@@ -273,7 +289,7 @@ module ActiveRecord
|
|
273
289
|
|
274
290
|
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
|
275
291
|
# the kind of the class of the associated objects. Meant to be used as
|
276
|
-
# a
|
292
|
+
# a safety check when you are about to assign an associated record.
|
277
293
|
def raise_on_type_mismatch!(record)
|
278
294
|
unless record.is_a?(reflection.klass)
|
279
295
|
fresh_class = reflection.class_name.safe_constantize
|
@@ -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,7 +3,7 @@
|
|
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
|
|
@@ -55,7 +55,8 @@ module ActiveRecord
|
|
55
55
|
|
56
56
|
def decrement_counters_before_last_save
|
57
57
|
if reflection.polymorphic?
|
58
|
-
|
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
|
59
60
|
else
|
60
61
|
model_was = klass
|
61
62
|
end
|
@@ -68,6 +69,14 @@ module ActiveRecord
|
|
68
69
|
end
|
69
70
|
|
70
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?
|
71
80
|
owner.saved_change_to_attribute?(reflection.foreign_key)
|
72
81
|
end
|
73
82
|
|
@@ -77,6 +86,8 @@ module ActiveRecord
|
|
77
86
|
raise_on_type_mismatch!(record)
|
78
87
|
set_inverse_instance(record)
|
79
88
|
@updated = true
|
89
|
+
elsif target
|
90
|
+
remove_inverse_instance(target)
|
80
91
|
end
|
81
92
|
|
82
93
|
replace_keys(record, force: true)
|
@@ -110,7 +121,7 @@ module ActiveRecord
|
|
110
121
|
def replace_keys(record, force: false)
|
111
122
|
target_key = record ? record._read_attribute(primary_key(record.class)) : nil
|
112
123
|
|
113
|
-
if force || owner
|
124
|
+
if force || owner._read_attribute(reflection.foreign_key) != target_key
|
114
125
|
owner[reflection.foreign_key] = target_key
|
115
126
|
end
|
116
127
|
end
|
@@ -125,7 +136,7 @@ module ActiveRecord
|
|
125
136
|
|
126
137
|
def invertible_for?(record)
|
127
138
|
inverse = inverse_reflection_for(record)
|
128
|
-
inverse && (inverse.has_one? ||
|
139
|
+
inverse && (inverse.has_one? || inverse.klass.has_many_inversing)
|
129
140
|
end
|
130
141
|
|
131
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)
|
@@ -30,11 +30,18 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
30
30
|
def self.define_callback(model, callback_name, name, options)
|
31
31
|
full_callback_name = "#{callback_name}_for_#{name}"
|
32
32
|
|
33
|
-
|
33
|
+
callback_values = Array(options[callback_name.to_sym])
|
34
|
+
method_defined = model.respond_to?(full_callback_name)
|
35
|
+
|
36
|
+
# If there are no callbacks, we must also check if a superclass had
|
37
|
+
# previously defined this association
|
38
|
+
return if callback_values.empty? && !method_defined
|
39
|
+
|
40
|
+
unless method_defined
|
34
41
|
model.class_attribute(full_callback_name, instance_accessor: false, instance_predicate: false)
|
35
42
|
end
|
36
43
|
|
37
|
-
callbacks =
|
44
|
+
callbacks = callback_values.map do |callback|
|
38
45
|
case callback
|
39
46
|
when Symbol
|
40
47
|
->(method, owner, record) { owner.send(callback, record) }
|
@@ -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
|
@@ -122,21 +126,6 @@ module ActiveRecord
|
|
122
126
|
end
|
123
127
|
end
|
124
128
|
|
125
|
-
# Starts a transaction in the association class's database connection.
|
126
|
-
#
|
127
|
-
# class Author < ActiveRecord::Base
|
128
|
-
# has_many :books
|
129
|
-
# end
|
130
|
-
#
|
131
|
-
# Author.first.books.transaction do
|
132
|
-
# # same effect as calling Book.transaction
|
133
|
-
# end
|
134
|
-
def transaction(*args)
|
135
|
-
reflection.klass.transaction(*args) do
|
136
|
-
yield
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
129
|
# Removes all records from the association without calling callbacks
|
141
130
|
# on the associated records. It honors the +:dependent+ option. However
|
142
131
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
@@ -284,9 +273,11 @@ module ActiveRecord
|
|
284
273
|
end
|
285
274
|
|
286
275
|
def target=(record)
|
287
|
-
return super unless
|
276
|
+
return super unless reflection.klass.has_many_inversing
|
288
277
|
|
289
278
|
case record
|
279
|
+
when nil
|
280
|
+
# It's not possible to remove the record from the inverse association.
|
290
281
|
when Array
|
291
282
|
super
|
292
283
|
else
|
@@ -313,6 +304,10 @@ module ActiveRecord
|
|
313
304
|
end
|
314
305
|
|
315
306
|
private
|
307
|
+
def transaction(&block)
|
308
|
+
reflection.klass.transaction(&block)
|
309
|
+
end
|
310
|
+
|
316
311
|
# We have some records loaded from the database (persisted) and some that are
|
317
312
|
# in-memory (memory). The same record may be represented in the persisted array
|
318
313
|
# and in the memory array.
|
@@ -345,7 +340,7 @@ module ActiveRecord
|
|
345
340
|
|
346
341
|
def _create_record(attributes, raise = false, &block)
|
347
342
|
unless owner.persisted?
|
348
|
-
raise ActiveRecord::RecordNotSaved
|
343
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
349
344
|
end
|
350
345
|
|
351
346
|
if attributes.is_a?(Array)
|
@@ -489,7 +484,11 @@ module ActiveRecord
|
|
489
484
|
|
490
485
|
def callbacks_for(callback_name)
|
491
486
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
492
|
-
owner.class.
|
487
|
+
if owner.class.respond_to?(full_callback_name)
|
488
|
+
owner.class.send(full_callback_name)
|
489
|
+
else
|
490
|
+
[]
|
491
|
+
end
|
493
492
|
end
|
494
493
|
|
495
494
|
def include_in_memory?(record)
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
# is computed directly through SQL and does not trigger by itself the
|
28
28
|
# instantiation of the actual post records.
|
29
29
|
class CollectionProxy < Relation
|
30
|
-
def initialize(klass, association, **)
|
30
|
+
def initialize(klass, association, **) # :nodoc:
|
31
31
|
@association = association
|
32
32
|
super klass
|
33
33
|
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
# Returns +true+ if the association has been loaded, otherwise +false+.
|
47
47
|
#
|
48
48
|
# person.pets.loaded? # => false
|
49
|
-
# person.pets
|
49
|
+
# person.pets.records
|
50
50
|
# person.pets.loaded? # => true
|
51
51
|
def loaded?
|
52
52
|
@association.loaded?
|
@@ -813,7 +813,7 @@ module ActiveRecord
|
|
813
813
|
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
814
814
|
# it is equivalent to <tt>!collection.exists?</tt>. If the collection has
|
815
815
|
# not already been loaded and you are going to fetch the records anyway it
|
816
|
-
# is better to check <tt>collection.
|
816
|
+
# is better to check <tt>collection.load.empty?</tt>.
|
817
817
|
#
|
818
818
|
# class Person < ActiveRecord::Base
|
819
819
|
# has_many :pets
|
@@ -849,6 +849,11 @@ module ActiveRecord
|
|
849
849
|
# person.pets.count # => 1
|
850
850
|
# person.pets.any? # => true
|
851
851
|
#
|
852
|
+
# Calling it without a block when the collection is not yet
|
853
|
+
# loaded is equivalent to <tt>collection.exists?</tt>.
|
854
|
+
# If you're going to load the collection anyway, it is better
|
855
|
+
# to call <tt>collection.load.any?</tt> to avoid an extra query.
|
856
|
+
#
|
852
857
|
# You can also pass a +block+ to define criteria. The behavior
|
853
858
|
# is the same, it returns true if the collection based on the
|
854
859
|
# criteria is not empty.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Associations
|
5
|
+
class DisableJoinsAssociationScope < AssociationScope # :nodoc:
|
6
|
+
def scope(association)
|
7
|
+
source_reflection = association.reflection
|
8
|
+
owner = association.owner
|
9
|
+
unscoped = association.klass.unscoped
|
10
|
+
reverse_chain = get_chain(source_reflection, association, unscoped.alias_tracker).reverse
|
11
|
+
|
12
|
+
last_reflection, last_ordered, last_join_ids = last_scope_chain(reverse_chain, owner)
|
13
|
+
|
14
|
+
add_constraints(last_reflection, last_reflection.join_primary_key, last_join_ids, owner, last_ordered)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def last_scope_chain(reverse_chain, owner)
|
19
|
+
first_item = reverse_chain.shift
|
20
|
+
first_scope = [first_item, false, [owner._read_attribute(first_item.join_foreign_key)]]
|
21
|
+
|
22
|
+
reverse_chain.inject(first_scope) do |(reflection, ordered, join_ids), next_reflection|
|
23
|
+
key = reflection.join_primary_key
|
24
|
+
records = add_constraints(reflection, key, join_ids, owner, ordered)
|
25
|
+
foreign_key = next_reflection.join_foreign_key
|
26
|
+
record_ids = records.pluck(foreign_key)
|
27
|
+
records_ordered = records && records.order_values.any?
|
28
|
+
|
29
|
+
[next_reflection, records_ordered, record_ids]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_constraints(reflection, key, join_ids, owner, ordered)
|
34
|
+
scope = reflection.build_scope(reflection.aliased_table).where(key => join_ids)
|
35
|
+
|
36
|
+
relation = reflection.klass.scope_for_association
|
37
|
+
scope.merge!(
|
38
|
+
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
|
39
|
+
)
|
40
|
+
|
41
|
+
scope = reflection.constraints.inject(scope) do |memo, scope_chain_item|
|
42
|
+
item = eval_scope(reflection, scope_chain_item, owner)
|
43
|
+
scope.unscope!(*item.unscope_values)
|
44
|
+
scope.where_clause += item.where_clause
|
45
|
+
scope.order_values = item.order_values | scope.order_values
|
46
|
+
scope
|
47
|
+
end
|
48
|
+
|
49
|
+
if scope.order_values.empty? && ordered
|
50
|
+
split_scope = DisableJoinsAssociationRelation.create(scope.klass, key, join_ids)
|
51
|
+
split_scope.where_clause += scope.where_clause
|
52
|
+
split_scope
|
53
|
+
else
|
54
|
+
scope
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
#
|
8
8
|
# If the association has a <tt>:through</tt> option further specialization
|
9
9
|
# is provided by its child HasManyThroughAssociation.
|
10
|
-
class HasManyAssociation < CollectionAssociation
|
10
|
+
class HasManyAssociation < CollectionAssociation # :nodoc:
|
11
11
|
include ForeignAssociation
|
12
12
|
|
13
13
|
def handle_dependency
|