activerecord 6.1.7 → 7.1.5
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 +2030 -1020
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +51 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- 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 +6 -2
- data/lib/active_record/associations/collection_association.rb +39 -35
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/preloader/association.rb +210 -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 +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +446 -306
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +27 -12
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +161 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +65 -31
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- 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/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- 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 +30 -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 +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +108 -137
- data/lib/active_record/core.rb +242 -233
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +88 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +66 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- 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/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -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 +157 -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 +53 -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 +92 -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 +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +172 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +147 -86
- data/lib/active_record/future_result.rb +174 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +37 -22
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +112 -14
- data/lib/active_record/migration/compatibility.rb +233 -46
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +361 -173
- data/lib/active_record/model_schema.rb +125 -101
- data/lib/active_record/nested_attributes.rb +50 -20
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +409 -88
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +220 -44
- data/lib/active_record/railties/controller_runtime.rb +15 -10
- data/lib/active_record/railties/databases.rake +188 -252
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +248 -81
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +246 -90
- data/lib/active_record/relation/delegation.rb +28 -14
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +670 -129
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +287 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +32 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +251 -140
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +117 -96
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- 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/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +5 -13
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +141 -20
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- 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 +96 -16
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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,17 +30,17 @@ 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
|
37
37
|
}
|
38
38
|
|
39
39
|
klass = reflection.class_name.safe_constantize
|
40
|
-
klass.
|
40
|
+
klass._counter_cache_columns |= [cache_column] if klass && klass.respond_to?(:_counter_cache_columns)
|
41
41
|
end
|
42
42
|
|
43
|
-
def self.touch_record(o, changes, foreign_key, name, touch
|
43
|
+
def self.touch_record(o, changes, foreign_key, name, touch) # :nodoc:
|
44
44
|
old_foreign_id = changes[foreign_key] && changes[foreign_key].first
|
45
45
|
|
46
46
|
if old_foreign_id
|
@@ -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
|
@@ -58,9 +58,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
58
58
|
|
59
59
|
if old_record
|
60
60
|
if touch != true
|
61
|
-
old_record.
|
61
|
+
old_record.touch_later(touch)
|
62
62
|
else
|
63
|
-
old_record.
|
63
|
+
old_record.touch_later
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
@@ -68,9 +68,9 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
68
68
|
record = o.public_send name
|
69
69
|
if record && record.persisted?
|
70
70
|
if touch != true
|
71
|
-
record.
|
71
|
+
record.touch_later(touch)
|
72
72
|
else
|
73
|
-
record.
|
73
|
+
record.touch_later
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -81,13 +81,13 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
81
81
|
touch = reflection.options[:touch]
|
82
82
|
|
83
83
|
callback = lambda { |changes_method| lambda { |record|
|
84
|
-
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch
|
84
|
+
BelongsTo.touch_record(record, record.send(changes_method), foreign_key, name, touch)
|
85
85
|
}}
|
86
86
|
|
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
|
@@ -123,11 +123,37 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
123
123
|
super
|
124
124
|
|
125
125
|
if required
|
126
|
-
|
126
|
+
if ActiveRecord.belongs_to_required_validates_foreign_key
|
127
|
+
model.validates_presence_of reflection.name, message: :required
|
128
|
+
else
|
129
|
+
condition = lambda { |record|
|
130
|
+
foreign_key = reflection.foreign_key
|
131
|
+
foreign_type = reflection.foreign_type
|
132
|
+
|
133
|
+
record.read_attribute(foreign_key).nil? ||
|
134
|
+
record.attribute_changed?(foreign_key) ||
|
135
|
+
(reflection.polymorphic? && (record.read_attribute(foreign_type).nil? || record.attribute_changed?(foreign_type)))
|
136
|
+
}
|
137
|
+
|
138
|
+
model.validates_presence_of reflection.name, message: :required, if: condition
|
139
|
+
end
|
127
140
|
end
|
128
141
|
end
|
129
142
|
|
130
|
-
|
131
|
-
|
143
|
+
def self.define_change_tracking_methods(model, reflection)
|
144
|
+
model.generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
145
|
+
def #{reflection.name}_changed?
|
146
|
+
association(:#{reflection.name}).target_changed?
|
147
|
+
end
|
148
|
+
|
149
|
+
def #{reflection.name}_previously_changed?
|
150
|
+
association(:#{reflection.name}).target_previously_changed?
|
151
|
+
end
|
152
|
+
CODE
|
153
|
+
end
|
154
|
+
|
155
|
+
private_class_method :macro, :valid_options, :valid_dependent_options, :define_callbacks,
|
156
|
+
:define_validations, :define_change_tracking_methods, :add_counter_cache_callbacks,
|
157
|
+
:add_touch_callbacks, :add_default_callbacks, :add_destroy_callbacks
|
132
158
|
end
|
133
159
|
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) }
|
@@ -20,6 +20,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
20
20
|
attr_accessor :right_reflection
|
21
21
|
end
|
22
22
|
|
23
|
+
@table_name = nil
|
23
24
|
def self.table_name
|
24
25
|
# Table name needs to be resolved lazily
|
25
26
|
# because RHS class might not have been loaded
|
@@ -44,11 +45,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
|
|
44
45
|
def self.retrieve_connection
|
45
46
|
left_model.retrieve_connection
|
46
47
|
end
|
47
|
-
|
48
|
-
private
|
49
|
-
def self.suppress_composite_primary_key(pk)
|
50
|
-
pk unless pk.is_a?(Array)
|
51
|
-
end
|
52
48
|
}
|
53
49
|
|
54
50
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
@@ -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,12 +13,16 @@ 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}
|
20
20
|
association(:#{name}).force_reload_reader
|
21
21
|
end
|
22
|
+
|
23
|
+
def reset_#{name}
|
24
|
+
association(:#{name}).reset
|
25
|
+
end
|
22
26
|
CODE
|
23
27
|
end
|
24
28
|
|
@@ -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
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
#
|
15
17
|
# The CollectionAssociation class provides common methods to the collections
|
16
18
|
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
|
17
|
-
# the
|
19
|
+
# the <tt>:through association</tt> option.
|
18
20
|
#
|
19
21
|
# You need to be careful with assumptions regarding the target: The proxy
|
20
22
|
# does not fetch records from the database until it needs them, but new
|
@@ -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
|
@@ -44,11 +48,11 @@ module ActiveRecord
|
|
44
48
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
49
|
def ids_reader
|
46
50
|
if loaded?
|
47
|
-
target.pluck(reflection.association_primary_key)
|
51
|
+
target.pluck(*reflection.association_primary_key)
|
48
52
|
elsif !target.empty?
|
49
|
-
load_target.pluck(reflection.association_primary_key)
|
53
|
+
load_target.pluck(*reflection.association_primary_key)
|
50
54
|
else
|
51
|
-
@association_ids ||= scope.pluck(reflection.association_primary_key)
|
55
|
+
@association_ids ||= scope.pluck(*reflection.association_primary_key)
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
@@ -57,14 +61,20 @@ module ActiveRecord
|
|
57
61
|
primary_key = reflection.association_primary_key
|
58
62
|
pk_type = klass.type_for_attribute(primary_key)
|
59
63
|
ids = Array(ids).compact_blank
|
60
|
-
ids.map! { |
|
64
|
+
ids.map! { |id| pk_type.cast(id) }
|
61
65
|
|
62
|
-
records = klass.
|
63
|
-
|
66
|
+
records = if klass.composite_primary_key?
|
67
|
+
klass.where(primary_key => ids).index_by do |record|
|
68
|
+
primary_key.map { |primary_key| record._read_attribute(primary_key) }
|
69
|
+
end
|
70
|
+
else
|
71
|
+
klass.where(primary_key => ids).index_by do |record|
|
72
|
+
record._read_attribute(primary_key)
|
73
|
+
end
|
64
74
|
end.values_at(*ids).compact
|
65
75
|
|
66
76
|
if records.size != ids.size
|
67
|
-
found_ids = records.map { |record| record.
|
77
|
+
found_ids = records.map { |record| record._read_attribute(primary_key) }
|
68
78
|
not_found_ids = ids - found_ids
|
69
79
|
klass.all.raise_record_not_found_exception!(ids, records.size, ids.size, primary_key, not_found_ids)
|
70
80
|
else
|
@@ -75,7 +85,7 @@ module ActiveRecord
|
|
75
85
|
def reset
|
76
86
|
super
|
77
87
|
@target = []
|
78
|
-
@replaced_or_added_targets = Set.new
|
88
|
+
@replaced_or_added_targets = Set.new.compare_by_identity
|
79
89
|
@association_ids = nil
|
80
90
|
end
|
81
91
|
|
@@ -115,28 +125,13 @@ module ActiveRecord
|
|
115
125
|
def concat(*records)
|
116
126
|
records = records.flatten
|
117
127
|
if owner.new_record?
|
118
|
-
load_target
|
128
|
+
skip_strict_loading { load_target }
|
119
129
|
concat_records(records)
|
120
130
|
else
|
121
131
|
transaction { concat_records(records) }
|
122
132
|
end
|
123
133
|
end
|
124
134
|
|
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
135
|
# Removes all records from the association without calling callbacks
|
141
136
|
# on the associated records. It honors the +:dependent+ option. However
|
142
137
|
# if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+
|
@@ -191,7 +186,7 @@ module ActiveRecord
|
|
191
186
|
end
|
192
187
|
|
193
188
|
# Deletes the +records+ and removes them from this association calling
|
194
|
-
# +before_remove
|
189
|
+
# +before_remove+, +after_remove+, +before_destroy+ and +after_destroy+ callbacks.
|
195
190
|
#
|
196
191
|
# Note that this method removes records from the database ignoring the
|
197
192
|
# +:dependent+ option.
|
@@ -244,7 +239,7 @@ module ActiveRecord
|
|
244
239
|
# and delete/add only records that have changed.
|
245
240
|
def replace(other_array)
|
246
241
|
other_array.each { |val| raise_on_type_mismatch!(val) }
|
247
|
-
original_target = load_target.dup
|
242
|
+
original_target = skip_strict_loading { load_target }.dup
|
248
243
|
|
249
244
|
if owner.new_record?
|
250
245
|
replace_records(other_array, original_target)
|
@@ -284,9 +279,11 @@ module ActiveRecord
|
|
284
279
|
end
|
285
280
|
|
286
281
|
def target=(record)
|
287
|
-
return super unless
|
282
|
+
return super unless reflection.klass.has_many_inversing
|
288
283
|
|
289
284
|
case record
|
285
|
+
when nil
|
286
|
+
# It's not possible to remove the record from the inverse association.
|
290
287
|
when Array
|
291
288
|
super
|
292
289
|
else
|
@@ -306,13 +303,17 @@ module ActiveRecord
|
|
306
303
|
|
307
304
|
def find_from_target?
|
308
305
|
loaded? ||
|
309
|
-
owner.strict_loading? ||
|
306
|
+
(owner.strict_loading? && owner.strict_loading_all?) ||
|
310
307
|
reflection.strict_loading? ||
|
311
308
|
owner.new_record? ||
|
312
309
|
target.any? { |record| record.new_record? || record.changed? }
|
313
310
|
end
|
314
311
|
|
315
312
|
private
|
313
|
+
def transaction(&block)
|
314
|
+
reflection.klass.transaction(&block)
|
315
|
+
end
|
316
|
+
|
316
317
|
# We have some records loaded from the database (persisted) and some that are
|
317
318
|
# in-memory (memory). The same record may be represented in the persisted array
|
318
319
|
# and in the memory array.
|
@@ -325,13 +326,12 @@ module ActiveRecord
|
|
325
326
|
# * Otherwise, attributes should have the value found in the database
|
326
327
|
def merge_target_lists(persisted, memory)
|
327
328
|
return persisted if memory.empty?
|
328
|
-
return memory if persisted.empty?
|
329
329
|
|
330
330
|
persisted.map! do |record|
|
331
331
|
if mem_record = memory.delete(record)
|
332
332
|
|
333
|
-
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save).each do |name|
|
334
|
-
mem_record
|
333
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changed_attribute_names_to_save - mem_record.class._attr_readonly).each do |name|
|
334
|
+
mem_record._write_attribute(name, record[name])
|
335
335
|
end
|
336
336
|
|
337
337
|
mem_record
|
@@ -345,7 +345,7 @@ module ActiveRecord
|
|
345
345
|
|
346
346
|
def _create_record(attributes, raise = false, &block)
|
347
347
|
unless owner.persisted?
|
348
|
-
raise ActiveRecord::RecordNotSaved
|
348
|
+
raise ActiveRecord::RecordNotSaved.new("You cannot call create unless the parent is saved", owner)
|
349
349
|
end
|
350
350
|
|
351
351
|
if attributes.is_a?(Array)
|
@@ -489,7 +489,11 @@ module ActiveRecord
|
|
489
489
|
|
490
490
|
def callbacks_for(callback_name)
|
491
491
|
full_callback_name = "#{callback_name}_for_#{reflection.name}"
|
492
|
-
owner.class.
|
492
|
+
if owner.class.respond_to?(full_callback_name)
|
493
|
+
owner.class.send(full_callback_name)
|
494
|
+
else
|
495
|
+
[]
|
496
|
+
end
|
493
497
|
end
|
494
498
|
|
495
499
|
def include_in_memory?(record)
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Associations
|
5
|
+
# = Active Record Collection Proxy
|
6
|
+
#
|
5
7
|
# Collection proxies in Active Record are middlemen between an
|
6
8
|
# <tt>association</tt>, and its <tt>target</tt> result set.
|
7
9
|
#
|
@@ -27,7 +29,7 @@ module ActiveRecord
|
|
27
29
|
# is computed directly through SQL and does not trigger by itself the
|
28
30
|
# instantiation of the actual post records.
|
29
31
|
class CollectionProxy < Relation
|
30
|
-
def initialize(klass, association, **)
|
32
|
+
def initialize(klass, association, **) # :nodoc:
|
31
33
|
@association = association
|
32
34
|
super klass
|
33
35
|
|
@@ -46,7 +48,7 @@ module ActiveRecord
|
|
46
48
|
# Returns +true+ if the association has been loaded, otherwise +false+.
|
47
49
|
#
|
48
50
|
# person.pets.loaded? # => false
|
49
|
-
# person.pets
|
51
|
+
# person.pets.records
|
50
52
|
# person.pets.loaded? # => true
|
51
53
|
def loaded?
|
52
54
|
@association.loaded?
|
@@ -94,12 +96,12 @@ module ActiveRecord
|
|
94
96
|
# receive:
|
95
97
|
#
|
96
98
|
# person.pets.select(:name).first.person_id
|
97
|
-
# # => ActiveModel::MissingAttributeError: missing attribute
|
99
|
+
# # => ActiveModel::MissingAttributeError: missing attribute 'person_id' for Pet
|
98
100
|
#
|
99
|
-
# *Second:* You can pass a block so it can be used just like Array#select
|
101
|
+
# *Second:* You can pass a block so it can be used just like <tt>Array#select</tt>.
|
100
102
|
# This builds an array of objects from the database for the scope,
|
101
103
|
# converting them into an array and iterating through them using
|
102
|
-
# Array#select
|
104
|
+
# <tt>Array#select</tt>.
|
103
105
|
#
|
104
106
|
# person.pets.select { |pet| /oo/.match?(pet.name) }
|
105
107
|
# # => [
|
@@ -108,7 +110,7 @@ module ActiveRecord
|
|
108
110
|
# # ]
|
109
111
|
|
110
112
|
# Finds an object in the collection responding to the +id+. Uses the same
|
111
|
-
# rules as ActiveRecord::
|
113
|
+
# rules as ActiveRecord::FinderMethods.find. Returns ActiveRecord::RecordNotFound
|
112
114
|
# error if the object cannot be found.
|
113
115
|
#
|
114
116
|
# class Person < ActiveRecord::Base
|
@@ -218,7 +220,7 @@ module ActiveRecord
|
|
218
220
|
# :call-seq:
|
219
221
|
# third_to_last()
|
220
222
|
#
|
221
|
-
# Same as #
|
223
|
+
# Same as #last except returns only the third-to-last record.
|
222
224
|
|
223
225
|
##
|
224
226
|
# :method: second_to_last
|
@@ -226,7 +228,7 @@ module ActiveRecord
|
|
226
228
|
# :call-seq:
|
227
229
|
# second_to_last()
|
228
230
|
#
|
229
|
-
# Same as #
|
231
|
+
# Same as #last except returns only the second-to-last record.
|
230
232
|
|
231
233
|
# Returns the last record, or the last +n+ records, from the collection.
|
232
234
|
# If the collection is empty, the first form returns +nil+, and the second
|
@@ -260,7 +262,7 @@ module ActiveRecord
|
|
260
262
|
end
|
261
263
|
|
262
264
|
# Gives a record (or N records if a parameter is supplied) from the collection
|
263
|
-
# using the same rules as
|
265
|
+
# using the same rules as ActiveRecord::FinderMethods.take.
|
264
266
|
#
|
265
267
|
# class Person < ActiveRecord::Base
|
266
268
|
# has_many :pets
|
@@ -382,7 +384,7 @@ module ActiveRecord
|
|
382
384
|
# # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
|
383
385
|
#
|
384
386
|
# If the supplied array has an incorrect association type, it raises
|
385
|
-
# an
|
387
|
+
# an ActiveRecord::AssociationTypeMismatch error:
|
386
388
|
#
|
387
389
|
# person.pets.replace(["doo", "ggie", "gaga"])
|
388
390
|
# # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
|
@@ -475,7 +477,7 @@ module ActiveRecord
|
|
475
477
|
|
476
478
|
# Deletes the records of the collection directly from the database
|
477
479
|
# ignoring the +:dependent+ option. Records are instantiated and it
|
478
|
-
# invokes +before_remove+, +after_remove
|
480
|
+
# invokes +before_remove+, +after_remove+, +before_destroy+, and
|
479
481
|
# +after_destroy+ callbacks.
|
480
482
|
#
|
481
483
|
# class Person < ActiveRecord::Base
|
@@ -813,7 +815,7 @@ module ActiveRecord
|
|
813
815
|
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
|
814
816
|
# it is equivalent to <tt>!collection.exists?</tt>. If the collection has
|
815
817
|
# not already been loaded and you are going to fetch the records anyway it
|
816
|
-
# is better to check <tt>collection.
|
818
|
+
# is better to check <tt>collection.load.empty?</tt>.
|
817
819
|
#
|
818
820
|
# class Person < ActiveRecord::Base
|
819
821
|
# has_many :pets
|
@@ -849,6 +851,11 @@ module ActiveRecord
|
|
849
851
|
# person.pets.count # => 1
|
850
852
|
# person.pets.any? # => true
|
851
853
|
#
|
854
|
+
# Calling it without a block when the collection is not yet
|
855
|
+
# loaded is equivalent to <tt>collection.exists?</tt>.
|
856
|
+
# If you're going to load the collection anyway, it is better
|
857
|
+
# to call <tt>collection.load.any?</tt> to avoid an extra query.
|
858
|
+
#
|
852
859
|
# You can also pass a +block+ to define criteria. The behavior
|
853
860
|
# is the same, it returns true if the collection based on the
|
854
861
|
# criteria is not empty.
|
@@ -925,7 +932,7 @@ module ActiveRecord
|
|
925
932
|
@association
|
926
933
|
end
|
927
934
|
|
928
|
-
# Returns a
|
935
|
+
# Returns a Relation object for the records in this association
|
929
936
|
def scope
|
930
937
|
@scope ||= @association.scope
|
931
938
|
end
|
@@ -950,10 +957,13 @@ module ActiveRecord
|
|
950
957
|
# person.pets == other
|
951
958
|
# # => true
|
952
959
|
#
|
960
|
+
#
|
961
|
+
# Note that unpersisted records can still be seen as equal:
|
962
|
+
#
|
953
963
|
# other = [Pet.new(id: 1), Pet.new(id: 2)]
|
954
964
|
#
|
955
965
|
# person.pets == other
|
956
|
-
# # =>
|
966
|
+
# # => true
|
957
967
|
def ==(other)
|
958
968
|
load_target == other
|
959
969
|
end
|
@@ -1097,13 +1107,18 @@ module ActiveRecord
|
|
1097
1107
|
super
|
1098
1108
|
end
|
1099
1109
|
|
1110
|
+
def pretty_print(pp) # :nodoc:
|
1111
|
+
load_target if find_from_target?
|
1112
|
+
super
|
1113
|
+
end
|
1114
|
+
|
1100
1115
|
delegate_methods = [
|
1101
1116
|
QueryMethods,
|
1102
1117
|
SpawnMethods,
|
1103
1118
|
].flat_map { |klass|
|
1104
1119
|
klass.public_instance_methods(false)
|
1105
1120
|
} - self.public_instance_methods(false) - [:select] + [
|
1106
|
-
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
|
1121
|
+
:scoping, :values, :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all, :load_async
|
1107
1122
|
]
|
1108
1123
|
|
1109
1124
|
delegate(*delegate_methods, to: :scope)
|
@@ -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
|
@@ -12,7 +12,7 @@ module ActiveRecord::Associations
|
|
12
12
|
|
13
13
|
def nullified_owner_attributes
|
14
14
|
Hash.new.tap do |attrs|
|
15
|
-
|
15
|
+
Array(reflection.foreign_key).each { |foreign_key| attrs[foreign_key] = nil }
|
16
16
|
attrs[reflection.type] = nil if reflection.type.present?
|
17
17
|
end
|
18
18
|
end
|
@@ -22,8 +22,15 @@ module ActiveRecord::Associations
|
|
22
22
|
def set_owner_attributes(record)
|
23
23
|
return if options[:through]
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
primary_key_attribute_names = Array(reflection.join_primary_key)
|
26
|
+
foreign_key_attribute_names = Array(reflection.join_foreign_key)
|
27
|
+
|
28
|
+
primary_key_foreign_key_pairs = primary_key_attribute_names.zip(foreign_key_attribute_names)
|
29
|
+
|
30
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
31
|
+
value = owner._read_attribute(foreign_key)
|
32
|
+
record._write_attribute(primary_key, value)
|
33
|
+
end
|
27
34
|
|
28
35
|
if reflection.type
|
29
36
|
record._write_attribute(reflection.type, owner.class.polymorphic_name)
|