activerecord 6.1.7 → 7.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2030 -1020
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +51 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +39 -35
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/preloader/association.rb +210 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +446 -306
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +27 -12
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +161 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +65 -31
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +113 -597
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +172 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +367 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -150
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +317 -164
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +39 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +112 -55
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +89 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +397 -75
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +508 -246
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +72 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +296 -104
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +258 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +108 -137
- data/lib/active_record/core.rb +242 -233
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -2
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +88 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +66 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +155 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +172 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +147 -86
- data/lib/active_record/future_result.rb +174 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +135 -22
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +119 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +37 -22
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +112 -14
- data/lib/active_record/migration/compatibility.rb +233 -46
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +361 -173
- data/lib/active_record/model_schema.rb +125 -101
- data/lib/active_record/nested_attributes.rb +50 -20
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +409 -88
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +220 -44
- data/lib/active_record/railties/controller_runtime.rb +15 -10
- data/lib/active_record/railties/databases.rake +188 -252
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +248 -81
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +246 -90
- data/lib/active_record/relation/delegation.rb +28 -14
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +670 -129
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +287 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +32 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +251 -140
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +117 -96
- data/lib/active_record/timestamp.rb +32 -19
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +5 -13
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +141 -20
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +96 -16
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -57,14 +57,44 @@ module ActiveRecord
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
+
# Builds an object (or multiple objects) and returns either the built object or a list of built
|
61
|
+
# objects.
|
62
|
+
#
|
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.
|
65
|
+
#
|
66
|
+
# ==== Examples
|
67
|
+
# # Build a single new object
|
68
|
+
# User.build(first_name: 'Jamie')
|
69
|
+
#
|
70
|
+
# # Build an Array of new objects
|
71
|
+
# User.build([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
|
72
|
+
#
|
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
|
77
|
+
#
|
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
|
88
|
+
end
|
89
|
+
|
60
90
|
# Inserts a single record into the database in a single SQL INSERT
|
61
91
|
# statement. It does not instantiate any models nor does it trigger
|
62
92
|
# Active Record callbacks or validations. Though passed values
|
63
93
|
# go through Active Record's type casting and serialization.
|
64
94
|
#
|
65
|
-
# See
|
66
|
-
def insert(attributes, returning: nil, unique_by: nil)
|
67
|
-
insert_all([ attributes ], returning: returning, unique_by: unique_by)
|
95
|
+
# See #insert_all for documentation.
|
96
|
+
def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
97
|
+
insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
|
68
98
|
end
|
69
99
|
|
70
100
|
# Inserts multiple records into the database in a single SQL INSERT
|
@@ -79,18 +109,21 @@ module ActiveRecord
|
|
79
109
|
# duplicate rows are skipped.
|
80
110
|
# Override with <tt>:unique_by</tt> (see below).
|
81
111
|
#
|
82
|
-
# Returns an
|
112
|
+
# Returns an ActiveRecord::Result with its contents based on
|
83
113
|
# <tt>:returning</tt> (see below).
|
84
114
|
#
|
85
115
|
# ==== Options
|
86
116
|
#
|
87
117
|
# [:returning]
|
88
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
118
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
89
119
|
# inserted records, which by default is the primary key.
|
90
120
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
91
121
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
92
122
|
# clause entirely.
|
93
123
|
#
|
124
|
+
# You can also pass an SQL string if you need more control on the return values
|
125
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
126
|
+
#
|
94
127
|
# [:unique_by]
|
95
128
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
96
129
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -99,7 +132,7 @@ module ActiveRecord
|
|
99
132
|
#
|
100
133
|
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
101
134
|
# row has an existing id, or is not unique by another unique index,
|
102
|
-
#
|
135
|
+
# ActiveRecord::RecordNotUnique is raised.
|
103
136
|
#
|
104
137
|
# Unique indexes can be identified by columns or name:
|
105
138
|
#
|
@@ -107,6 +140,17 @@ module ActiveRecord
|
|
107
140
|
# unique_by: %i[ author_id name ]
|
108
141
|
# unique_by: :index_books_on_isbn
|
109
142
|
#
|
143
|
+
# [:record_timestamps]
|
144
|
+
# By default, automatic setting of timestamp columns is controlled by
|
145
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
146
|
+
# behavior.
|
147
|
+
#
|
148
|
+
# To override this and force automatic setting of timestamp columns one
|
149
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
150
|
+
#
|
151
|
+
# record_timestamps: true # Always set timestamps automatically
|
152
|
+
# record_timestamps: false # Never set timestamps automatically
|
153
|
+
#
|
110
154
|
# Because it relies on the index information from the database
|
111
155
|
# <tt>:unique_by</tt> is recommended to be paired with
|
112
156
|
# Active Record's schema_cache.
|
@@ -120,8 +164,16 @@ module ActiveRecord
|
|
120
164
|
# { id: 1, title: "Rework", author: "David" },
|
121
165
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
122
166
|
# ])
|
123
|
-
|
124
|
-
|
167
|
+
#
|
168
|
+
# # insert_all works on chained scopes, and you can use create_with
|
169
|
+
# # to set default attributes for all inserted records.
|
170
|
+
#
|
171
|
+
# author.books.create_with(created_at: Time.now).insert_all([
|
172
|
+
# { id: 1, title: "Rework" },
|
173
|
+
# { id: 2, title: "Eloquent Ruby" }
|
174
|
+
# ])
|
175
|
+
def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
|
176
|
+
InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
125
177
|
end
|
126
178
|
|
127
179
|
# Inserts a single record into the database in a single SQL INSERT
|
@@ -129,9 +181,9 @@ module ActiveRecord
|
|
129
181
|
# Active Record callbacks or validations. Though passed values
|
130
182
|
# go through Active Record's type casting and serialization.
|
131
183
|
#
|
132
|
-
# See
|
133
|
-
def insert!(attributes, returning: nil)
|
134
|
-
insert_all!([ attributes ], returning: returning)
|
184
|
+
# See #insert_all! for more.
|
185
|
+
def insert!(attributes, returning: nil, record_timestamps: nil)
|
186
|
+
insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
|
135
187
|
end
|
136
188
|
|
137
189
|
# Inserts multiple records into the database in a single SQL INSERT
|
@@ -142,24 +194,37 @@ module ActiveRecord
|
|
142
194
|
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
143
195
|
# the attributes for a single row and must have the same keys.
|
144
196
|
#
|
145
|
-
# Raises
|
197
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
146
198
|
# unique index on the table. In that case, no rows are inserted.
|
147
199
|
#
|
148
|
-
# To skip duplicate rows, see
|
149
|
-
# To replace them, see <tt>ActiveRecord::Persistence#upsert_all</tt>.
|
200
|
+
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
150
201
|
#
|
151
|
-
# Returns an
|
202
|
+
# Returns an ActiveRecord::Result with its contents based on
|
152
203
|
# <tt>:returning</tt> (see below).
|
153
204
|
#
|
154
205
|
# ==== Options
|
155
206
|
#
|
156
207
|
# [:returning]
|
157
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
208
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
158
209
|
# inserted records, which by default is the primary key.
|
159
210
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
160
211
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
161
212
|
# clause entirely.
|
162
213
|
#
|
214
|
+
# You can also pass an SQL string if you need more control on the return values
|
215
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
216
|
+
#
|
217
|
+
# [:record_timestamps]
|
218
|
+
# By default, automatic setting of timestamp columns is controlled by
|
219
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
220
|
+
# behavior.
|
221
|
+
#
|
222
|
+
# To override this and force automatic setting of timestamp columns one
|
223
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
224
|
+
#
|
225
|
+
# record_timestamps: true # Always set timestamps automatically
|
226
|
+
# record_timestamps: false # Never set timestamps automatically
|
227
|
+
#
|
163
228
|
# ==== Examples
|
164
229
|
#
|
165
230
|
# # Insert multiple records
|
@@ -174,8 +239,8 @@ module ActiveRecord
|
|
174
239
|
# { id: 1, title: "Rework", author: "David" },
|
175
240
|
# { id: 1, title: "Eloquent Ruby", author: "Russ" }
|
176
241
|
# ])
|
177
|
-
def insert_all!(attributes, returning: nil)
|
178
|
-
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning).execute
|
242
|
+
def insert_all!(attributes, returning: nil, record_timestamps: nil)
|
243
|
+
InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
|
179
244
|
end
|
180
245
|
|
181
246
|
# Updates or inserts (upserts) a single record into the database in a
|
@@ -183,9 +248,9 @@ module ActiveRecord
|
|
183
248
|
# it trigger Active Record callbacks or validations. Though passed values
|
184
249
|
# go through Active Record's type casting and serialization.
|
185
250
|
#
|
186
|
-
# See
|
187
|
-
def upsert(attributes,
|
188
|
-
upsert_all([ attributes ],
|
251
|
+
# See #upsert_all for documentation.
|
252
|
+
def upsert(attributes, **kwargs)
|
253
|
+
upsert_all([ attributes ], **kwargs)
|
189
254
|
end
|
190
255
|
|
191
256
|
# Updates or inserts (upserts) multiple records into the database in a
|
@@ -196,18 +261,25 @@ module ActiveRecord
|
|
196
261
|
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
197
262
|
# the attributes for a single row and must have the same keys.
|
198
263
|
#
|
199
|
-
# Returns an
|
264
|
+
# Returns an ActiveRecord::Result with its contents based on
|
200
265
|
# <tt>:returning</tt> (see below).
|
201
266
|
#
|
267
|
+
# By default, +upsert_all+ will update all the columns that can be updated when
|
268
|
+
# there is a conflict. These are all the columns except primary keys, read-only
|
269
|
+
# columns, and columns covered by the optional +unique_by+.
|
270
|
+
#
|
202
271
|
# ==== Options
|
203
272
|
#
|
204
273
|
# [:returning]
|
205
|
-
# (PostgreSQL only) An array of attributes to return for all successfully
|
274
|
+
# (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
|
206
275
|
# inserted records, which by default is the primary key.
|
207
276
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
208
277
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
209
278
|
# clause entirely.
|
210
279
|
#
|
280
|
+
# You can also pass an SQL string if you need more control on the return values
|
281
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
282
|
+
#
|
211
283
|
# [:unique_by]
|
212
284
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
213
285
|
# by every unique index on the table. Any duplicate rows are skipped.
|
@@ -216,7 +288,7 @@ module ActiveRecord
|
|
216
288
|
#
|
217
289
|
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
218
290
|
# row has an existing id, or is not unique by another unique index,
|
219
|
-
#
|
291
|
+
# ActiveRecord::RecordNotUnique is raised.
|
220
292
|
#
|
221
293
|
# Unique indexes can be identified by columns or name:
|
222
294
|
#
|
@@ -228,6 +300,54 @@ module ActiveRecord
|
|
228
300
|
# <tt>:unique_by</tt> is recommended to be paired with
|
229
301
|
# Active Record's schema_cache.
|
230
302
|
#
|
303
|
+
# [:on_duplicate]
|
304
|
+
# Configure the SQL update sentence that will be used in case of conflict.
|
305
|
+
#
|
306
|
+
# NOTE: If you use this option you must provide all the columns you want to update
|
307
|
+
# by yourself.
|
308
|
+
#
|
309
|
+
# Example:
|
310
|
+
#
|
311
|
+
# Commodity.upsert_all(
|
312
|
+
# [
|
313
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
314
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
315
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
316
|
+
# ],
|
317
|
+
# on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
|
318
|
+
# )
|
319
|
+
#
|
320
|
+
# See the related +:update_only+ option. Both options can't be used at the same time.
|
321
|
+
#
|
322
|
+
# [:update_only]
|
323
|
+
# Provide a list of column names that will be updated in case of conflict. If not provided,
|
324
|
+
# +upsert_all+ will update all the columns that can be updated. These are all the columns
|
325
|
+
# except primary keys, read-only columns, and columns covered by the optional +unique_by+
|
326
|
+
#
|
327
|
+
# Example:
|
328
|
+
#
|
329
|
+
# Commodity.upsert_all(
|
330
|
+
# [
|
331
|
+
# { id: 2, name: "Copper", price: 4.84 },
|
332
|
+
# { id: 4, name: "Gold", price: 1380.87 },
|
333
|
+
# { id: 6, name: "Aluminium", price: 0.35 }
|
334
|
+
# ],
|
335
|
+
# update_only: [:price] # Only prices will be updated
|
336
|
+
# )
|
337
|
+
#
|
338
|
+
# See the related +:on_duplicate+ option. Both options can't be used at the same time.
|
339
|
+
#
|
340
|
+
# [:record_timestamps]
|
341
|
+
# By default, automatic setting of timestamp columns is controlled by
|
342
|
+
# the model's <tt>record_timestamps</tt> config, matching typical
|
343
|
+
# behavior.
|
344
|
+
#
|
345
|
+
# To override this and force automatic setting of timestamp columns one
|
346
|
+
# way or the other, pass <tt>:record_timestamps</tt>:
|
347
|
+
#
|
348
|
+
# record_timestamps: true # Always set timestamps automatically
|
349
|
+
# record_timestamps: false # Never set timestamps automatically
|
350
|
+
#
|
231
351
|
# ==== Examples
|
232
352
|
#
|
233
353
|
# # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
|
@@ -239,8 +359,8 @@ module ActiveRecord
|
|
239
359
|
# ], unique_by: :isbn)
|
240
360
|
#
|
241
361
|
# Book.find_by(isbn: "1").title # => "Eloquent Ruby"
|
242
|
-
def upsert_all(attributes, returning: nil, unique_by: nil)
|
243
|
-
InsertAll.new(self, attributes, on_duplicate: :
|
362
|
+
def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
|
363
|
+
InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
|
244
364
|
end
|
245
365
|
|
246
366
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
@@ -264,6 +384,7 @@ module ActiveRecord
|
|
264
384
|
# ==== Parameters
|
265
385
|
#
|
266
386
|
# * +id+ - This should be the id or an array of ids to be updated.
|
387
|
+
# Optional argument, defaults to all records in the relation.
|
267
388
|
# * +attributes+ - This should be a hash of attributes or an array of hashes.
|
268
389
|
#
|
269
390
|
# ==== Examples
|
@@ -286,6 +407,11 @@ module ActiveRecord
|
|
286
407
|
# for updating all records in a single query.
|
287
408
|
def update(id = :all, attributes)
|
288
409
|
if id.is_a?(Array)
|
410
|
+
if id.any?(ActiveRecord::Base)
|
411
|
+
raise ArgumentError,
|
412
|
+
"You are passing an array of ActiveRecord::Base instances to `update`. " \
|
413
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
414
|
+
end
|
289
415
|
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
290
416
|
object.update(attributes[idx])
|
291
417
|
}
|
@@ -303,6 +429,88 @@ module ActiveRecord
|
|
303
429
|
end
|
304
430
|
end
|
305
431
|
|
432
|
+
# Updates the object (or multiple objects) just like #update but calls #update! instead
|
433
|
+
# of +update+, so an exception is raised if the record is invalid and saving will fail.
|
434
|
+
def update!(id = :all, attributes)
|
435
|
+
if id.is_a?(Array)
|
436
|
+
if id.any?(ActiveRecord::Base)
|
437
|
+
raise ArgumentError,
|
438
|
+
"You are passing an array of ActiveRecord::Base instances to `update!`. " \
|
439
|
+
"Please pass the ids of the objects by calling `pluck(:id)` or `map(&:id)`."
|
440
|
+
end
|
441
|
+
id.map { |one_id| find(one_id) }.each_with_index { |object, idx|
|
442
|
+
object.update!(attributes[idx])
|
443
|
+
}
|
444
|
+
elsif id == :all
|
445
|
+
all.each { |record| record.update!(attributes) }
|
446
|
+
else
|
447
|
+
if ActiveRecord::Base === id
|
448
|
+
raise ArgumentError,
|
449
|
+
"You are passing an instance of ActiveRecord::Base to `update!`. " \
|
450
|
+
"Please pass the id of the object by calling `.id`."
|
451
|
+
end
|
452
|
+
object = find(id)
|
453
|
+
object.update!(attributes)
|
454
|
+
object
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
# Accepts a list of attribute names to be used in the WHERE clause
|
459
|
+
# of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
|
460
|
+
#
|
461
|
+
# class Developer < ActiveRecord::Base
|
462
|
+
# query_constraints :company_id, :id
|
463
|
+
# end
|
464
|
+
#
|
465
|
+
# developer = Developer.first
|
466
|
+
# # SELECT "developers".* FROM "developers" ORDER BY "developers"."company_id" ASC, "developers"."id" ASC LIMIT 1
|
467
|
+
# developer.inspect # => #<Developer id: 1, company_id: 1, ...>
|
468
|
+
#
|
469
|
+
# developer.update!(name: "Nikita")
|
470
|
+
# # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
471
|
+
#
|
472
|
+
# # It is possible to update an attribute used in the query_constraints clause:
|
473
|
+
# developer.update!(company_id: 2)
|
474
|
+
# # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
475
|
+
#
|
476
|
+
# developer.name = "Bob"
|
477
|
+
# developer.save!
|
478
|
+
# # UPDATE "developers" SET "name" = 'Bob' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
479
|
+
#
|
480
|
+
# developer.destroy!
|
481
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
482
|
+
#
|
483
|
+
# developer.delete
|
484
|
+
# # DELETE FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
|
485
|
+
#
|
486
|
+
# developer.reload
|
487
|
+
# # SELECT "developers".* FROM "developers" WHERE "developers"."company_id" = 1 AND "developers"."id" = 1 LIMIT 1
|
488
|
+
def query_constraints(*columns_list)
|
489
|
+
raise ArgumentError, "You must specify at least one column to be used in querying" if columns_list.empty?
|
490
|
+
|
491
|
+
@query_constraints_list = columns_list.map(&:to_s)
|
492
|
+
@has_query_constraints = @query_constraints_list
|
493
|
+
end
|
494
|
+
|
495
|
+
def has_query_constraints? # :nodoc:
|
496
|
+
@has_query_constraints
|
497
|
+
end
|
498
|
+
|
499
|
+
def query_constraints_list # :nodoc:
|
500
|
+
@query_constraints_list ||= if base_class? || primary_key != base_class.primary_key
|
501
|
+
primary_key if primary_key.is_a?(Array)
|
502
|
+
else
|
503
|
+
base_class.query_constraints_list
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# Returns an array of column names to be used in queries. The source of column
|
508
|
+
# names is derived from +query_constraints_list+ or +primary_key+. This method
|
509
|
+
# is for internal use when the primary key is to be treated as an array.
|
510
|
+
def composite_query_constraints_list # :nodoc:
|
511
|
+
@composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
|
512
|
+
end
|
513
|
+
|
306
514
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
307
515
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
308
516
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -323,7 +531,13 @@ module ActiveRecord
|
|
323
531
|
# todos = [1,2,3]
|
324
532
|
# Todo.destroy(todos)
|
325
533
|
def destroy(id)
|
326
|
-
if
|
534
|
+
multiple_ids = if composite_primary_key?
|
535
|
+
id.first.is_a?(Array)
|
536
|
+
else
|
537
|
+
id.is_a?(Array)
|
538
|
+
end
|
539
|
+
|
540
|
+
if multiple_ids
|
327
541
|
find(id).each(&:destroy)
|
328
542
|
else
|
329
543
|
find(id).destroy
|
@@ -352,50 +566,73 @@ module ActiveRecord
|
|
352
566
|
delete_by(primary_key => id_or_array)
|
353
567
|
end
|
354
568
|
|
355
|
-
def _insert_record(values) # :nodoc:
|
569
|
+
def _insert_record(values, returning) # :nodoc:
|
356
570
|
primary_key = self.primary_key
|
357
571
|
primary_key_value = nil
|
358
572
|
|
359
|
-
if
|
360
|
-
|
361
|
-
|
362
|
-
if !primary_key_value && prefetch_primary_key?
|
573
|
+
if prefetch_primary_key? && primary_key
|
574
|
+
values[primary_key] ||= begin
|
363
575
|
primary_key_value = next_sequence_value
|
364
|
-
|
576
|
+
_default_attributes[primary_key].with_cast_value(primary_key_value)
|
365
577
|
end
|
366
578
|
end
|
367
579
|
|
580
|
+
im = Arel::InsertManager.new(arel_table)
|
581
|
+
|
368
582
|
if values.empty?
|
369
|
-
im
|
370
|
-
im.into arel_table
|
583
|
+
im.insert(connection.empty_insert_statement_value(primary_key))
|
371
584
|
else
|
372
|
-
im
|
585
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
373
586
|
end
|
374
587
|
|
375
|
-
connection.insert(
|
588
|
+
connection.insert(
|
589
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
590
|
+
returning: returning
|
591
|
+
)
|
376
592
|
end
|
377
593
|
|
378
594
|
def _update_record(values, constraints) # :nodoc:
|
379
|
-
constraints =
|
595
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
596
|
+
|
597
|
+
default_constraint = build_default_constraint
|
598
|
+
constraints << default_constraint if default_constraint
|
599
|
+
|
600
|
+
if current_scope = self.global_current_scope
|
601
|
+
constraints << current_scope.where_clause.ast
|
602
|
+
end
|
380
603
|
|
381
|
-
um =
|
382
|
-
|
383
|
-
|
604
|
+
um = Arel::UpdateManager.new(arel_table)
|
605
|
+
um.set(values.transform_keys { |name| arel_table[name] })
|
606
|
+
um.wheres = constraints
|
384
607
|
|
385
608
|
connection.update(um, "#{self} Update")
|
386
609
|
end
|
387
610
|
|
388
611
|
def _delete_record(constraints) # :nodoc:
|
389
|
-
constraints =
|
612
|
+
constraints = constraints.map { |name, value| predicate_builder[name, value] }
|
390
613
|
|
391
|
-
|
392
|
-
|
614
|
+
default_constraint = build_default_constraint
|
615
|
+
constraints << default_constraint if default_constraint
|
616
|
+
|
617
|
+
if current_scope = self.global_current_scope
|
618
|
+
constraints << current_scope.where_clause.ast
|
619
|
+
end
|
620
|
+
|
621
|
+
dm = Arel::DeleteManager.new(arel_table)
|
393
622
|
dm.wheres = constraints
|
394
623
|
|
395
624
|
connection.delete(dm, "#{self} Destroy")
|
396
625
|
end
|
397
626
|
|
398
627
|
private
|
628
|
+
def inherited(subclass)
|
629
|
+
super
|
630
|
+
subclass.class_eval do
|
631
|
+
@_query_constraints_list = nil
|
632
|
+
@has_query_constraints = false
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
399
636
|
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
400
637
|
# new instance of the class. Accepts only keys as strings.
|
401
638
|
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
@@ -412,12 +649,14 @@ module ActiveRecord
|
|
412
649
|
self
|
413
650
|
end
|
414
651
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
652
|
+
# Called by +_update_record+ and +_delete_record+
|
653
|
+
# to build `where` clause from default scopes.
|
654
|
+
# Skips empty scopes.
|
655
|
+
def build_default_constraint
|
656
|
+
return unless default_scopes?(all_queries: true)
|
657
|
+
|
658
|
+
default_where_clause = default_scoped(all_queries: true).where_clause
|
659
|
+
default_where_clause.ast unless default_where_clause.empty?
|
421
660
|
end
|
422
661
|
end
|
423
662
|
|
@@ -428,12 +667,17 @@ module ActiveRecord
|
|
428
667
|
end
|
429
668
|
|
430
669
|
# Returns true if this object was just created -- that is, prior to the last
|
431
|
-
#
|
670
|
+
# update or delete, the object didn't exist in the database and new_record? would have
|
432
671
|
# returned true.
|
433
672
|
def previously_new_record?
|
434
673
|
@previously_new_record
|
435
674
|
end
|
436
675
|
|
676
|
+
# Returns true if this object was previously persisted but now it has been deleted.
|
677
|
+
def previously_persisted?
|
678
|
+
!new_record? && destroyed?
|
679
|
+
end
|
680
|
+
|
437
681
|
# Returns true if this object has been destroyed, otherwise returns false.
|
438
682
|
def destroyed?
|
439
683
|
@destroyed
|
@@ -522,6 +766,7 @@ module ActiveRecord
|
|
522
766
|
def delete
|
523
767
|
_delete_row if persisted?
|
524
768
|
@destroyed = true
|
769
|
+
@previously_new_record = false
|
525
770
|
freeze
|
526
771
|
end
|
527
772
|
|
@@ -535,12 +780,9 @@ module ActiveRecord
|
|
535
780
|
def destroy
|
536
781
|
_raise_readonly_record_error if readonly?
|
537
782
|
destroy_associations
|
538
|
-
@_trigger_destroy_callback
|
539
|
-
destroy_row > 0
|
540
|
-
else
|
541
|
-
true
|
542
|
-
end
|
783
|
+
@_trigger_destroy_callback ||= persisted? && destroy_row > 0
|
543
784
|
@destroyed = true
|
785
|
+
@previously_new_record = false
|
544
786
|
freeze
|
545
787
|
end
|
546
788
|
|
@@ -556,21 +798,24 @@ module ActiveRecord
|
|
556
798
|
end
|
557
799
|
|
558
800
|
# 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
|
801
|
+
# current record. This is mostly useful in relation to single table
|
802
|
+
# inheritance (STI) structures where you want a subclass to appear as the
|
561
803
|
# superclass. This can be used along with record identification in
|
562
804
|
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
|
563
805
|
# like render <tt>partial: @client.becomes(Company)</tt> to render that
|
564
806
|
# instance using the companies/company partial instead of clients/client.
|
565
807
|
#
|
566
808
|
# Note: The new instance will share a link to the same attributes as the original class.
|
567
|
-
# Therefore the
|
809
|
+
# Therefore the STI column value will still be the same.
|
568
810
|
# Any change to the attributes on either instance will affect both instances.
|
569
|
-
#
|
811
|
+
# This includes any attribute initialization done by the new instance.
|
812
|
+
#
|
813
|
+
# If you want to change the STI column as well, use #becomes! instead.
|
570
814
|
def becomes(klass)
|
571
815
|
became = klass.allocate
|
572
816
|
|
573
817
|
became.send(:initialize) do |becoming|
|
818
|
+
@attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
|
574
819
|
becoming.instance_variable_set(:@attributes, @attributes)
|
575
820
|
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
576
821
|
becoming.instance_variable_set(:@new_record, new_record?)
|
@@ -581,11 +826,11 @@ module ActiveRecord
|
|
581
826
|
became
|
582
827
|
end
|
583
828
|
|
584
|
-
# Wrapper around #becomes that also changes the instance's
|
829
|
+
# Wrapper around #becomes that also changes the instance's STI column value.
|
585
830
|
# This is especially useful if you want to persist the changed class in your
|
586
831
|
# database.
|
587
832
|
#
|
588
|
-
# Note: The old instance's
|
833
|
+
# Note: The old instance's STI column value will be changed too, as both objects
|
589
834
|
# share the same set of attributes.
|
590
835
|
def becomes!(klass)
|
591
836
|
became = becomes(klass)
|
@@ -605,7 +850,7 @@ module ActiveRecord
|
|
605
850
|
# * updated_at/updated_on column is updated if that column is available.
|
606
851
|
# * Updates all the attributes that are dirty in this object.
|
607
852
|
#
|
608
|
-
# This method raises an ActiveRecord::ActiveRecordError
|
853
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
609
854
|
# attribute is marked as readonly.
|
610
855
|
#
|
611
856
|
# Also see #update_column.
|
@@ -617,6 +862,28 @@ module ActiveRecord
|
|
617
862
|
save(validate: false)
|
618
863
|
end
|
619
864
|
|
865
|
+
# Updates a single attribute and saves the record.
|
866
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
867
|
+
#
|
868
|
+
# * Validation is skipped.
|
869
|
+
# * \Callbacks are invoked.
|
870
|
+
# * updated_at/updated_on column is updated if that column is available.
|
871
|
+
# * Updates all the attributes that are dirty in this object.
|
872
|
+
#
|
873
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
874
|
+
# attribute is marked as readonly.
|
875
|
+
#
|
876
|
+
# If any of the <tt>before_*</tt> callbacks throws +:abort+ the action is cancelled
|
877
|
+
# and #update_attribute! raises ActiveRecord::RecordNotSaved. See
|
878
|
+
# ActiveRecord::Callbacks for further details.
|
879
|
+
def update_attribute!(name, value)
|
880
|
+
name = name.to_s
|
881
|
+
verify_readonly_attribute(name)
|
882
|
+
public_send("#{name}=", value)
|
883
|
+
|
884
|
+
save!(validate: false)
|
885
|
+
end
|
886
|
+
|
620
887
|
# Updates the attributes of the model from the passed-in hash and saves the
|
621
888
|
# record, all wrapped in a transaction. If the object is invalid, the saving
|
622
889
|
# will fail and false will be returned.
|
@@ -664,6 +931,7 @@ module ActiveRecord
|
|
664
931
|
def update_columns(attributes)
|
665
932
|
raise ActiveRecordError, "cannot update a new record" if new_record?
|
666
933
|
raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
|
934
|
+
_raise_readonly_record_error if readonly?
|
667
935
|
|
668
936
|
attributes = attributes.transform_keys do |key|
|
669
937
|
name = key.to_s
|
@@ -671,14 +939,15 @@ module ActiveRecord
|
|
671
939
|
verify_readonly_attribute(name) || name
|
672
940
|
end
|
673
941
|
|
674
|
-
|
675
|
-
attributes.
|
676
|
-
|
942
|
+
update_constraints = _query_constraints_hash
|
943
|
+
attributes = attributes.each_with_object({}) do |(k, v), h|
|
944
|
+
h[k] = @attributes.write_cast_value(k, v)
|
945
|
+
clear_attribute_change(k)
|
677
946
|
end
|
678
947
|
|
679
948
|
affected_rows = self.class._update_record(
|
680
949
|
attributes,
|
681
|
-
|
950
|
+
update_constraints
|
682
951
|
)
|
683
952
|
|
684
953
|
affected_rows == 1
|
@@ -800,13 +1069,14 @@ module ActiveRecord
|
|
800
1069
|
def reload(options = nil)
|
801
1070
|
self.class.connection.clear_query_cache
|
802
1071
|
|
803
|
-
fresh_object =
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
end
|
1072
|
+
fresh_object = if apply_scoping?(options)
|
1073
|
+
_find_record((options || {}).merge(all_queries: true))
|
1074
|
+
else
|
1075
|
+
self.class.unscoped { _find_record(options) }
|
1076
|
+
end
|
809
1077
|
|
1078
|
+
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
1079
|
+
@association_cache.each_value { |association| association.owner = self }
|
810
1080
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
811
1081
|
@new_record = false
|
812
1082
|
@previously_new_record = false
|
@@ -849,12 +1119,15 @@ module ActiveRecord
|
|
849
1119
|
#
|
850
1120
|
def touch(*names, time: nil)
|
851
1121
|
_raise_record_not_touched_error unless persisted?
|
1122
|
+
_raise_readonly_record_error if readonly?
|
852
1123
|
|
853
1124
|
attribute_names = timestamp_attributes_for_update_in_model
|
854
|
-
attribute_names
|
1125
|
+
attribute_names = (attribute_names | names).map! do |name|
|
855
1126
|
name = name.to_s
|
856
|
-
self.class.attribute_aliases[name] || name
|
857
|
-
|
1127
|
+
name = self.class.attribute_aliases[name] || name
|
1128
|
+
verify_readonly_attribute(name)
|
1129
|
+
name
|
1130
|
+
end
|
858
1131
|
|
859
1132
|
unless attribute_names.empty?
|
860
1133
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -865,6 +1138,54 @@ module ActiveRecord
|
|
865
1138
|
end
|
866
1139
|
|
867
1140
|
private
|
1141
|
+
def init_internals
|
1142
|
+
super
|
1143
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
1144
|
+
@previously_new_record = false
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def strict_loaded_associations
|
1148
|
+
@association_cache.find_all do |_, assoc|
|
1149
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
1150
|
+
end.map(&:first)
|
1151
|
+
end
|
1152
|
+
|
1153
|
+
def _find_record(options)
|
1154
|
+
all_queries = options ? options[:all_queries] : nil
|
1155
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
1156
|
+
|
1157
|
+
if options && options[:lock]
|
1158
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
1159
|
+
else
|
1160
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
1161
|
+
end
|
1162
|
+
end
|
1163
|
+
|
1164
|
+
def _in_memory_query_constraints_hash
|
1165
|
+
if self.class.query_constraints_list.nil?
|
1166
|
+
{ @primary_key => id }
|
1167
|
+
else
|
1168
|
+
self.class.query_constraints_list.index_with do |column_name|
|
1169
|
+
attribute(column_name)
|
1170
|
+
end
|
1171
|
+
end
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
def apply_scoping?(options)
|
1175
|
+
!(options && options[:unscoped]) &&
|
1176
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1177
|
+
end
|
1178
|
+
|
1179
|
+
def _query_constraints_hash
|
1180
|
+
if self.class.query_constraints_list.nil?
|
1181
|
+
{ @primary_key => id_in_database }
|
1182
|
+
else
|
1183
|
+
self.class.query_constraints_list.index_with do |column_name|
|
1184
|
+
attribute_in_database(column_name)
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
end
|
1188
|
+
|
868
1189
|
# A hook to be overridden by association modules.
|
869
1190
|
def destroy_associations
|
870
1191
|
end
|
@@ -874,7 +1195,7 @@ module ActiveRecord
|
|
874
1195
|
end
|
875
1196
|
|
876
1197
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
1198
|
+
self.class._delete_record(_query_constraints_hash)
|
878
1199
|
end
|
879
1200
|
|
880
1201
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +1211,7 @@ module ActiveRecord
|
|
890
1211
|
def _update_row(attribute_names, attempted_action = "update")
|
891
1212
|
self.class._update_record(
|
892
1213
|
attributes_with_values(attribute_names),
|
893
|
-
|
1214
|
+
_query_constraints_hash
|
894
1215
|
)
|
895
1216
|
end
|
896
1217
|
|
@@ -926,11 +1247,16 @@ module ActiveRecord
|
|
926
1247
|
def _create_record(attribute_names = self.attribute_names)
|
927
1248
|
attribute_names = attributes_for_create(attribute_names)
|
928
1249
|
|
929
|
-
|
930
|
-
|
1250
|
+
returning_columns = self.class._returning_columns_for_insert
|
1251
|
+
|
1252
|
+
returning_values = self.class._insert_record(
|
1253
|
+
attributes_with_values(attribute_names),
|
1254
|
+
returning_columns
|
931
1255
|
)
|
932
1256
|
|
933
|
-
|
1257
|
+
returning_columns.zip(returning_values).each do |column, value|
|
1258
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
1259
|
+
end if returning_values
|
934
1260
|
|
935
1261
|
@new_record = false
|
936
1262
|
@previously_new_record = true
|
@@ -946,7 +1272,8 @@ module ActiveRecord
|
|
946
1272
|
|
947
1273
|
def _raise_record_not_destroyed
|
948
1274
|
@_association_destroy_exception ||= nil
|
949
|
-
|
1275
|
+
key = self.class.primary_key
|
1276
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
950
1277
|
ensure
|
951
1278
|
@_association_destroy_exception = nil
|
952
1279
|
end
|
@@ -961,11 +1288,5 @@ module ActiveRecord
|
|
961
1288
|
persisted?, new_record?, or destroyed? before touching.
|
962
1289
|
MSG
|
963
1290
|
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
1291
|
end
|
971
1292
|
end
|