activerecord 6.1.7 → 7.2.2
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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- 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 +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +284 -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 +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- 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 +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- 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 +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- 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 +114 -54
- 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/interval.rb +1 -1
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- 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 +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -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 +3 -3
- 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 +175 -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 +170 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- 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 +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- 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 +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- data/lib/active_record/migration/default_strategy.rb +22 -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 +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- 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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- 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 +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -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 +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- 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 +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +118 -41
- data/lib/active_record/translation.rb +3 -5
- 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 -7
- 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/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- 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/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- 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 +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- 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/templates/create_table_migration.rb.tt +4 -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 +103 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/object/json"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module TokenFor
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
class_attribute :token_definitions, instance_accessor: false, instance_predicate: false, default: {}
|
11
|
+
class_attribute :generated_token_verifier, instance_accessor: false, instance_predicate: false
|
12
|
+
end
|
13
|
+
|
14
|
+
TokenDefinition = Struct.new(:defining_class, :purpose, :expires_in, :block) do # :nodoc:
|
15
|
+
def full_purpose
|
16
|
+
@full_purpose ||= [defining_class.name, purpose, expires_in].join("\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def message_verifier
|
20
|
+
defining_class.generated_token_verifier
|
21
|
+
end
|
22
|
+
|
23
|
+
def payload_for(model)
|
24
|
+
block ? [model.id, model.instance_eval(&block).as_json] : [model.id]
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_token(model)
|
28
|
+
message_verifier.generate(payload_for(model), expires_in: expires_in, purpose: full_purpose)
|
29
|
+
end
|
30
|
+
|
31
|
+
def resolve_token(token)
|
32
|
+
payload = message_verifier.verified(token, purpose: full_purpose)
|
33
|
+
model = yield(payload[0]) if payload
|
34
|
+
model if model && payload_for(model) == payload
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module RelationMethods
|
39
|
+
# Finds a record using a given +token+ for a predefined +purpose+. Returns
|
40
|
+
# +nil+ if the token is invalid or the record was not found.
|
41
|
+
def find_by_token_for(purpose, token)
|
42
|
+
raise UnknownPrimaryKey.new(self) unless model.primary_key
|
43
|
+
model.token_definitions.fetch(purpose).resolve_token(token) { |id| find_by(model.primary_key => id) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Finds a record using a given +token+ for a predefined +purpose+. Raises
|
47
|
+
# ActiveSupport::MessageVerifier::InvalidSignature if the token is invalid
|
48
|
+
# (e.g. expired, bad format, etc). Raises ActiveRecord::RecordNotFound if
|
49
|
+
# the token is valid but the record was not found.
|
50
|
+
def find_by_token_for!(purpose, token)
|
51
|
+
model.token_definitions.fetch(purpose).resolve_token(token) { |id| find(id) } ||
|
52
|
+
(raise ActiveSupport::MessageVerifier::InvalidSignature)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
# Defines the behavior of tokens generated for a specific +purpose+.
|
58
|
+
# A token can be generated by calling TokenFor#generate_token_for on a
|
59
|
+
# record. Later, that record can be fetched by calling #find_by_token_for
|
60
|
+
# (or #find_by_token_for!) with the same purpose and token.
|
61
|
+
#
|
62
|
+
# Tokens are signed so that they are tamper-proof. Thus they can be
|
63
|
+
# exposed to outside world as, for example, password reset tokens.
|
64
|
+
#
|
65
|
+
# By default, tokens do not expire. They can be configured to expire by
|
66
|
+
# specifying a duration via the +expires_in+ option. The duration becomes
|
67
|
+
# part of the token's signature, so changing the value of +expires_in+
|
68
|
+
# will automatically invalidate previously generated tokens.
|
69
|
+
#
|
70
|
+
# A block may also be specified. When generating a token with
|
71
|
+
# TokenFor#generate_token_for, the block will be evaluated in the context
|
72
|
+
# of the record, and its return value will be embedded in the token as
|
73
|
+
# JSON. Later, when fetching the record with #find_by_token_for, the block
|
74
|
+
# will be evaluated again in the context of the fetched record. If the two
|
75
|
+
# JSON values do not match, the token will be treated as invalid. Note
|
76
|
+
# that the value returned by the block <b>should not contain sensitive
|
77
|
+
# information</b> because it will be embedded in the token as
|
78
|
+
# <b>human-readable plaintext JSON</b>.
|
79
|
+
#
|
80
|
+
# ==== Examples
|
81
|
+
#
|
82
|
+
# class User < ActiveRecord::Base
|
83
|
+
# has_secure_password
|
84
|
+
#
|
85
|
+
# generates_token_for :password_reset, expires_in: 15.minutes do
|
86
|
+
# # Last 10 characters of password salt, which changes when password is updated:
|
87
|
+
# password_salt&.last(10)
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# user = User.first
|
92
|
+
#
|
93
|
+
# token = user.generate_token_for(:password_reset)
|
94
|
+
# User.find_by_token_for(:password_reset, token) # => user
|
95
|
+
# # 16 minutes later...
|
96
|
+
# User.find_by_token_for(:password_reset, token) # => nil
|
97
|
+
#
|
98
|
+
# token = user.generate_token_for(:password_reset)
|
99
|
+
# User.find_by_token_for(:password_reset, token) # => user
|
100
|
+
# user.update!(password: "new password")
|
101
|
+
# User.find_by_token_for(:password_reset, token) # => nil
|
102
|
+
def generates_token_for(purpose, expires_in: nil, &block)
|
103
|
+
self.token_definitions = token_definitions.merge(purpose => TokenDefinition.new(self, purpose, expires_in, block))
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_by_token_for(purpose, token) # :nodoc:
|
107
|
+
all.find_by_token_for(purpose, token)
|
108
|
+
end
|
109
|
+
|
110
|
+
def find_by_token_for!(purpose, token) # :nodoc:
|
111
|
+
all.find_by_token_for!(purpose, token)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Generates a token for a predefined +purpose+.
|
116
|
+
#
|
117
|
+
# Use ClassMethods#generates_token_for to define a token purpose and
|
118
|
+
# behavior.
|
119
|
+
def generate_token_for(purpose)
|
120
|
+
self.class.token_definitions.fetch(purpose).generate_token(self)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -24,9 +24,13 @@ module ActiveRecord
|
|
24
24
|
@_new_record_before_last_commit ||= false
|
25
25
|
|
26
26
|
# touch the parents as we are not calling the after_save callbacks
|
27
|
-
self.class.reflect_on_all_associations
|
27
|
+
self.class.reflect_on_all_associations.each do |r|
|
28
28
|
if touch = r.options[:touch]
|
29
|
-
|
29
|
+
if r.macro == :belongs_to
|
30
|
+
ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, changes_to_save, r.foreign_key, r.name, touch)
|
31
|
+
elsif r.macro == :has_one
|
32
|
+
ActiveRecord::Associations::Builder::HasOne.touch_record(self, r.name, touch)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
end
|
@@ -42,6 +46,11 @@ module ActiveRecord
|
|
42
46
|
end
|
43
47
|
|
44
48
|
private
|
49
|
+
def init_internals
|
50
|
+
super
|
51
|
+
@_defer_touch_attrs = nil
|
52
|
+
end
|
53
|
+
|
45
54
|
def surreptitiously_touch(attr_names)
|
46
55
|
attr_names.each do |attr_name|
|
47
56
|
_write_attribute(attr_name, @_touch_time)
|
@@ -55,11 +64,7 @@ module ActiveRecord
|
|
55
64
|
end
|
56
65
|
|
57
66
|
def has_defer_touch_attrs?
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def belongs_to_touch_method
|
62
|
-
:touch_later
|
67
|
+
@_defer_touch_attrs.present?
|
63
68
|
end
|
64
69
|
end
|
65
70
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/digest"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
# Class specifies the interface to interact with the current transaction state.
|
7
|
+
#
|
8
|
+
# It can either map to an actual transaction/savepoint, or represent the
|
9
|
+
# absence of a transaction.
|
10
|
+
#
|
11
|
+
# == State
|
12
|
+
#
|
13
|
+
# We say that a transaction is _finalized_ when it wraps a real transaction
|
14
|
+
# that has been either committed or rolled back.
|
15
|
+
#
|
16
|
+
# A transaction is _open_ if it wraps a real transaction that is not finalized.
|
17
|
+
#
|
18
|
+
# On the other hand, a transaction is _closed_ when it is not open. That is,
|
19
|
+
# when it represents absence of transaction, or it wraps a real but finalized
|
20
|
+
# one.
|
21
|
+
#
|
22
|
+
# You can check whether a transaction is open or closed with the +open?+ and
|
23
|
+
# +closed?+ predicates:
|
24
|
+
#
|
25
|
+
# if Article.current_transaction.open?
|
26
|
+
# # We are inside a real and not finalized transaction.
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Closed transactions are `blank?` too.
|
30
|
+
#
|
31
|
+
# == Callbacks
|
32
|
+
#
|
33
|
+
# After updating the database state, you may sometimes need to perform some extra work, or reflect these
|
34
|
+
# changes in a remote system like clearing or updating a cache:
|
35
|
+
#
|
36
|
+
# def publish_article(article)
|
37
|
+
# article.update!(published: true)
|
38
|
+
# NotificationService.article_published(article)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# The above code works but has one important flaw, which is that it no longer works properly if called inside
|
42
|
+
# a transaction, as it will interact with the remote system before the changes are persisted:
|
43
|
+
#
|
44
|
+
# Article.transaction do
|
45
|
+
# article = create_article(article)
|
46
|
+
# publish_article(article)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# The callbacks offered by ActiveRecord::Transaction allow to rewriting this method in a way that is compatible
|
50
|
+
# with transactions:
|
51
|
+
#
|
52
|
+
# def publish_article(article)
|
53
|
+
# article.update!(published: true)
|
54
|
+
# Article.current_transaction.after_commit do
|
55
|
+
# NotificationService.article_published(article)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# In the above example, if +publish_article+ is called inside a transaction, the callback will be invoked
|
60
|
+
# after the transaction is successfully committed, and if called outside a transaction, the callback will be invoked
|
61
|
+
# immediately.
|
62
|
+
#
|
63
|
+
# == Caveats
|
64
|
+
#
|
65
|
+
# When using after_commit callbacks, it is important to note that if the callback raises an error, the transaction
|
66
|
+
# won't be rolled back as it was already committed. Relying solely on these to synchronize state between multiple
|
67
|
+
# systems may lead to consistency issues.
|
68
|
+
class Transaction
|
69
|
+
def initialize(internal_transaction) # :nodoc:
|
70
|
+
@internal_transaction = internal_transaction
|
71
|
+
@uuid = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
# Registers a block to be called after the transaction is fully committed.
|
75
|
+
#
|
76
|
+
# If there is no currently open transactions, the block is called
|
77
|
+
# immediately, unless the transaction is finalized, in which case attempting
|
78
|
+
# to register the callback raises ActiveRecord::ActiveRecordError.
|
79
|
+
#
|
80
|
+
# If the transaction has a parent transaction, the callback is transferred to
|
81
|
+
# the parent when the current transaction commits, or dropped when the current transaction
|
82
|
+
# is rolled back. This operation is repeated until the outermost transaction is reached.
|
83
|
+
#
|
84
|
+
# If the callback raises an error, the transaction remains committed.
|
85
|
+
def after_commit(&block)
|
86
|
+
if @internal_transaction.nil?
|
87
|
+
yield
|
88
|
+
else
|
89
|
+
@internal_transaction.after_commit(&block)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Registers a block to be called after the transaction is rolled back.
|
94
|
+
#
|
95
|
+
# If there is no currently open transactions, the block is not called. But
|
96
|
+
# if the transaction is finalized, attempting to register the callback
|
97
|
+
# raises ActiveRecord::ActiveRecordError.
|
98
|
+
#
|
99
|
+
# If the transaction is successfully committed but has a parent
|
100
|
+
# transaction, the callback is automatically added to the parent transaction.
|
101
|
+
#
|
102
|
+
# If the entire chain of nested transactions are all successfully committed,
|
103
|
+
# the block is never called.
|
104
|
+
#
|
105
|
+
# If the transaction is already finalized, attempting to register a callback
|
106
|
+
# will raise ActiveRecord::ActiveRecordError.
|
107
|
+
def after_rollback(&block)
|
108
|
+
@internal_transaction&.after_rollback(&block)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns true if the transaction exists and isn't finalized yet.
|
112
|
+
def open?
|
113
|
+
!closed?
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns true if the transaction doesn't exist or is finalized.
|
117
|
+
def closed?
|
118
|
+
@internal_transaction.nil? || @internal_transaction.state.finalized?
|
119
|
+
end
|
120
|
+
|
121
|
+
alias_method :blank?, :closed?
|
122
|
+
|
123
|
+
# Returns a UUID for this transaction or +nil+ if no transaction is open.
|
124
|
+
def uuid
|
125
|
+
if @internal_transaction
|
126
|
+
@uuid ||= Digest::UUID.uuid_v4
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
NULL_TRANSACTION = new(nil).freeze
|
131
|
+
end
|
132
|
+
end
|
@@ -4,7 +4,7 @@ module ActiveRecord
|
|
4
4
|
# See ActiveRecord::Transactions::ClassMethods for documentation.
|
5
5
|
module Transactions
|
6
6
|
extend ActiveSupport::Concern
|
7
|
-
|
7
|
+
# :nodoc:
|
8
8
|
ACTIONS = [:create, :destroy, :update]
|
9
9
|
|
10
10
|
included do
|
@@ -13,7 +13,9 @@ module ActiveRecord
|
|
13
13
|
scope: [:kind, :name]
|
14
14
|
end
|
15
15
|
|
16
|
-
|
16
|
+
attr_accessor :_new_record_before_last_commit # :nodoc:
|
17
|
+
|
18
|
+
# = Active Record \Transactions
|
17
19
|
#
|
18
20
|
# \Transactions are protective blocks where SQL statements are only permanent
|
19
21
|
# if they can all succeed as one atomic action. The classic example is a
|
@@ -98,7 +100,8 @@ module ActiveRecord
|
|
98
100
|
# catch those in your application code.
|
99
101
|
#
|
100
102
|
# One exception is the ActiveRecord::Rollback exception, which will trigger
|
101
|
-
# a ROLLBACK when raised, but not be re-raised by the transaction block.
|
103
|
+
# a ROLLBACK when raised, but not be re-raised by the transaction block. Any
|
104
|
+
# other exception will be re-raised.
|
102
105
|
#
|
103
106
|
# *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
|
104
107
|
# inside a transaction block. ActiveRecord::StatementInvalid exceptions indicate that an
|
@@ -185,6 +188,27 @@ module ActiveRecord
|
|
185
188
|
# #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
|
186
189
|
# within a transaction could trigger the cache to be regenerated before the database is updated.
|
187
190
|
#
|
191
|
+
# ==== NOTE: Callbacks are deduplicated per callback by filter.
|
192
|
+
#
|
193
|
+
# Trying to define multiple callbacks with the same filter will result in a single callback being run.
|
194
|
+
#
|
195
|
+
# For example:
|
196
|
+
#
|
197
|
+
# after_commit :do_something
|
198
|
+
# after_commit :do_something # only the last one will be called
|
199
|
+
#
|
200
|
+
# This applies to all variations of <tt>after_*_commit</tt> callbacks as well.
|
201
|
+
#
|
202
|
+
# after_commit :do_something
|
203
|
+
# after_create_commit :do_something
|
204
|
+
# after_save_commit :do_something
|
205
|
+
#
|
206
|
+
# It is recommended to use the +on:+ option to specify when the callback should be run.
|
207
|
+
#
|
208
|
+
# after_commit :do_something, on: [:create, :update]
|
209
|
+
#
|
210
|
+
# This is equivalent to using +after_create_commit+ and +after_update_commit+, but will not be deduplicated.
|
211
|
+
#
|
188
212
|
# === Caveats
|
189
213
|
#
|
190
214
|
# If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
|
@@ -195,9 +219,9 @@ module ActiveRecord
|
|
195
219
|
# database error will occur because the savepoint has already been
|
196
220
|
# automatically released. The following example demonstrates the problem:
|
197
221
|
#
|
198
|
-
# Model.
|
199
|
-
# Model.
|
200
|
-
# Model.
|
222
|
+
# Model.lease_connection.transaction do # BEGIN
|
223
|
+
# Model.lease_connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
224
|
+
# Model.lease_connection.create_table(...) # active_record_1 now automatically released
|
201
225
|
# end # RELEASE SAVEPOINT active_record_1
|
202
226
|
# # ^^^^ BOOM! database error!
|
203
227
|
# end
|
@@ -206,7 +230,20 @@ module ActiveRecord
|
|
206
230
|
module ClassMethods
|
207
231
|
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
|
208
232
|
def transaction(**options, &block)
|
209
|
-
connection
|
233
|
+
with_connection do |connection|
|
234
|
+
connection.transaction(**options, &block)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Returns a representation of the current transaction state,
|
239
|
+
# which can be a top level transaction, a savepoint, or the absence of a transaction.
|
240
|
+
#
|
241
|
+
# An object is always returned, whether or not a transaction is currently active.
|
242
|
+
# To check if a transaction was opened, use <tt>current_transaction.open?</tt>.
|
243
|
+
#
|
244
|
+
# See the ActiveRecord::Transaction documentation for detailed behavior.
|
245
|
+
def current_transaction
|
246
|
+
connection_pool.active_connection&.current_transaction&.user_transaction || Transaction::NULL_TRANSACTION
|
210
247
|
end
|
211
248
|
|
212
249
|
def before_commit(*args, &block) # :nodoc:
|
@@ -227,31 +264,31 @@ module ActiveRecord
|
|
227
264
|
# after_commit :do_bar_baz, on: [:update, :destroy]
|
228
265
|
#
|
229
266
|
def after_commit(*args, &block)
|
230
|
-
set_options_for_callbacks!(args)
|
267
|
+
set_options_for_callbacks!(args, prepend_option)
|
231
268
|
set_callback(:commit, :after, *args, &block)
|
232
269
|
end
|
233
270
|
|
234
271
|
# Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
|
235
272
|
def after_save_commit(*args, &block)
|
236
|
-
set_options_for_callbacks!(args, on: [ :create, :update ])
|
273
|
+
set_options_for_callbacks!(args, on: [ :create, :update ], **prepend_option)
|
237
274
|
set_callback(:commit, :after, *args, &block)
|
238
275
|
end
|
239
276
|
|
240
277
|
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
241
278
|
def after_create_commit(*args, &block)
|
242
|
-
set_options_for_callbacks!(args, on: :create)
|
279
|
+
set_options_for_callbacks!(args, on: :create, **prepend_option)
|
243
280
|
set_callback(:commit, :after, *args, &block)
|
244
281
|
end
|
245
282
|
|
246
283
|
# Shortcut for <tt>after_commit :hook, on: :update</tt>.
|
247
284
|
def after_update_commit(*args, &block)
|
248
|
-
set_options_for_callbacks!(args, on: :update)
|
285
|
+
set_options_for_callbacks!(args, on: :update, **prepend_option)
|
249
286
|
set_callback(:commit, :after, *args, &block)
|
250
287
|
end
|
251
288
|
|
252
289
|
# Shortcut for <tt>after_commit :hook, on: :destroy</tt>.
|
253
290
|
def after_destroy_commit(*args, &block)
|
254
|
-
set_options_for_callbacks!(args, on: :destroy)
|
291
|
+
set_options_for_callbacks!(args, on: :destroy, **prepend_option)
|
255
292
|
set_callback(:commit, :after, *args, &block)
|
256
293
|
end
|
257
294
|
|
@@ -259,11 +296,38 @@ module ActiveRecord
|
|
259
296
|
#
|
260
297
|
# Please check the documentation of #after_commit for options.
|
261
298
|
def after_rollback(*args, &block)
|
262
|
-
set_options_for_callbacks!(args)
|
299
|
+
set_options_for_callbacks!(args, prepend_option)
|
263
300
|
set_callback(:rollback, :after, *args, &block)
|
264
301
|
end
|
265
302
|
|
303
|
+
# Similar to ActiveSupport::Callbacks::ClassMethods#set_callback, but with
|
304
|
+
# support for options available on #after_commit and #after_rollback callbacks.
|
305
|
+
def set_callback(name, *filter_list, &block)
|
306
|
+
options = filter_list.extract_options!
|
307
|
+
filter_list << options
|
308
|
+
|
309
|
+
if name.in?([:commit, :rollback]) && options[:on]
|
310
|
+
fire_on = Array(options[:on])
|
311
|
+
assert_valid_transaction_action(fire_on)
|
312
|
+
options[:if] = [
|
313
|
+
-> { transaction_include_any_action?(fire_on) },
|
314
|
+
*options[:if]
|
315
|
+
]
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
super(name, *filter_list, &block)
|
320
|
+
end
|
321
|
+
|
266
322
|
private
|
323
|
+
def prepend_option
|
324
|
+
if ActiveRecord.run_after_transaction_callbacks_in_order_defined
|
325
|
+
{ prepend: true }
|
326
|
+
else
|
327
|
+
{}
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
267
331
|
def set_options_for_callbacks!(args, enforced_options = {})
|
268
332
|
options = args.extract_options!.merge!(enforced_options)
|
269
333
|
args << options
|
@@ -290,19 +354,19 @@ module ActiveRecord
|
|
290
354
|
self.class.transaction(**options, &block)
|
291
355
|
end
|
292
356
|
|
293
|
-
def destroy
|
357
|
+
def destroy # :nodoc:
|
294
358
|
with_transaction_returning_status { super }
|
295
359
|
end
|
296
360
|
|
297
|
-
def save(**)
|
361
|
+
def save(**) # :nodoc:
|
298
362
|
with_transaction_returning_status { super }
|
299
363
|
end
|
300
364
|
|
301
|
-
def save!(**)
|
365
|
+
def save!(**) # :nodoc:
|
302
366
|
with_transaction_returning_status { super }
|
303
367
|
end
|
304
368
|
|
305
|
-
def touch(*, **)
|
369
|
+
def touch(*, **) # :nodoc:
|
306
370
|
with_transaction_returning_status { super }
|
307
371
|
end
|
308
372
|
|
@@ -314,8 +378,8 @@ module ActiveRecord
|
|
314
378
|
#
|
315
379
|
# Ensure that it is not called if the object was never persisted (failed create),
|
316
380
|
# but call it after the commit of a destroyed object.
|
317
|
-
def committed!(should_run_callbacks: true)
|
318
|
-
|
381
|
+
def committed!(should_run_callbacks: true) # :nodoc:
|
382
|
+
@_start_transaction_state = nil
|
319
383
|
if should_run_callbacks
|
320
384
|
@_committed_already_called = true
|
321
385
|
_run_commit_callbacks
|
@@ -326,7 +390,7 @@ module ActiveRecord
|
|
326
390
|
|
327
391
|
# Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
|
328
392
|
# state should be rolled back to the beginning or just to the last savepoint.
|
329
|
-
def rolledback!(force_restore_state: false, should_run_callbacks: true)
|
393
|
+
def rolledback!(force_restore_state: false, should_run_callbacks: true) # :nodoc:
|
330
394
|
if should_run_callbacks
|
331
395
|
_run_rollback_callbacks
|
332
396
|
end
|
@@ -336,25 +400,26 @@ module ActiveRecord
|
|
336
400
|
@_trigger_update_callback = @_trigger_destroy_callback = false if force_restore_state
|
337
401
|
end
|
338
402
|
|
339
|
-
# Executes
|
340
|
-
# status flag. If the status is true the transaction is committed,
|
341
|
-
# a ROLLBACK is issued. In any case the status flag is returned.
|
403
|
+
# Executes a block within a transaction and captures its return value as a
|
404
|
+
# status flag. If the status is true, the transaction is committed,
|
405
|
+
# otherwise a ROLLBACK is issued. In any case, the status flag is returned.
|
342
406
|
#
|
343
407
|
# This method is available within the context of an ActiveRecord::Base
|
344
408
|
# instance.
|
345
409
|
def with_transaction_returning_status
|
346
|
-
|
347
|
-
|
348
|
-
|
410
|
+
self.class.with_connection do |connection|
|
411
|
+
status = nil
|
412
|
+
ensure_finalize = !connection.transaction_open?
|
349
413
|
|
350
|
-
|
351
|
-
|
352
|
-
|
414
|
+
connection.transaction do
|
415
|
+
add_to_transaction(ensure_finalize || has_transactional_callbacks?)
|
416
|
+
remember_transaction_record_state
|
353
417
|
|
354
|
-
|
355
|
-
|
418
|
+
status = yield
|
419
|
+
raise ActiveRecord::Rollback unless status
|
420
|
+
end
|
421
|
+
status
|
356
422
|
end
|
357
|
-
status
|
358
423
|
end
|
359
424
|
|
360
425
|
def trigger_transactional_callbacks? # :nodoc:
|
@@ -365,6 +430,13 @@ module ActiveRecord
|
|
365
430
|
private
|
366
431
|
attr_reader :_committed_already_called, :_trigger_update_callback, :_trigger_destroy_callback
|
367
432
|
|
433
|
+
def init_internals
|
434
|
+
super
|
435
|
+
@_start_transaction_state = nil
|
436
|
+
@_committed_already_called = nil
|
437
|
+
@_new_record_before_last_commit = nil
|
438
|
+
end
|
439
|
+
|
368
440
|
# Save the new record state and id of a record so it can be restored later if a transaction fails.
|
369
441
|
def remember_transaction_record_state
|
370
442
|
@_start_transaction_state ||= {
|
@@ -389,12 +461,7 @@ module ActiveRecord
|
|
389
461
|
def clear_transaction_record_state
|
390
462
|
return unless @_start_transaction_state
|
391
463
|
@_start_transaction_state[:level] -= 1
|
392
|
-
|
393
|
-
end
|
394
|
-
|
395
|
-
# Force to clear the transaction record state.
|
396
|
-
def force_clear_transaction_record_state
|
397
|
-
@_start_transaction_state = nil
|
464
|
+
@_start_transaction_state = nil if @_start_transaction_state[:level] < 1
|
398
465
|
end
|
399
466
|
|
400
467
|
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
|
@@ -411,8 +478,16 @@ module ActiveRecord
|
|
411
478
|
end
|
412
479
|
@mutations_from_database = nil
|
413
480
|
@mutations_before_last_save = nil
|
414
|
-
if
|
415
|
-
|
481
|
+
if self.class.composite_primary_key?
|
482
|
+
if restore_state[:id] != @primary_key.map { |col| @attributes.fetch_value(col) }
|
483
|
+
@primary_key.zip(restore_state[:id]).each do |col, val|
|
484
|
+
@attributes.write_from_user(col, val)
|
485
|
+
end
|
486
|
+
end
|
487
|
+
else
|
488
|
+
if @attributes.fetch_value(@primary_key) != restore_state[:id]
|
489
|
+
@attributes.write_from_user(@primary_key, restore_state[:id])
|
490
|
+
end
|
416
491
|
end
|
417
492
|
freeze if restore_state[:frozen?]
|
418
493
|
end
|
@@ -436,7 +511,9 @@ module ActiveRecord
|
|
436
511
|
# Add the record to the current transaction so that the #after_rollback and #after_commit
|
437
512
|
# callbacks can be called.
|
438
513
|
def add_to_transaction(ensure_finalize = true)
|
439
|
-
self.class.connection
|
514
|
+
self.class.with_connection do |connection|
|
515
|
+
connection.add_transaction_record(self, ensure_finalize)
|
516
|
+
end
|
440
517
|
end
|
441
518
|
|
442
519
|
def has_transactional_callbacks?
|
@@ -2,10 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Translation
|
5
|
-
include ActiveModel::Translation
|
6
|
-
|
7
5
|
# Set the lookup ancestors for ActiveModel.
|
8
|
-
def lookup_ancestors
|
6
|
+
def lookup_ancestors # :nodoc:
|
9
7
|
klass = self
|
10
8
|
classes = [klass]
|
11
9
|
return classes if klass == ActiveRecord::Base
|
@@ -16,8 +14,8 @@ module ActiveRecord
|
|
16
14
|
classes
|
17
15
|
end
|
18
16
|
|
19
|
-
# Set the i18n scope to
|
20
|
-
def i18n_scope
|
17
|
+
# Set the i18n scope to override ActiveModel.
|
18
|
+
def i18n_scope # :nodoc:
|
21
19
|
:activerecord
|
22
20
|
end
|
23
21
|
end
|