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
@@ -57,190 +57,34 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# Active Record callbacks or validations. Though passed values
|
63
|
-
# go through Active Record's type casting and serialization.
|
60
|
+
# Builds an object (or multiple objects) and returns either the built object or a list of built
|
61
|
+
# objects.
|
64
62
|
#
|
65
|
-
#
|
66
|
-
|
67
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Inserts multiple records into the database in a single SQL INSERT
|
71
|
-
# statement. It does not instantiate any models nor does it trigger
|
72
|
-
# Active Record callbacks or validations. Though passed values
|
73
|
-
# go through Active Record's type casting and serialization.
|
74
|
-
#
|
75
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
76
|
-
# the attributes for a single row and must have the same keys.
|
77
|
-
#
|
78
|
-
# Rows are considered to be unique by every unique index on the table. Any
|
79
|
-
# duplicate rows are skipped.
|
80
|
-
# Override with <tt>:unique_by</tt> (see below).
|
81
|
-
#
|
82
|
-
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
83
|
-
# <tt>:returning</tt> (see below).
|
84
|
-
#
|
85
|
-
# ==== Options
|
86
|
-
#
|
87
|
-
# [:returning]
|
88
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
89
|
-
# inserted records, which by default is the primary key.
|
90
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
91
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
|
-
# clause entirely.
|
93
|
-
#
|
94
|
-
# [:unique_by]
|
95
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
97
|
-
#
|
98
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
99
|
-
#
|
100
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
101
|
-
# row has an existing id, or is not unique by another unique index,
|
102
|
-
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
103
|
-
#
|
104
|
-
# Unique indexes can be identified by columns or name:
|
105
|
-
#
|
106
|
-
# unique_by: :isbn
|
107
|
-
# unique_by: %i[ author_id name ]
|
108
|
-
# unique_by: :index_books_on_isbn
|
109
|
-
#
|
110
|
-
# Because it relies on the index information from the database
|
111
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
112
|
-
# Active Record's schema_cache.
|
113
|
-
#
|
114
|
-
# ==== Example
|
115
|
-
#
|
116
|
-
# # Insert records and skip inserting any duplicates.
|
117
|
-
# # Here "Eloquent Ruby" is skipped because its id is not unique.
|
118
|
-
#
|
119
|
-
# Book.insert_all([
|
120
|
-
# { id: 1, title: "Rework", author: "David" },
|
121
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
|
-
# ])
|
123
|
-
def insert_all(attributes, returning: nil, unique_by: nil)
|
124
|
-
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by).execute
|
125
|
-
end
|
126
|
-
|
127
|
-
# Inserts a single record into the database in a single SQL INSERT
|
128
|
-
# statement. It does not instantiate any models nor does it trigger
|
129
|
-
# Active Record callbacks or validations. Though passed values
|
130
|
-
# go through Active Record's type casting and serialization.
|
131
|
-
#
|
132
|
-
# See <tt>ActiveRecord::Persistence#insert_all!</tt> for more.
|
133
|
-
def insert!(attributes, returning: nil)
|
134
|
-
insert_all!([ attributes ], returning: returning)
|
135
|
-
end
|
136
|
-
|
137
|
-
# Inserts multiple records into the database in a single SQL INSERT
|
138
|
-
# statement. It does not instantiate any models nor does it trigger
|
139
|
-
# Active Record callbacks or validations. Though passed values
|
140
|
-
# go through Active Record's type casting and serialization.
|
141
|
-
#
|
142
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
143
|
-
# the attributes for a single row and must have the same keys.
|
144
|
-
#
|
145
|
-
# Raises <tt>ActiveRecord::RecordNotUnique</tt> if any rows violate a
|
146
|
-
# unique index on the table. In that case, no rows are inserted.
|
147
|
-
#
|
148
|
-
# To skip duplicate rows, see <tt>ActiveRecord::Persistence#insert_all</tt>.
|
149
|
-
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
150
|
-
#
|
151
|
-
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
152
|
-
# <tt>:returning</tt> (see below).
|
153
|
-
#
|
154
|
-
# ==== Options
|
155
|
-
#
|
156
|
-
# [:returning]
|
157
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
158
|
-
# inserted records, which by default is the primary key.
|
159
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
160
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
|
-
# clause entirely.
|
162
|
-
#
|
163
|
-
# ==== Examples
|
164
|
-
#
|
165
|
-
# # Insert multiple records
|
166
|
-
# Book.insert_all!([
|
167
|
-
# { title: "Rework", author: "David" },
|
168
|
-
# { title: "Eloquent Ruby", author: "Russ" }
|
169
|
-
# ])
|
170
|
-
#
|
171
|
-
# # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
|
172
|
-
# # does not have a unique id.
|
173
|
-
# Book.insert_all!([
|
174
|
-
# { id: 1, title: "Rework", author: "David" },
|
175
|
-
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
|
-
# ])
|
177
|
-
def insert_all!(attributes, returning: nil)
|
178
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
179
|
-
end
|
180
|
-
|
181
|
-
# Updates or inserts (upserts) a single record into the database in a
|
182
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
183
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
184
|
-
# go through Active Record's type casting and serialization.
|
185
|
-
#
|
186
|
-
# See <tt>ActiveRecord::Persistence#upsert_all</tt> for documentation.
|
187
|
-
def upsert(attributes, returning: nil, unique_by: nil)
|
188
|
-
upsert_all([ attributes ], returning: returning, unique_by: unique_by)
|
189
|
-
end
|
190
|
-
|
191
|
-
# Updates or inserts (upserts) multiple records into the database in a
|
192
|
-
# single SQL INSERT statement. It does not instantiate any models nor does
|
193
|
-
# it trigger Active Record callbacks or validations. Though passed values
|
194
|
-
# go through Active Record's type casting and serialization.
|
195
|
-
#
|
196
|
-
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
197
|
-
# the attributes for a single row and must have the same keys.
|
198
|
-
#
|
199
|
-
# Returns an <tt>ActiveRecord::Result</tt> with its contents based on
|
200
|
-
# <tt>:returning</tt> (see below).
|
201
|
-
#
|
202
|
-
# ==== Options
|
203
|
-
#
|
204
|
-
# [:returning]
|
205
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
206
|
-
# inserted records, which by default is the primary key.
|
207
|
-
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
208
|
-
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
|
-
# clause entirely.
|
210
|
-
#
|
211
|
-
# [:unique_by]
|
212
|
-
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
|
-
# by every unique index on the table. Any duplicate rows are skipped.
|
214
|
-
#
|
215
|
-
# To skip rows according to just one unique index pass <tt>:unique_by</tt>.
|
216
|
-
#
|
217
|
-
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
218
|
-
# row has an existing id, or is not unique by another unique index,
|
219
|
-
# <tt>ActiveRecord::RecordNotUnique</tt> is raised.
|
220
|
-
#
|
221
|
-
# Unique indexes can be identified by columns or name:
|
222
|
-
#
|
223
|
-
# unique_by: :isbn
|
224
|
-
# unique_by: %i[ author_id name ]
|
225
|
-
# unique_by: :index_books_on_isbn
|
226
|
-
#
|
227
|
-
# Because it relies on the index information from the database
|
228
|
-
# <tt>:unique_by</tt> is recommended to be paired with
|
229
|
-
# Active Record's schema_cache.
|
63
|
+
# The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
|
64
|
+
# attributes on the objects that are to be built.
|
230
65
|
#
|
231
66
|
# ==== Examples
|
67
|
+
# # Build a single new object
|
68
|
+
# User.build(first_name: 'Jamie')
|
232
69
|
#
|
233
|
-
# #
|
234
|
-
#
|
70
|
+
# # Build an Array of new objects
|
71
|
+
# User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
|
235
72
|
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
73
|
+
# # Build a single object and pass it into a block to set other attributes.
|
74
|
+
# User.build(first_name: 'Jamie') do |u|
|
75
|
+
# u.is_admin = false
|
76
|
+
# end
|
240
77
|
#
|
241
|
-
#
|
242
|
-
|
243
|
-
|
78
|
+
# # Building an Array of new objects using a block, where the block is executed for each object:
|
79
|
+
# User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
|
80
|
+
# u.is_admin = false
|
81
|
+
# end
|
82
|
+
def build(attributes = nil, &block)
|
83
|
+
if attributes.is_a?(Array)
|
84
|
+
attributes.collect { |attr| build(attr, &block) }
|
85
|
+
else
|
86
|
+
new(attributes, &block)
|
87
|
+
end
|
244
88
|
end
|
245
89
|
|
246
90
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
@@ -264,6 +108,7 @@ module ActiveRecord
|
|
264
108
|
# ==== Parameters
|
265
109
|
#
|
266
110
|
# * +id+ - This should be the id or an array of ids to be updated.
|
111
|
+
# Optional argument, defaults to all records in the relation.
|
267
112
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
113
|
#
|
269
114
|
# ==== Examples
|
@@ -286,6 +131,11 @@ module ActiveRecord
|
|
286
131
|
# for updating all records in a single query.
|
287
132
|
def update(id = :all, attributes)
|
288
133
|
if id.is_a?(Array)
|
134
|
+
if id.any?(ActiveRecord::Base)
|
135
|
+
raise ArgumentError,
|
136
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
137
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
138
|
+
end
|
289
139
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
140
|
object.update(attributes[idx])
|
291
141
|
}
|
@@ -303,99 +153,161 @@ module ActiveRecord
|
|
303
153
|
end
|
304
154
|
end
|
305
155
|
|
306
|
-
#
|
307
|
-
#
|
308
|
-
|
309
|
-
#
|
310
|
-
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
311
|
-
# from the attributes, and then calls destroy on it.
|
312
|
-
#
|
313
|
-
# ==== Parameters
|
314
|
-
#
|
315
|
-
# * +id+ - This should be the id or an array of ids to be destroyed.
|
316
|
-
#
|
317
|
-
# ==== Examples
|
318
|
-
#
|
319
|
-
# # Destroy a single object
|
320
|
-
# Todo.destroy(1)
|
321
|
-
#
|
322
|
-
# # Destroy multiple objects
|
323
|
-
# todos = [1,2,3]
|
324
|
-
# Todo.destroy(todos)
|
325
|
-
def destroy(id)
|
156
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
157
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
158
|
+
def update!(id = :all, attributes)
|
326
159
|
if id.is_a?(Array)
|
327
|
-
|
160
|
+
if id.any?(ActiveRecord::Base)
|
161
|
+
raise ArgumentError,
|
162
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
163
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
164
|
+
end
|
165
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
166
|
+
object.update!(attributes[idx])
|
167
|
+
}
|
168
|
+
elsif id == :all
|
169
|
+
all.each { |record| record.update!(attributes) }
|
328
170
|
else
|
329
|
-
|
171
|
+
if ActiveRecord::Base === id
|
172
|
+
raise ArgumentError,
|
173
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
174
|
+
"Please pass the id of the object by calling `.id`."
|
175
|
+
end
|
176
|
+
object = find(id)
|
177
|
+
object.update!(attributes)
|
178
|
+
object
|
330
179
|
end
|
331
180
|
end
|
332
181
|
|
333
|
-
#
|
334
|
-
#
|
335
|
-
# Record objects are not instantiated, so the object's callbacks are not
|
336
|
-
# executed, including any <tt>:dependent</tt> association options.
|
182
|
+
# Accepts a list of attribute names to be used in the WHERE clause
|
183
|
+
# of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
|
337
184
|
#
|
338
|
-
#
|
185
|
+
# class Developer < ActiveRecord::Base
|
186
|
+
# query_constraints :company_id, :id
|
187
|
+
# end
|
339
188
|
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
189
|
+
# developer = Developer.first
|
190
|
+
# # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
|
191
|
+
# developer.inspect # => #<Developer id: 1, company_id: 1, ...>
|
343
192
|
#
|
344
|
-
#
|
193
|
+
# developer.update!(name: "Nikita")
|
194
|
+
# # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
195
|
+
#
|
196
|
+
# # It is possible to update an attribute used in the query_constraints clause:
|
197
|
+
# developer.update!(company_id: 2)
|
198
|
+
# # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
199
|
+
#
|
200
|
+
# developer.name = "Bob"
|
201
|
+
# developer.save!
|
202
|
+
# # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
345
203
|
#
|
346
|
-
#
|
347
|
-
#
|
204
|
+
# developer.destroy!
|
205
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
348
206
|
#
|
349
|
-
#
|
350
|
-
#
|
351
|
-
|
352
|
-
|
207
|
+
# developer.delete
|
208
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
209
|
+
#
|
210
|
+
# developer.reload
|
211
|
+
# # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
|
212
|
+
def query_constraints(*columns_list)
|
213
|
+
raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
|
214
|
+
|
215
|
+
@query_constraints_list = columns_list.map(&:to_s)
|
216
|
+
@has_query_constraints = @query_constraints_list
|
217
|
+
end
|
218
|
+
|
219
|
+
def has_query_constraints? # :nodoc:
|
220
|
+
@has_query_constraints
|
221
|
+
end
|
222
|
+
|
223
|
+
def query_constraints_list # :nodoc:
|
224
|
+
@query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
|
225
|
+
primary_key if primary_key.is_a?(Array)
|
226
|
+
else
|
227
|
+
base_class.query_constraints_list
|
228
|
+
end
|
353
229
|
end
|
354
230
|
|
355
|
-
|
231
|
+
# Returns an array of column names to be used in queries. The source of column
|
232
|
+
# names is derived from +query_constraints_list+ or +primary_key+. This method
|
233
|
+
# is for internal use when the primary key is to be treated as an array.
|
234
|
+
def composite_query_constraints_list # :nodoc:
|
235
|
+
@composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
|
236
|
+
end
|
237
|
+
|
238
|
+
def _insert_record(connection, values, returning) # :nodoc:
|
356
239
|
primary_key = self.primary_key
|
357
240
|
primary_key_value = nil
|
358
241
|
|
359
|
-
if
|
360
|
-
|
361
|
-
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
242
|
+
if prefetch_primary_key? && primary_key
|
243
|
+
values[primary_key] ||= begin
|
363
244
|
primary_key_value = next_sequence_value
|
364
|
-
|
245
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
365
246
|
end
|
366
247
|
end
|
367
248
|
|
368
|
-
|
369
|
-
im = arel_table.compile_insert(connection.empty_insert_statement_value(primary_key))
|
370
|
-
im.into arel_table
|
371
|
-
else
|
372
|
-
im = arel_table.compile_insert(_substitute_values(values))
|
373
|
-
end
|
249
|
+
im = Arel::InsertManager.new(arel_table)
|
374
250
|
|
375
|
-
|
251
|
+
with_connection do |c|
|
252
|
+
if values.empty?
|
253
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
254
|
+
else
|
255
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
256
|
+
end
|
257
|
+
|
258
|
+
connection.insert(
|
259
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
260
|
+
returning: returning
|
261
|
+
)
|
262
|
+
end
|
376
263
|
end
|
377
264
|
|
378
265
|
def _update_record(values, constraints) # :nodoc:
|
379
|
-
constraints =
|
266
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
380
267
|
|
381
|
-
|
382
|
-
|
383
|
-
).compile_update(_substitute_values(values), primary_key)
|
268
|
+
default_constraint = build_default_constraint
|
269
|
+
constraints << default_constraint if default_constraint
|
384
270
|
|
385
|
-
|
271
|
+
if current_scope = self.global_current_scope
|
272
|
+
constraints << current_scope.where_clause.ast
|
273
|
+
end
|
274
|
+
|
275
|
+
um = Arel::UpdateManager.new(arel_table)
|
276
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
277
|
+
um.wheres = constraints
|
278
|
+
|
279
|
+
with_connection do |c|
|
280
|
+
c.update(um, "#{self} Update")
|
281
|
+
end
|
386
282
|
end
|
387
283
|
|
388
284
|
def _delete_record(constraints) # :nodoc:
|
389
|
-
constraints =
|
285
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
286
|
+
|
287
|
+
default_constraint = build_default_constraint
|
288
|
+
constraints << default_constraint if default_constraint
|
390
289
|
|
391
|
-
|
392
|
-
|
290
|
+
if current_scope = self.global_current_scope
|
291
|
+
constraints << current_scope.where_clause.ast
|
292
|
+
end
|
293
|
+
|
294
|
+
dm = Arel::DeleteManager.new(arel_table)
|
393
295
|
dm.wheres = constraints
|
394
296
|
|
395
|
-
|
297
|
+
with_connection do |c|
|
298
|
+
c.delete(dm, "#{self} Destroy")
|
299
|
+
end
|
396
300
|
end
|
397
301
|
|
398
302
|
private
|
303
|
+
def inherited(subclass)
|
304
|
+
super
|
305
|
+
subclass.class_eval do
|
306
|
+
@_query_constraints_list = nil
|
307
|
+
@has_query_constraints = false
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
399
311
|
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
400
312
|
# new instance of the class. Accepts only keys as strings.
|
401
313
|
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
@@ -412,12 +324,14 @@ module ActiveRecord
|
|
412
324
|
self
|
413
325
|
end
|
414
326
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
327
|
+
# Called by +_update_record+ and +_delete_record+
|
328
|
+
# to build `where` clause from default scopes.
|
329
|
+
# Skips empty scopes.
|
330
|
+
def build_default_constraint
|
331
|
+
return unless default_scopes?(all_queries: true)
|
332
|
+
|
333
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
334
|
+
default_where_clause.ast unless default_where_clause.empty?
|
421
335
|
end
|
422
336
|
end
|
423
337
|
|
@@ -428,12 +342,17 @@ module ActiveRecord
|
|
428
342
|
end
|
429
343
|
|
430
344
|
# Returns true if this object was just created -- that is, prior to the last
|
431
|
-
#
|
345
|
+
# update or delete, the object didn't exist in the database and new_record? would have
|
432
346
|
# returned true.
|
433
347
|
def previously_new_record?
|
434
348
|
@previously_new_record
|
435
349
|
end
|
436
350
|
|
351
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
352
|
+
def previously_persisted?
|
353
|
+
!new_record? && destroyed?
|
354
|
+
end
|
355
|
+
|
437
356
|
# Returns true if this object has been destroyed, otherwise returns false.
|
438
357
|
def destroyed?
|
439
358
|
@destroyed
|
@@ -522,6 +441,7 @@ module ActiveRecord
|
|
522
441
|
def delete
|
523
442
|
_delete_row if persisted?
|
524
443
|
@destroyed = true
|
444
|
+
@previously_new_record = false
|
525
445
|
freeze
|
526
446
|
end
|
527
447
|
|
@@ -535,12 +455,9 @@ module ActiveRecord
|
|
535
455
|
def destroy
|
536
456
|
_raise_readonly_record_error if readonly?
|
537
457
|
destroy_associations
|
538
|
-
@_trigger_destroy_callback
|
539
|
-
destroy_row > 0
|
540
|
-
else
|
541
|
-
true
|
542
|
-
end
|
458
|
+
@_trigger_destroy_callback ||= persisted? && destroy_row > 0
|
543
459
|
@destroyed = true
|
460
|
+
@previously_new_record = false
|
544
461
|
freeze
|
545
462
|
end
|
546
463
|
|
@@ -556,21 +473,24 @@ module ActiveRecord
|
|
556
473
|
end
|
557
474
|
|
558
475
|
# Returns an instance of the specified +klass+ with the attributes of the
|
559
|
-
# current record. This is mostly useful in relation to single
|
560
|
-
# inheritance structures where you want a subclass to appear as the
|
476
|
+
# current record. This is mostly useful in relation to single table
|
477
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
561
478
|
# superclass. This can be used along with record identification in
|
562
479
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
563
480
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
564
481
|
# instance using the companies/company partial instead of clients/client.
|
565
482
|
#
|
566
483
|
# Note: The new instance will share a link to the same attributes as the original class.
|
567
|
-
# Therefore the
|
484
|
+
# Therefore the STI column value will still be the same.
|
568
485
|
# Any change to the attributes on either instance will affect both instances.
|
569
|
-
#
|
486
|
+
# This includes any attribute initialization done by the new instance.
|
487
|
+
#
|
488
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
570
489
|
def becomes(klass)
|
571
490
|
became = klass.allocate
|
572
491
|
|
573
492
|
became.send(:initialize) do |becoming|
|
493
|
+
@attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
|
574
494
|
becoming.instance_variable_set(:@attributes, @attributes)
|
575
495
|
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
576
496
|
becoming.instance_variable_set(:@new_record, new_record?)
|
@@ -581,11 +501,11 @@ module ActiveRecord
|
|
581
501
|
became
|
582
502
|
end
|
583
503
|
|
584
|
-
# Wrapper around #becomes that also changes the instance's
|
504
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
585
505
|
# This is especially useful if you want to persist the changed class in your
|
586
506
|
# database.
|
587
507
|
#
|
588
|
-
# Note: The old instance's
|
508
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
589
509
|
# share the same set of attributes.
|
590
510
|
def becomes!(klass)
|
591
511
|
became = becomes(klass)
|
@@ -605,7 +525,7 @@ module ActiveRecord
|
|
605
525
|
# * updated_at/updated_on column is updated if that column is available.
|
606
526
|
# * Updates all the attributes that are dirty in this object.
|
607
527
|
#
|
608
|
-
# This method raises an ActiveRecord::ActiveRecordError
|
528
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
609
529
|
# attribute is marked as readonly.
|
610
530
|
#
|
611
531
|
# Also see #update_column.
|
@@ -617,6 +537,28 @@ module ActiveRecord
|
|
617
537
|
save(validate: false)
|
618
538
|
end
|
619
539
|
|
540
|
+
# Updates a single attribute and saves the record.
|
541
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
542
|
+
#
|
543
|
+
# * Validation is skipped.
|
544
|
+
# * \Callbacks are invoked.
|
545
|
+
# * updated_at/updated_on column is updated if that column is available.
|
546
|
+
# * Updates all the attributes that are dirty in this object.
|
547
|
+
#
|
548
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
549
|
+
# attribute is marked as readonly.
|
550
|
+
#
|
551
|
+
# If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
552
|
+
# and #update_attribute! raises ActiveRecord::RecordNotSaved. See
|
553
|
+
# ActiveRecord::Callbacks for further details.
|
554
|
+
def update_attribute!(name, value)
|
555
|
+
name = name.to_s
|
556
|
+
verify_readonly_attribute(name)
|
557
|
+
public_send("#{name}=", value)
|
558
|
+
|
559
|
+
save!(validate: false)
|
560
|
+
end
|
561
|
+
|
620
562
|
# Updates the attributes of the model from the passed-in hash and saves the
|
621
563
|
# record, all wrapped in a transaction. If the object is invalid, the saving
|
622
564
|
# will fail and false will be returned.
|
@@ -664,6 +606,7 @@ module ActiveRecord
|
|
664
606
|
def update_columns(attributes)
|
665
607
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
666
608
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
609
|
+
_raise_readonly_record_error if readonly?
|
667
610
|
|
668
611
|
attributes = attributes.transform_keys do |key|
|
669
612
|
name = key.to_s
|
@@ -671,14 +614,15 @@ module ActiveRecord
|
|
671
614
|
verify_readonly_attribute(name) || name
|
672
615
|
end
|
673
616
|
|
674
|
-
|
675
|
-
attributes.
|
676
|
-
|
617
|
+
update_constraints = _query_constraints_hash
|
618
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
619
|
+
h[k] = @attributes.write_cast_value(k, v)
|
620
|
+
clear_attribute_change(k)
|
677
621
|
end
|
678
622
|
|
679
623
|
affected_rows = self.class._update_record(
|
680
624
|
attributes,
|
681
|
-
|
625
|
+
update_constraints
|
682
626
|
)
|
683
627
|
|
684
628
|
affected_rows == 1
|
@@ -798,15 +742,16 @@ module ActiveRecord
|
|
798
742
|
# end
|
799
743
|
#
|
800
744
|
def reload(options = nil)
|
801
|
-
self.class.
|
745
|
+
self.class.connection_pool.clear_query_cache
|
802
746
|
|
803
|
-
fresh_object =
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
end
|
747
|
+
fresh_object = if apply_scoping?(options)
|
748
|
+
_find_record((options || {}).merge(all_queries: true))
|
749
|
+
else
|
750
|
+
self.class.unscoped { _find_record(options) }
|
751
|
+
end
|
809
752
|
|
753
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
754
|
+
@association_cache.each_value { |association| association.owner = self }
|
810
755
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
811
756
|
@new_record = false
|
812
757
|
@previously_new_record = false
|
@@ -849,12 +794,15 @@ module ActiveRecord
|
|
849
794
|
#
|
850
795
|
def touch(*names, time: nil)
|
851
796
|
_raise_record_not_touched_error unless persisted?
|
797
|
+
_raise_readonly_record_error if readonly?
|
852
798
|
|
853
799
|
attribute_names = timestamp_attributes_for_update_in_model
|
854
|
-
attribute_names
|
800
|
+
attribute_names = (attribute_names | names).map! do |name|
|
855
801
|
name = name.to_s
|
856
|
-
self.class.attribute_aliases[name] || name
|
857
|
-
|
802
|
+
name = self.class.attribute_aliases[name] || name
|
803
|
+
verify_readonly_attribute(name)
|
804
|
+
name
|
805
|
+
end
|
858
806
|
|
859
807
|
unless attribute_names.empty?
|
860
808
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -865,6 +813,54 @@ module ActiveRecord
|
|
865
813
|
end
|
866
814
|
|
867
815
|
private
|
816
|
+
def init_internals
|
817
|
+
super
|
818
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
819
|
+
@previously_new_record = false
|
820
|
+
end
|
821
|
+
|
822
|
+
def strict_loaded_associations
|
823
|
+
@association_cache.find_all do |_, assoc|
|
824
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
825
|
+
end.map(&:first)
|
826
|
+
end
|
827
|
+
|
828
|
+
def _find_record(options)
|
829
|
+
all_queries = options ? options[:all_queries] : nil
|
830
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
831
|
+
|
832
|
+
if options && options[:lock]
|
833
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
834
|
+
else
|
835
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
def _in_memory_query_constraints_hash
|
840
|
+
if self.class.query_constraints_list.nil?
|
841
|
+
{ @primary_key => id }
|
842
|
+
else
|
843
|
+
self.class.query_constraints_list.index_with do |column_name|
|
844
|
+
attribute(column_name)
|
845
|
+
end
|
846
|
+
end
|
847
|
+
end
|
848
|
+
|
849
|
+
def apply_scoping?(options)
|
850
|
+
!(options && options[:unscoped]) &&
|
851
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
852
|
+
end
|
853
|
+
|
854
|
+
def _query_constraints_hash
|
855
|
+
if self.class.query_constraints_list.nil?
|
856
|
+
{ @primary_key => id_in_database }
|
857
|
+
else
|
858
|
+
self.class.query_constraints_list.index_with do |column_name|
|
859
|
+
attribute_in_database(column_name)
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
868
864
|
# A hook to be overridden by association modules.
|
869
865
|
def destroy_associations
|
870
866
|
end
|
@@ -874,7 +870,7 @@ module ActiveRecord
|
|
874
870
|
end
|
875
871
|
|
876
872
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
873
|
+
self.class._delete_record(_query_constraints_hash)
|
878
874
|
end
|
879
875
|
|
880
876
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +886,7 @@ module ActiveRecord
|
|
890
886
|
def _update_row(attribute_names, attempted_action = "update")
|
891
887
|
self.class._update_record(
|
892
888
|
attributes_with_values(attribute_names),
|
893
|
-
|
889
|
+
_query_constraints_hash
|
894
890
|
)
|
895
891
|
end
|
896
892
|
|
@@ -926,11 +922,19 @@ module ActiveRecord
|
|
926
922
|
def _create_record(attribute_names = self.attribute_names)
|
927
923
|
attribute_names = attributes_for_create(attribute_names)
|
928
924
|
|
929
|
-
|
930
|
-
|
931
|
-
|
925
|
+
self.class.with_connection do |connection|
|
926
|
+
returning_columns = self.class._returning_columns_for_insert(connection)
|
927
|
+
|
928
|
+
returning_values = self.class._insert_record(
|
929
|
+
connection,
|
930
|
+
attributes_with_values(attribute_names),
|
931
|
+
returning_columns
|
932
|
+
)
|
932
933
|
|
933
|
-
|
934
|
+
returning_columns.zip(returning_values).each do |column, value|
|
935
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
936
|
+
end if returning_values
|
937
|
+
end
|
934
938
|
|
935
939
|
@new_record = false
|
936
940
|
@previously_new_record = true
|
@@ -946,7 +950,8 @@ module ActiveRecord
|
|
946
950
|
|
947
951
|
def _raise_record_not_destroyed
|
948
952
|
@_association_destroy_exception ||= nil
|
949
|
-
|
953
|
+
key = self.class.primary_key
|
954
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
950
955
|
ensure
|
951
956
|
@_association_destroy_exception = nil
|
952
957
|
end
|
@@ -961,11 +966,5 @@ module ActiveRecord
|
|
961
966
|
persisted?, new_record?, or destroyed? before touching.
|
962
967
|
MSG
|
963
968
|
end
|
964
|
-
|
965
|
-
# The name of the method used to touch a +belongs_to+ association when the
|
966
|
-
# +:touch+ option is used.
|
967
|
-
def belongs_to_touch_method
|
968
|
-
:touch
|
969
|
-
end
|
970
969
|
end
|
971
970
|
end
|