activerecord 6.1.7.10 → 7.0.0.alpha1
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 +726 -1404
- 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 +31 -9
- 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 +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 +14 -23
- 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 -47
- 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 +2 -14
- 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 +12 -14
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
- data/lib/active_record/connection_adapters/abstract/transaction.rb +3 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +112 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +1 -1
- 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/postgresql/database_statements.rb +19 -14
- 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 -32
- 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 +159 -102
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -37
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -19
- 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 +20 -38
- data/lib/active_record/core.rb +111 -125
- data/lib/active_record/database_configurations/connection_url_resolver.rb +0 -1
- 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 +17 -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 +41 -41
- 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 +34 -5
- data/lib/active_record/integration.rb +1 -1
- data/lib/active_record/internal_metadata.rb +1 -5
- 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 +89 -10
- 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 +45 -31
- 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 +72 -48
- 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 +39 -26
- data/lib/active_record/relation/delegation.rb +6 -6
- data/lib/active_record/relation/finder_methods.rb +31 -22
- 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 +230 -49
- 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 +8 -4
- data/lib/active_record/relation.rb +166 -77
- 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 +61 -12
- 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/store.rb +1 -6
- data/lib/active_record/tasks/database_tasks.rb +106 -22
- 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 +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.rb +170 -2
- data/lib/arel/attributes/attribute.rb +0 -8
- 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/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 +1 -1
- 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 +43 -2
- 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 +52 -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
|
@@ -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,13 +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
|
228
246
|
end
|
229
247
|
|
230
|
-
def
|
248
|
+
def violates_strict_loading?
|
231
249
|
return reflection.strict_loading? if reflection.options.key?(:strict_loading)
|
232
250
|
|
233
|
-
owner.strict_loading?
|
251
|
+
owner.strict_loading? && !owner.strict_loading_n_plus_one_only?
|
234
252
|
end
|
235
253
|
|
236
254
|
# The scope for this association.
|
@@ -241,7 +259,11 @@ module ActiveRecord
|
|
241
259
|
# actually gets built.
|
242
260
|
def association_scope
|
243
261
|
if klass
|
244
|
-
@association_scope ||=
|
262
|
+
@association_scope ||= if disable_joins
|
263
|
+
DisableJoinsAssociationScope.scope(self)
|
264
|
+
else
|
265
|
+
AssociationScope.scope(self)
|
266
|
+
end
|
245
267
|
end
|
246
268
|
end
|
247
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,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)
|
@@ -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.
|
@@ -340,12 +335,12 @@ module ActiveRecord
|
|
340
335
|
end
|
341
336
|
end
|
342
337
|
|
343
|
-
persisted + memory
|
338
|
+
persisted + memory
|
344
339
|
end
|
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)
|
@@ -461,10 +456,6 @@ module ActiveRecord
|
|
461
456
|
|
462
457
|
yield(record) if block_given?
|
463
458
|
|
464
|
-
if !index && @replaced_or_added_targets.include?(record)
|
465
|
-
index = @target.index(record)
|
466
|
-
end
|
467
|
-
|
468
459
|
@replaced_or_added_targets << record if inversing || index || record.new_record?
|
469
460
|
|
470
461
|
if index
|
@@ -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
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
5
|
# = Active Record Has Many Through Association
|
6
|
-
class HasManyThroughAssociation < HasManyAssociation
|
6
|
+
class HasManyThroughAssociation < HasManyAssociation # :nodoc:
|
7
7
|
include ThroughAssociation
|
8
8
|
|
9
9
|
def initialize(owner, reflection)
|
@@ -214,6 +214,7 @@ module ActiveRecord
|
|
214
214
|
|
215
215
|
def find_target
|
216
216
|
return [] unless target_reflection_has_associated_record?
|
217
|
+
return scope.to_a if disable_joins
|
217
218
|
super
|
218
219
|
end
|
219
220
|
|