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
@@ -23,7 +23,7 @@ module ActiveRecord
|
|
23
23
|
|
24
24
|
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
25
25
|
|
26
|
-
class GeneratedAttributeMethods < Module
|
26
|
+
class GeneratedAttributeMethods < Module # :nodoc:
|
27
27
|
include Mutex_m
|
28
28
|
end
|
29
29
|
|
@@ -33,26 +33,99 @@ module ActiveRecord
|
|
33
33
|
Base.instance_methods +
|
34
34
|
Base.private_instance_methods -
|
35
35
|
Base.superclass.instance_methods -
|
36
|
-
Base.superclass.private_instance_methods
|
36
|
+
Base.superclass.private_instance_methods +
|
37
|
+
%i[__id__ dup freeze frozen? hash class clone]
|
37
38
|
).map { |m| -m.to_s }.to_set.freeze
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
module ClassMethods
|
42
|
-
def inherited(child_class) #:nodoc:
|
43
|
-
child_class.initialize_generated_modules
|
44
|
-
super
|
45
|
-
end
|
46
|
-
|
47
43
|
def initialize_generated_modules # :nodoc:
|
48
44
|
@generated_attribute_methods = const_set(:GeneratedAttributeMethods, GeneratedAttributeMethods.new)
|
49
45
|
private_constant :GeneratedAttributeMethods
|
50
46
|
@attribute_methods_generated = false
|
47
|
+
@alias_attributes_mass_generated = false
|
51
48
|
include @generated_attribute_methods
|
52
49
|
|
53
50
|
super
|
54
51
|
end
|
55
52
|
|
53
|
+
def alias_attribute(new_name, old_name)
|
54
|
+
super
|
55
|
+
|
56
|
+
if @alias_attributes_mass_generated
|
57
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
58
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eagerly_generate_alias_attribute_methods(_new_name, _old_name) # :nodoc:
|
64
|
+
# alias attributes in Active Record are lazily generated
|
65
|
+
end
|
66
|
+
|
67
|
+
def generate_alias_attributes # :nodoc:
|
68
|
+
superclass.generate_alias_attributes unless superclass == Base
|
69
|
+
return false if @alias_attributes_mass_generated
|
70
|
+
|
71
|
+
generated_attribute_methods.synchronize do
|
72
|
+
return if @alias_attributes_mass_generated
|
73
|
+
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |code_generator|
|
74
|
+
aliases_by_attribute_name.each do |old_name, new_names|
|
75
|
+
new_names.each do |new_name|
|
76
|
+
generate_alias_attribute_methods(code_generator, new_name, old_name)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
@alias_attributes_mass_generated = true
|
82
|
+
end
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_alias_attribute_methods(code_generator, new_name, old_name) # :nodoc:
|
87
|
+
attribute_method_patterns.each do |pattern|
|
88
|
+
alias_attribute_method_definition(code_generator, pattern, new_name, old_name)
|
89
|
+
end
|
90
|
+
attribute_method_patterns_cache.clear
|
91
|
+
end
|
92
|
+
|
93
|
+
def alias_attribute_method_definition(code_generator, pattern, new_name, old_name) # :nodoc:
|
94
|
+
method_name = pattern.method_name(new_name).to_s
|
95
|
+
target_name = pattern.method_name(old_name).to_s
|
96
|
+
old_name = old_name.to_s
|
97
|
+
|
98
|
+
method_defined = method_defined?(target_name) || private_method_defined?(target_name)
|
99
|
+
manually_defined = method_defined &&
|
100
|
+
!self.instance_method(target_name).owner.is_a?(GeneratedAttributeMethods)
|
101
|
+
reserved_method_name = ::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(target_name)
|
102
|
+
|
103
|
+
if !abstract_class? && !has_attribute?(old_name)
|
104
|
+
# We only need to issue this deprecation warning once, so we issue it when defining the original reader method.
|
105
|
+
should_warn = target_name == old_name
|
106
|
+
if should_warn
|
107
|
+
ActiveRecord.deprecator.warn(
|
108
|
+
"#{self} model aliases `#{old_name}`, but `#{old_name}` is not an attribute. " \
|
109
|
+
"Starting in Rails 7.2, alias_attribute with non-attribute targets will raise. " \
|
110
|
+
"Use `alias_method :#{new_name}, :#{old_name}` or define the method manually."
|
111
|
+
)
|
112
|
+
end
|
113
|
+
super
|
114
|
+
elsif manually_defined && !reserved_method_name
|
115
|
+
aliased_method_redefined_as_well = method_defined_within?(method_name, self)
|
116
|
+
return if aliased_method_redefined_as_well
|
117
|
+
|
118
|
+
ActiveRecord.deprecator.warn(
|
119
|
+
"#{self} model aliases `#{old_name}` and has a method called `#{target_name}` defined. " \
|
120
|
+
"Starting in Rails 7.2 `#{method_name}` will not be calling `#{target_name}` anymore. " \
|
121
|
+
"You may want to additionally define `#{method_name}` to preserve the current behavior."
|
122
|
+
)
|
123
|
+
super
|
124
|
+
else
|
125
|
+
define_attribute_method_pattern(pattern, old_name, owner: code_generator, as: new_name, override: true)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
56
129
|
# Generates all the attribute related methods for columns in the database
|
57
130
|
# accessors, mutators and query methods.
|
58
131
|
def define_attribute_methods # :nodoc:
|
@@ -65,12 +138,18 @@ module ActiveRecord
|
|
65
138
|
super(attribute_names)
|
66
139
|
@attribute_methods_generated = true
|
67
140
|
end
|
141
|
+
true
|
142
|
+
end
|
143
|
+
|
144
|
+
def attribute_methods_generated? # :nodoc:
|
145
|
+
@attribute_methods_generated && @alias_attributes_mass_generated
|
68
146
|
end
|
69
147
|
|
70
148
|
def undefine_attribute_methods # :nodoc:
|
71
149
|
generated_attribute_methods.synchronize do
|
72
150
|
super if defined?(@attribute_methods_generated) && @attribute_methods_generated
|
73
151
|
@attribute_methods_generated = false
|
152
|
+
@alias_attributes_mass_generated = false
|
74
153
|
end
|
75
154
|
end
|
76
155
|
|
@@ -97,7 +176,7 @@ module ActiveRecord
|
|
97
176
|
super
|
98
177
|
else
|
99
178
|
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
|
100
|
-
# defines its own attribute method, then we don't want to
|
179
|
+
# defines its own attribute method, then we don't want to override that.
|
101
180
|
defined = method_defined_within?(method_name, superclass, Base) &&
|
102
181
|
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
|
103
182
|
defined || super
|
@@ -186,6 +265,16 @@ module ActiveRecord
|
|
186
265
|
def _has_attribute?(attr_name) # :nodoc:
|
187
266
|
attribute_types.key?(attr_name)
|
188
267
|
end
|
268
|
+
|
269
|
+
private
|
270
|
+
def inherited(child_class)
|
271
|
+
super
|
272
|
+
child_class.initialize_generated_modules
|
273
|
+
child_class.class_eval do
|
274
|
+
@alias_attributes_mass_generated = false
|
275
|
+
@attribute_names = nil
|
276
|
+
end
|
277
|
+
end
|
189
278
|
end
|
190
279
|
|
191
280
|
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
@@ -267,9 +356,8 @@ module ActiveRecord
|
|
267
356
|
|
268
357
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
269
358
|
# attribute +attr_name+. String attributes are truncated up to 50
|
270
|
-
# characters
|
271
|
-
#
|
272
|
-
# <tt>#inspect</tt> without modification.
|
359
|
+
# characters. Other attributes return the value of <tt>#inspect</tt>
|
360
|
+
# without modification.
|
273
361
|
#
|
274
362
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
275
363
|
#
|
@@ -277,7 +365,7 @@ module ActiveRecord
|
|
277
365
|
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
|
278
366
|
#
|
279
367
|
# person.attribute_for_inspect(:created_at)
|
280
|
-
# # => "\"2012-10-22 00:15:07\""
|
368
|
+
# # => "\"2012-10-22 00:15:07.000000000 +0000\""
|
281
369
|
#
|
282
370
|
# person.attribute_for_inspect(:tag_ids)
|
283
371
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
@@ -310,37 +398,40 @@ module ActiveRecord
|
|
310
398
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
311
399
|
end
|
312
400
|
|
313
|
-
# Returns the value of the attribute identified by
|
314
|
-
#
|
315
|
-
#
|
316
|
-
#
|
317
|
-
# Note: +:id+ is always present.
|
401
|
+
# Returns the value of the attribute identified by +attr_name+ after it has
|
402
|
+
# been type cast. (For information about specific type casting behavior, see
|
403
|
+
# the types under ActiveModel::Type.)
|
318
404
|
#
|
319
405
|
# class Person < ActiveRecord::Base
|
320
406
|
# belongs_to :organization
|
321
407
|
# end
|
322
408
|
#
|
323
|
-
# person = Person.new(name:
|
324
|
-
# person[:name]
|
325
|
-
# person[:
|
409
|
+
# person = Person.new(name: "Francesco", date_of_birth: "2004-12-12")
|
410
|
+
# person[:name] # => "Francesco"
|
411
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
412
|
+
# person[:organization_id] # => nil
|
326
413
|
#
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
414
|
+
# Raises ActiveModel::MissingAttributeError if the attribute is missing.
|
415
|
+
# Note, however, that the +id+ attribute will never be considered missing.
|
416
|
+
#
|
417
|
+
# person = Person.select(:name).first
|
418
|
+
# person[:name] # => "Francesco"
|
419
|
+
# person[:date_of_birth] # => ActiveModel::MissingAttributeError: missing attribute 'date_of_birth' for Person
|
420
|
+
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute 'organization_id' for Person
|
421
|
+
# person[:id] # => nil
|
330
422
|
def [](attr_name)
|
331
423
|
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
|
332
424
|
end
|
333
425
|
|
334
|
-
# Updates the attribute identified by
|
335
|
-
#
|
426
|
+
# Updates the attribute identified by +attr_name+ using the specified
|
427
|
+
# +value+. The attribute value will be type cast upon being read.
|
336
428
|
#
|
337
429
|
# class Person < ActiveRecord::Base
|
338
430
|
# end
|
339
431
|
#
|
340
432
|
# person = Person.new
|
341
|
-
# person[:
|
342
|
-
# person[:
|
343
|
-
# person[:age].class # => Integer
|
433
|
+
# person[:date_of_birth] = "2004-12-12"
|
434
|
+
# person[:date_of_birth] # => Date.new(2004, 12, 12)
|
344
435
|
def []=(attr_name, value)
|
345
436
|
write_attribute(attr_name, value)
|
346
437
|
end
|
@@ -361,10 +452,9 @@ module ActiveRecord
|
|
361
452
|
# end
|
362
453
|
#
|
363
454
|
# private
|
364
|
-
#
|
365
|
-
#
|
366
|
-
#
|
367
|
-
# end
|
455
|
+
# def print_accessed_fields
|
456
|
+
# p @posts.first.accessed_fields
|
457
|
+
# end
|
368
458
|
# end
|
369
459
|
#
|
370
460
|
# Which allows you to quickly change your code to:
|
@@ -379,31 +469,62 @@ module ActiveRecord
|
|
379
469
|
end
|
380
470
|
|
381
471
|
private
|
472
|
+
def respond_to_missing?(name, include_private = false)
|
473
|
+
if self.class.define_attribute_methods
|
474
|
+
# Some methods weren't defined yet.
|
475
|
+
return true if self.class.method_defined?(name)
|
476
|
+
return true if include_private && self.class.private_method_defined?(name)
|
477
|
+
end
|
478
|
+
|
479
|
+
super
|
480
|
+
end
|
481
|
+
|
482
|
+
def method_missing(name, ...)
|
483
|
+
unless self.class.attribute_methods_generated?
|
484
|
+
if self.class.method_defined?(name)
|
485
|
+
# The method is explicitly defined in the model, but calls a generated
|
486
|
+
# method with super. So we must resume the call chain at the right setp.
|
487
|
+
last_method = method(name)
|
488
|
+
last_method = last_method.super_method while last_method.super_method
|
489
|
+
self.class.define_attribute_methods
|
490
|
+
if last_method.super_method
|
491
|
+
return last_method.super_method.call(...)
|
492
|
+
end
|
493
|
+
elsif self.class.define_attribute_methods | self.class.generate_alias_attributes
|
494
|
+
# Some attribute methods weren't generated yet, we retry the call
|
495
|
+
return public_send(name, ...)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
super
|
500
|
+
end
|
501
|
+
|
382
502
|
def attribute_method?(attr_name)
|
383
503
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
384
504
|
defined?(@attributes) && @attributes.key?(attr_name)
|
385
505
|
end
|
386
506
|
|
387
507
|
def attributes_with_values(attribute_names)
|
388
|
-
attribute_names.index_with
|
389
|
-
_read_attribute(name)
|
390
|
-
end
|
508
|
+
attribute_names.index_with { |name| @attributes[name] }
|
391
509
|
end
|
392
510
|
|
393
|
-
# Filters the primary keys
|
511
|
+
# Filters the primary keys, readonly attributes and virtual columns from the attribute names.
|
394
512
|
def attributes_for_update(attribute_names)
|
395
513
|
attribute_names &= self.class.column_names
|
396
514
|
attribute_names.delete_if do |name|
|
397
|
-
self.class.readonly_attribute?(name)
|
515
|
+
self.class.readonly_attribute?(name) ||
|
516
|
+
self.class.counter_cache_column?(name) ||
|
517
|
+
column_for_attribute(name).virtual?
|
398
518
|
end
|
399
519
|
end
|
400
520
|
|
401
|
-
# Filters out the primary keys, from the attribute names, when the primary
|
521
|
+
# Filters out the virtual columns and also primary keys, from the attribute names, when the primary
|
402
522
|
# key is to be generated (e.g. the id attribute has no value).
|
403
523
|
def attributes_for_create(attribute_names)
|
404
524
|
attribute_names &= self.class.column_names
|
405
525
|
attribute_names.delete_if do |name|
|
406
|
-
pk_attribute?(name) && id.nil?
|
526
|
+
(pk_attribute?(name) && id.nil?) ||
|
527
|
+
column_for_attribute(name).virtual?
|
407
528
|
end
|
408
529
|
end
|
409
530
|
|
@@ -414,7 +535,7 @@ module ActiveRecord
|
|
414
535
|
inspected_value = if value.is_a?(String) && value.length > 50
|
415
536
|
"#{value[0, 50]}...".inspect
|
416
537
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
417
|
-
%("#{value.
|
538
|
+
%("#{value.to_fs(:inspect)}")
|
418
539
|
else
|
419
540
|
value.inspect
|
420
541
|
end
|
@@ -10,11 +10,8 @@ module ActiveRecord
|
|
10
10
|
included do
|
11
11
|
class_attribute :attributes_to_define_after_schema_loads, instance_accessor: false, default: {} # :internal:
|
12
12
|
end
|
13
|
-
|
13
|
+
# = Active Record \Attributes
|
14
14
|
module ClassMethods
|
15
|
-
##
|
16
|
-
# :call-seq: attribute(name, cast_type = nil, **options)
|
17
|
-
#
|
18
15
|
# Defines an attribute with a type on this model. It will override the
|
19
16
|
# type of existing attributes if needed. This allows control over how
|
20
17
|
# values are converted to and from SQL when assigned to a model. It also
|
@@ -197,10 +194,10 @@ module ActiveRecord
|
|
197
194
|
# end
|
198
195
|
#
|
199
196
|
# Product.where(price_in_bitcoins: Money.new(5, "USD"))
|
200
|
-
# #
|
197
|
+
# # SELECT * FROM products WHERE price_in_bitcoins = 0.02230
|
201
198
|
#
|
202
199
|
# Product.where(price_in_bitcoins: Money.new(5, "GBP"))
|
203
|
-
# #
|
200
|
+
# # SELECT * FROM products WHERE price_in_bitcoins = 0.03412
|
204
201
|
#
|
205
202
|
# ==== Dirty Tracking
|
206
203
|
#
|
@@ -208,14 +205,31 @@ module ActiveRecord
|
|
208
205
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
209
206
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
210
207
|
# methods in ActiveModel::Type::Value for more details.
|
211
|
-
def attribute(name, cast_type = nil, **options
|
208
|
+
def attribute(name, cast_type = nil, default: NO_DEFAULT_PROVIDED, **options)
|
212
209
|
name = name.to_s
|
210
|
+
name = attribute_aliases[name] || name
|
211
|
+
|
213
212
|
reload_schema_from_cache
|
214
213
|
|
214
|
+
case cast_type
|
215
|
+
when Symbol
|
216
|
+
cast_type = Type.lookup(cast_type, **options, adapter: Type.adapter_name_from(self))
|
217
|
+
when nil
|
218
|
+
if (prev_cast_type, prev_default = attributes_to_define_after_schema_loads[name])
|
219
|
+
default = prev_default if default == NO_DEFAULT_PROVIDED
|
220
|
+
else
|
221
|
+
prev_cast_type = -> subtype { subtype }
|
222
|
+
end
|
223
|
+
|
224
|
+
cast_type = if block_given?
|
225
|
+
-> subtype { yield Proc === prev_cast_type ? prev_cast_type[subtype] : prev_cast_type }
|
226
|
+
else
|
227
|
+
prev_cast_type
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
215
231
|
self.attributes_to_define_after_schema_loads =
|
216
|
-
attributes_to_define_after_schema_loads.merge(
|
217
|
-
name => [cast_type || block, options]
|
218
|
-
)
|
232
|
+
attributes_to_define_after_schema_loads.merge(name => [cast_type, default])
|
219
233
|
end
|
220
234
|
|
221
235
|
# This is the low level API which sits beneath +attribute+. It only
|
@@ -248,8 +262,9 @@ module ActiveRecord
|
|
248
262
|
|
249
263
|
def load_schema! # :nodoc:
|
250
264
|
super
|
251
|
-
attributes_to_define_after_schema_loads.each do |name, (
|
252
|
-
|
265
|
+
attributes_to_define_after_schema_loads.each do |name, (cast_type, default)|
|
266
|
+
cast_type = cast_type[type_for_attribute(name)] if Proc === cast_type
|
267
|
+
define_attribute(name, cast_type, default: default)
|
253
268
|
end
|
254
269
|
end
|
255
270
|
|
@@ -272,32 +287,6 @@ module ActiveRecord
|
|
272
287
|
end
|
273
288
|
_default_attributes[name] = default_attribute
|
274
289
|
end
|
275
|
-
|
276
|
-
def decorate_attribute_type(attr_name, **default)
|
277
|
-
type, options = attributes_to_define_after_schema_loads[attr_name]
|
278
|
-
|
279
|
-
default.with_defaults!(default: options[:default]) if options&.key?(:default)
|
280
|
-
|
281
|
-
attribute(attr_name, **default) do |cast_type|
|
282
|
-
if type && !type.is_a?(Proc)
|
283
|
-
cast_type = _lookup_cast_type(attr_name, type, options)
|
284
|
-
end
|
285
|
-
|
286
|
-
yield cast_type
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
def _lookup_cast_type(name, type, options)
|
291
|
-
case type
|
292
|
-
when Symbol
|
293
|
-
adapter_name = ActiveRecord::Type.adapter_name_from(self)
|
294
|
-
ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
|
295
|
-
when Proc
|
296
|
-
type[type_for_attribute(name)]
|
297
|
-
else
|
298
|
-
type || type_for_attribute(name)
|
299
|
-
end
|
300
|
-
end
|
301
290
|
end
|
302
291
|
end
|
303
292
|
end
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
#
|
27
27
|
# Child records are validated unless <tt>:validate</tt> is +false+.
|
28
28
|
#
|
29
|
-
# == Callbacks
|
29
|
+
# == \Callbacks
|
30
30
|
#
|
31
31
|
# Association with autosave option defines several callbacks on your
|
32
32
|
# model (around_save, before_save, after_create, after_update). Please note that
|
@@ -138,7 +138,7 @@ module ActiveRecord
|
|
138
138
|
module AutosaveAssociation
|
139
139
|
extend ActiveSupport::Concern
|
140
140
|
|
141
|
-
module AssociationBuilderExtension
|
141
|
+
module AssociationBuilderExtension # :nodoc:
|
142
142
|
def self.build(model, reflection)
|
143
143
|
model.send(:add_autosave_association_callbacks, reflection)
|
144
144
|
end
|
@@ -150,25 +150,10 @@ module ActiveRecord
|
|
150
150
|
|
151
151
|
included do
|
152
152
|
Associations::Builder::Association.extensions << AssociationBuilderExtension
|
153
|
-
mattr_accessor :index_nested_attribute_errors, instance_writer: false, default: false
|
154
153
|
end
|
155
154
|
|
156
155
|
module ClassMethods # :nodoc:
|
157
156
|
private
|
158
|
-
if Module.method(:method_defined?).arity == 1 # MRI 2.5 and older
|
159
|
-
using Module.new {
|
160
|
-
refine Module do
|
161
|
-
def method_defined?(method, inherit = true)
|
162
|
-
if inherit
|
163
|
-
super(method)
|
164
|
-
else
|
165
|
-
instance_methods(false).include?(method.to_sym)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
}
|
170
|
-
end
|
171
|
-
|
172
157
|
def define_non_cyclic_method(name, &block)
|
173
158
|
return if method_defined?(name, false)
|
174
159
|
|
@@ -210,7 +195,7 @@ module ActiveRecord
|
|
210
195
|
after_create save_method
|
211
196
|
after_update save_method
|
212
197
|
elsif reflection.has_one?
|
213
|
-
|
198
|
+
define_non_cyclic_method(save_method) { save_has_one_association(reflection) }
|
214
199
|
# Configures two callbacks instead of a single after_save so that
|
215
200
|
# the model may rely on their execution order relative to its
|
216
201
|
# own callbacks.
|
@@ -288,6 +273,11 @@ module ActiveRecord
|
|
288
273
|
end
|
289
274
|
|
290
275
|
private
|
276
|
+
def init_internals
|
277
|
+
super
|
278
|
+
@_already_called = nil
|
279
|
+
end
|
280
|
+
|
291
281
|
# Returns the record for an association collection that should be validated
|
292
282
|
# or saved. If +autosave+ is +false+ only new records will be returned,
|
293
283
|
# unless the parent is/was a new record itself.
|
@@ -349,7 +339,7 @@ module ActiveRecord
|
|
349
339
|
|
350
340
|
unless valid = record.valid?(context)
|
351
341
|
if reflection.options[:autosave]
|
352
|
-
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord
|
342
|
+
indexed_attribute = !index.nil? && (reflection.options[:index_errors] || ActiveRecord.index_nested_attribute_errors)
|
353
343
|
|
354
344
|
record.errors.group_by_attribute.each { |attribute, errors|
|
355
345
|
attribute = normalize_reflection_attribute(indexed_attribute, reflection, index, attribute)
|
@@ -419,6 +409,8 @@ module ActiveRecord
|
|
419
409
|
saved = true
|
420
410
|
|
421
411
|
if autosave != false && (new_record_before_save || record.new_record?)
|
412
|
+
association.set_inverse_instance(record)
|
413
|
+
|
422
414
|
if autosave
|
423
415
|
saved = association.insert_record(record, false)
|
424
416
|
elsif !reflection.nested?
|
@@ -449,7 +441,9 @@ module ActiveRecord
|
|
449
441
|
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
|
450
442
|
def save_has_one_association(reflection)
|
451
443
|
association = association_instance_get(reflection.name)
|
452
|
-
|
444
|
+
return unless association && association.loaded?
|
445
|
+
|
446
|
+
record = association.load_target
|
453
447
|
|
454
448
|
if record && !record.destroyed?
|
455
449
|
autosave = reflection.options[:autosave]
|
@@ -457,14 +451,19 @@ module ActiveRecord
|
|
457
451
|
if autosave && record.marked_for_destruction?
|
458
452
|
record.destroy
|
459
453
|
elsif autosave != false
|
460
|
-
|
454
|
+
primary_key = Array(compute_primary_key(reflection, self)).map(&:to_s)
|
455
|
+
primary_key_value = primary_key.map { |key| _read_attribute(key) }
|
461
456
|
|
462
|
-
if (autosave && record.changed_for_autosave?) ||
|
457
|
+
if (autosave && record.changed_for_autosave?) || _record_changed?(reflection, record, primary_key_value)
|
463
458
|
unless reflection.through_reflection
|
464
|
-
|
465
|
-
|
466
|
-
|
459
|
+
foreign_key = Array(reflection.foreign_key)
|
460
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
461
|
+
|
462
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
463
|
+
association_id = _read_attribute(primary_key)
|
464
|
+
record[foreign_key] = association_id unless record[foreign_key] == association_id
|
467
465
|
end
|
466
|
+
association.set_inverse_instance(record)
|
468
467
|
end
|
469
468
|
|
470
469
|
saved = record.save(validate: !autosave)
|
@@ -476,16 +475,28 @@ module ActiveRecord
|
|
476
475
|
end
|
477
476
|
|
478
477
|
# If the record is new or it has changed, returns true.
|
479
|
-
def
|
478
|
+
def _record_changed?(reflection, record, key)
|
480
479
|
record.new_record? ||
|
481
|
-
association_foreign_key_changed?(reflection, record, key) ||
|
480
|
+
(association_foreign_key_changed?(reflection, record, key) ||
|
481
|
+
inverse_polymorphic_association_changed?(reflection, record)) ||
|
482
482
|
record.will_save_change_to_attribute?(reflection.foreign_key)
|
483
483
|
end
|
484
484
|
|
485
485
|
def association_foreign_key_changed?(reflection, record, key)
|
486
486
|
return false if reflection.through_reflection?
|
487
487
|
|
488
|
-
|
488
|
+
foreign_key = Array(reflection.foreign_key)
|
489
|
+
return false unless foreign_key.all? { |key| record._has_attribute?(key) }
|
490
|
+
|
491
|
+
foreign_key.map { |key| record._read_attribute(key) } != Array(key)
|
492
|
+
end
|
493
|
+
|
494
|
+
def inverse_polymorphic_association_changed?(reflection, record)
|
495
|
+
return false unless reflection.inverse_of&.polymorphic?
|
496
|
+
|
497
|
+
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
|
498
|
+
|
499
|
+
reflection.active_record != record.class.polymorphic_class_for(class_name)
|
489
500
|
end
|
490
501
|
|
491
502
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
@@ -500,14 +511,21 @@ module ActiveRecord
|
|
500
511
|
autosave = reflection.options[:autosave]
|
501
512
|
|
502
513
|
if autosave && record.marked_for_destruction?
|
503
|
-
|
514
|
+
foreign_key = Array(reflection.foreign_key)
|
515
|
+
foreign_key.each { |key| self[key] = nil }
|
504
516
|
record.destroy
|
505
517
|
elsif autosave != false
|
506
518
|
saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
|
507
519
|
|
508
520
|
if association.updated?
|
509
|
-
|
510
|
-
|
521
|
+
primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
|
522
|
+
foreign_key = Array(reflection.foreign_key)
|
523
|
+
|
524
|
+
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
|
525
|
+
primary_key_foreign_key_pairs.each do |primary_key, foreign_key|
|
526
|
+
association_id = record._read_attribute(primary_key)
|
527
|
+
self[foreign_key] = association_id unless self[foreign_key] == association_id
|
528
|
+
end
|
511
529
|
association.loaded!
|
512
530
|
end
|
513
531
|
|
@@ -516,6 +534,22 @@ module ActiveRecord
|
|
516
534
|
end
|
517
535
|
end
|
518
536
|
|
537
|
+
def compute_primary_key(reflection, record)
|
538
|
+
if primary_key_options = reflection.options[:primary_key]
|
539
|
+
primary_key_options
|
540
|
+
elsif reflection.options[:query_constraints] && (query_constraints = record.class.query_constraints_list)
|
541
|
+
query_constraints
|
542
|
+
elsif record.class.has_query_constraints? && !reflection.options[:foreign_key]
|
543
|
+
record.class.query_constraints_list
|
544
|
+
elsif record.class.composite_primary_key?
|
545
|
+
# If record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
546
|
+
primary_key = record.class.primary_key
|
547
|
+
primary_key.include?("id") ? "id" : primary_key
|
548
|
+
else
|
549
|
+
record.class.primary_key
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
519
553
|
def custom_validation_context?
|
520
554
|
validation_context && [:create, :update].exclude?(validation_context)
|
521
555
|
end
|