activerecord 6.1.6 → 7.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1627 -983
- 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 +50 -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 +35 -31
- 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.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -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 +439 -305
- 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 +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- 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 -34
- 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 +96 -590
- 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 +77 -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 +360 -138
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +631 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- 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 +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +104 -53
- 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/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 +394 -74
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +509 -247
- 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 +294 -102
- 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 +254 -0
- data/lib/active_record/connection_adapters.rb +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +202 -223
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -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 +61 -15
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- 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 +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -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 +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +154 -63
- data/lib/active_record/errors.rb +171 -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 +131 -86
- data/lib/active_record/future_result.rb +164 -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 +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- data/lib/active_record/marshalling.rb +56 -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 +221 -48
- 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 +358 -171
- data/lib/active_record/model_schema.rb +120 -101
- data/lib/active_record/nested_attributes.rb +37 -18
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- 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 +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- 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 +241 -80
- 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 +219 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- 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 +4 -6
- 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 +654 -127
- 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 +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -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 +16 -11
- 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 +225 -136
- 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 +123 -99
- data/lib/active_record/timestamp.rb +29 -18
- 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 +0 -12
- 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 +139 -19
- 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 +93 -13
- 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,7 +109,7 @@ 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
|
@@ -91,6 +121,9 @@ module ActiveRecord
|
|
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,13 +194,12 @@ 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
|
@@ -160,6 +211,20 @@ module ActiveRecord
|
|
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,9 +261,13 @@ 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]
|
@@ -208,6 +277,9 @@ module ActiveRecord
|
|
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 attribute used in the query_by 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,13 @@ 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)
|
810
1079
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
811
1080
|
@new_record = false
|
812
1081
|
@previously_new_record = false
|
@@ -849,12 +1118,15 @@ module ActiveRecord
|
|
849
1118
|
#
|
850
1119
|
def touch(*names, time: nil)
|
851
1120
|
_raise_record_not_touched_error unless persisted?
|
1121
|
+
_raise_readonly_record_error if readonly?
|
852
1122
|
|
853
1123
|
attribute_names = timestamp_attributes_for_update_in_model
|
854
|
-
attribute_names
|
1124
|
+
attribute_names = (attribute_names | names).map! do |name|
|
855
1125
|
name = name.to_s
|
856
|
-
self.class.attribute_aliases[name] || name
|
857
|
-
|
1126
|
+
name = self.class.attribute_aliases[name] || name
|
1127
|
+
verify_readonly_attribute(name)
|
1128
|
+
name
|
1129
|
+
end
|
858
1130
|
|
859
1131
|
unless attribute_names.empty?
|
860
1132
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -865,6 +1137,54 @@ module ActiveRecord
|
|
865
1137
|
end
|
866
1138
|
|
867
1139
|
private
|
1140
|
+
def init_internals
|
1141
|
+
super
|
1142
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
1143
|
+
@previously_new_record = false
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
def strict_loaded_associations
|
1147
|
+
@association_cache.find_all do |_, assoc|
|
1148
|
+
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
1149
|
+
end.map(&:first)
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
def _find_record(options)
|
1153
|
+
all_queries = options ? options[:all_queries] : nil
|
1154
|
+
base = self.class.all(all_queries: all_queries).preload(strict_loaded_associations)
|
1155
|
+
|
1156
|
+
if options && options[:lock]
|
1157
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
1158
|
+
else
|
1159
|
+
base.find_by!(_in_memory_query_constraints_hash)
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
def _in_memory_query_constraints_hash
|
1164
|
+
if self.class.query_constraints_list.nil?
|
1165
|
+
{ @primary_key => id }
|
1166
|
+
else
|
1167
|
+
self.class.query_constraints_list.index_with do |column_name|
|
1168
|
+
attribute(column_name)
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
def apply_scoping?(options)
|
1174
|
+
!(options && options[:unscoped]) &&
|
1175
|
+
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
def _query_constraints_hash
|
1179
|
+
if self.class.query_constraints_list.nil?
|
1180
|
+
{ @primary_key => id_in_database }
|
1181
|
+
else
|
1182
|
+
self.class.query_constraints_list.index_with do |column_name|
|
1183
|
+
attribute_in_database(column_name)
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
|
868
1188
|
# A hook to be overridden by association modules.
|
869
1189
|
def destroy_associations
|
870
1190
|
end
|
@@ -874,7 +1194,7 @@ module ActiveRecord
|
|
874
1194
|
end
|
875
1195
|
|
876
1196
|
def _delete_row
|
877
|
-
self.class._delete_record(
|
1197
|
+
self.class._delete_record(_query_constraints_hash)
|
878
1198
|
end
|
879
1199
|
|
880
1200
|
def _touch_row(attribute_names, time)
|
@@ -890,7 +1210,7 @@ module ActiveRecord
|
|
890
1210
|
def _update_row(attribute_names, attempted_action = "update")
|
891
1211
|
self.class._update_record(
|
892
1212
|
attributes_with_values(attribute_names),
|
893
|
-
|
1213
|
+
_query_constraints_hash
|
894
1214
|
)
|
895
1215
|
end
|
896
1216
|
|
@@ -926,11 +1246,16 @@ module ActiveRecord
|
|
926
1246
|
def _create_record(attribute_names = self.attribute_names)
|
927
1247
|
attribute_names = attributes_for_create(attribute_names)
|
928
1248
|
|
929
|
-
|
930
|
-
|
1249
|
+
returning_columns = self.class._returning_columns_for_insert
|
1250
|
+
|
1251
|
+
returning_values = self.class._insert_record(
|
1252
|
+
attributes_with_values(attribute_names),
|
1253
|
+
returning_columns
|
931
1254
|
)
|
932
1255
|
|
933
|
-
|
1256
|
+
returning_columns.zip(returning_values).each do |column, value|
|
1257
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
1258
|
+
end if returning_values
|
934
1259
|
|
935
1260
|
@new_record = false
|
936
1261
|
@previously_new_record = true
|
@@ -946,7 +1271,8 @@ module ActiveRecord
|
|
946
1271
|
|
947
1272
|
def _raise_record_not_destroyed
|
948
1273
|
@_association_destroy_exception ||= nil
|
949
|
-
|
1274
|
+
key = self.class.primary_key
|
1275
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
950
1276
|
ensure
|
951
1277
|
@_association_destroy_exception = nil
|
952
1278
|
end
|
@@ -961,11 +1287,5 @@ module ActiveRecord
|
|
961
1287
|
persisted?, new_record?, or destroyed? before touching.
|
962
1288
|
MSG
|
963
1289
|
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
1290
|
end
|
971
1291
|
end
|