activerecord 7.0.4 → 7.1.5.1
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 +1971 -1243
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +20 -14
- data/lib/active_record/associations/collection_proxy.rb +20 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +333 -222
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +21 -8
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -4
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +148 -26
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +59 -10
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +16 -32
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +80 -50
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +51 -7
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +297 -127
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -103
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +254 -125
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
- 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 +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +19 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +106 -55
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
- 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/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +365 -61
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +213 -85
- 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 +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +181 -154
- data/lib/active_record/counter_cache.rb +52 -27
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +28 -14
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +15 -10
- 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 +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +23 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -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 +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +40 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +33 -19
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- 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 +4 -0
- data/lib/active_record/middleware/database_selector.rb +9 -11
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +105 -7
- data/lib/active_record/migration/compatibility.rb +163 -58
- 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/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +271 -114
- data/lib/active_record/model_schema.rb +69 -44
- data/lib/active_record/nested_attributes.rb +37 -8
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +195 -42
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +4 -22
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +107 -45
- data/lib/active_record/railties/controller_runtime.rb +14 -9
- data/lib/active_record/railties/databases.rake +144 -150
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +189 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +232 -81
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- 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 +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +408 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +103 -37
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +50 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +9 -9
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +152 -108
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +114 -96
- data/lib/active_record/timestamp.rb +30 -16
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +130 -17
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- 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/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +5 -1
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +83 -18
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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
- metadata +51 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -57,6 +57,36 @@ 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
|
@@ -85,14 +115,14 @@ module ActiveRecord
|
|
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
|
#
|
94
124
|
# You can also pass an SQL string if you need more control on the return values
|
95
|
-
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
125
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
96
126
|
#
|
97
127
|
# [:unique_by]
|
98
128
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
@@ -102,7 +132,7 @@ module ActiveRecord
|
|
102
132
|
#
|
103
133
|
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
104
134
|
# row has an existing id, or is not unique by another unique index,
|
105
|
-
#
|
135
|
+
# ActiveRecord::RecordNotUnique is raised.
|
106
136
|
#
|
107
137
|
# Unique indexes can be identified by columns or name:
|
108
138
|
#
|
@@ -164,7 +194,7 @@ module ActiveRecord
|
|
164
194
|
# The +attributes+ parameter is an Array of Hashes. Every Hash determines
|
165
195
|
# the attributes for a single row and must have the same keys.
|
166
196
|
#
|
167
|
-
# Raises
|
197
|
+
# Raises ActiveRecord::RecordNotUnique if any rows violate a
|
168
198
|
# unique index on the table. In that case, no rows are inserted.
|
169
199
|
#
|
170
200
|
# To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
|
@@ -175,14 +205,14 @@ module ActiveRecord
|
|
175
205
|
# ==== Options
|
176
206
|
#
|
177
207
|
# [:returning]
|
178
|
-
# (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
|
179
209
|
# inserted records, which by default is the primary key.
|
180
210
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
181
211
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
182
212
|
# clause entirely.
|
183
213
|
#
|
184
214
|
# You can also pass an SQL string if you need more control on the return values
|
185
|
-
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
215
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
186
216
|
#
|
187
217
|
# [:record_timestamps]
|
188
218
|
# By default, automatic setting of timestamp columns is controlled by
|
@@ -219,8 +249,8 @@ module ActiveRecord
|
|
219
249
|
# go through Active Record's type casting and serialization.
|
220
250
|
#
|
221
251
|
# See #upsert_all for documentation.
|
222
|
-
def upsert(attributes,
|
223
|
-
upsert_all([ attributes ],
|
252
|
+
def upsert(attributes, **kwargs)
|
253
|
+
upsert_all([ attributes ], **kwargs)
|
224
254
|
end
|
225
255
|
|
226
256
|
# Updates or inserts (upserts) multiple records into the database in a
|
@@ -241,14 +271,14 @@ module ActiveRecord
|
|
241
271
|
# ==== Options
|
242
272
|
#
|
243
273
|
# [:returning]
|
244
|
-
# (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
|
245
275
|
# inserted records, which by default is the primary key.
|
246
276
|
# Pass <tt>returning: %w[ id name ]</tt> for both id and name
|
247
277
|
# or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
|
248
278
|
# clause entirely.
|
249
279
|
#
|
250
280
|
# You can also pass an SQL string if you need more control on the return values
|
251
|
-
# (for example, <tt>returning: "id, name as new_name"</tt>).
|
281
|
+
# (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
|
252
282
|
#
|
253
283
|
# [:unique_by]
|
254
284
|
# (PostgreSQL and SQLite only) By default rows are considered to be unique
|
@@ -258,7 +288,7 @@ module ActiveRecord
|
|
258
288
|
#
|
259
289
|
# Consider a Book model where no duplicate ISBNs make sense, but if any
|
260
290
|
# row has an existing id, or is not unique by another unique index,
|
261
|
-
#
|
291
|
+
# ActiveRecord::RecordNotUnique is raised.
|
262
292
|
#
|
263
293
|
# Unique indexes can be identified by columns or name:
|
264
294
|
#
|
@@ -425,6 +455,62 @@ module ActiveRecord
|
|
425
455
|
end
|
426
456
|
end
|
427
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
|
+
|
428
514
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
429
515
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
430
516
|
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
@@ -445,7 +531,13 @@ module ActiveRecord
|
|
445
531
|
# todos = [1,2,3]
|
446
532
|
# Todo.destroy(todos)
|
447
533
|
def destroy(id)
|
448
|
-
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
|
449
541
|
find(id).each(&:destroy)
|
450
542
|
else
|
451
543
|
find(id).destroy
|
@@ -474,7 +566,7 @@ module ActiveRecord
|
|
474
566
|
delete_by(primary_key => id_or_array)
|
475
567
|
end
|
476
568
|
|
477
|
-
def _insert_record(values) # :nodoc:
|
569
|
+
def _insert_record(values, returning) # :nodoc:
|
478
570
|
primary_key = self.primary_key
|
479
571
|
primary_key_value = nil
|
480
572
|
|
@@ -493,7 +585,10 @@ module ActiveRecord
|
|
493
585
|
im.insert(values.transform_keys { |name| arel_table[name] })
|
494
586
|
end
|
495
587
|
|
496
|
-
connection.insert(
|
588
|
+
connection.insert(
|
589
|
+
im, "#{self} Create", primary_key || false, primary_key_value,
|
590
|
+
returning: returning
|
591
|
+
)
|
497
592
|
end
|
498
593
|
|
499
594
|
def _update_record(values, constraints) # :nodoc:
|
@@ -530,6 +625,14 @@ module ActiveRecord
|
|
530
625
|
end
|
531
626
|
|
532
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
|
+
|
533
636
|
# Given a class, an attributes hash, +instantiate_instance_of+ returns a
|
534
637
|
# new instance of the class. Accepts only keys as strings.
|
535
638
|
def instantiate_instance_of(klass, attributes, column_types = {}, &block)
|
@@ -564,7 +667,7 @@ module ActiveRecord
|
|
564
667
|
end
|
565
668
|
|
566
669
|
# Returns true if this object was just created -- that is, prior to the last
|
567
|
-
#
|
670
|
+
# update or delete, the object didn't exist in the database and new_record? would have
|
568
671
|
# returned true.
|
569
672
|
def previously_new_record?
|
570
673
|
@previously_new_record
|
@@ -663,6 +766,7 @@ module ActiveRecord
|
|
663
766
|
def delete
|
664
767
|
_delete_row if persisted?
|
665
768
|
@destroyed = true
|
769
|
+
@previously_new_record = false
|
666
770
|
freeze
|
667
771
|
end
|
668
772
|
|
@@ -676,12 +780,9 @@ module ActiveRecord
|
|
676
780
|
def destroy
|
677
781
|
_raise_readonly_record_error if readonly?
|
678
782
|
destroy_associations
|
679
|
-
@_trigger_destroy_callback
|
680
|
-
destroy_row > 0
|
681
|
-
else
|
682
|
-
true
|
683
|
-
end
|
783
|
+
@_trigger_destroy_callback ||= persisted? && destroy_row > 0
|
684
784
|
@destroyed = true
|
785
|
+
@previously_new_record = false
|
685
786
|
freeze
|
686
787
|
end
|
687
788
|
|
@@ -707,11 +808,14 @@ module ActiveRecord
|
|
707
808
|
# Note: The new instance will share a link to the same attributes as the original class.
|
708
809
|
# Therefore the STI column value will still be the same.
|
709
810
|
# Any change to the attributes on either instance will affect both instances.
|
811
|
+
# This includes any attribute initialization done by the new instance.
|
812
|
+
#
|
710
813
|
# If you want to change the STI column as well, use #becomes! instead.
|
711
814
|
def becomes(klass)
|
712
815
|
became = klass.allocate
|
713
816
|
|
714
817
|
became.send(:initialize) do |becoming|
|
818
|
+
@attributes.reverse_merge!(becoming.instance_variable_get(:@attributes))
|
715
819
|
becoming.instance_variable_set(:@attributes, @attributes)
|
716
820
|
becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
|
717
821
|
becoming.instance_variable_set(:@new_record, new_record?)
|
@@ -746,7 +850,7 @@ module ActiveRecord
|
|
746
850
|
# * updated_at/updated_on column is updated if that column is available.
|
747
851
|
# * Updates all the attributes that are dirty in this object.
|
748
852
|
#
|
749
|
-
# This method raises an ActiveRecord::ActiveRecordError
|
853
|
+
# This method raises an ActiveRecord::ActiveRecordError if the
|
750
854
|
# attribute is marked as readonly.
|
751
855
|
#
|
752
856
|
# Also see #update_column.
|
@@ -758,6 +862,28 @@ module ActiveRecord
|
|
758
862
|
save(validate: false)
|
759
863
|
end
|
760
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
|
+
|
761
887
|
# Updates the attributes of the model from the passed-in hash and saves the
|
762
888
|
# record, all wrapped in a transaction. If the object is invalid, the saving
|
763
889
|
# will fail and false will be returned.
|
@@ -813,7 +939,7 @@ module ActiveRecord
|
|
813
939
|
verify_readonly_attribute(name) || name
|
814
940
|
end
|
815
941
|
|
816
|
-
update_constraints =
|
942
|
+
update_constraints = _query_constraints_hash
|
817
943
|
attributes = attributes.each_with_object({}) do |(k, v), h|
|
818
944
|
h[k] = @attributes.write_cast_value(k, v)
|
819
945
|
clear_attribute_change(k)
|
@@ -944,12 +1070,13 @@ module ActiveRecord
|
|
944
1070
|
self.class.connection.clear_query_cache
|
945
1071
|
|
946
1072
|
fresh_object = if apply_scoping?(options)
|
947
|
-
_find_record(options)
|
1073
|
+
_find_record((options || {}).merge(all_queries: true))
|
948
1074
|
else
|
949
1075
|
self.class.unscoped { _find_record(options) }
|
950
1076
|
end
|
951
1077
|
|
952
1078
|
@association_cache = fresh_object.instance_variable_get(:@association_cache)
|
1079
|
+
@association_cache.each_value { |association| association.owner = self }
|
953
1080
|
@attributes = fresh_object.instance_variable_get(:@attributes)
|
954
1081
|
@new_record = false
|
955
1082
|
@previously_new_record = false
|
@@ -995,10 +1122,12 @@ module ActiveRecord
|
|
995
1122
|
_raise_readonly_record_error if readonly?
|
996
1123
|
|
997
1124
|
attribute_names = timestamp_attributes_for_update_in_model
|
998
|
-
attribute_names
|
1125
|
+
attribute_names = (attribute_names | names).map! do |name|
|
999
1126
|
name = name.to_s
|
1000
|
-
self.class.attribute_aliases[name] || name
|
1001
|
-
|
1127
|
+
name = self.class.attribute_aliases[name] || name
|
1128
|
+
verify_readonly_attribute(name)
|
1129
|
+
name
|
1130
|
+
end
|
1002
1131
|
|
1003
1132
|
unless attribute_names.empty?
|
1004
1133
|
affected_rows = _touch_row(attribute_names, time)
|
@@ -1009,6 +1138,12 @@ module ActiveRecord
|
|
1009
1138
|
end
|
1010
1139
|
|
1011
1140
|
private
|
1141
|
+
def init_internals
|
1142
|
+
super
|
1143
|
+
@_trigger_destroy_callback = @_trigger_update_callback = nil
|
1144
|
+
@previously_new_record = false
|
1145
|
+
end
|
1146
|
+
|
1012
1147
|
def strict_loaded_associations
|
1013
1148
|
@association_cache.find_all do |_, assoc|
|
1014
1149
|
assoc.owner.strict_loading? && !assoc.owner.strict_loading_n_plus_one_only?
|
@@ -1016,10 +1151,23 @@ module ActiveRecord
|
|
1016
1151
|
end
|
1017
1152
|
|
1018
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
|
+
|
1019
1157
|
if options && options[:lock]
|
1020
|
-
|
1158
|
+
base.lock(options[:lock]).find_by!(_in_memory_query_constraints_hash)
|
1021
1159
|
else
|
1022
|
-
|
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
|
1023
1171
|
end
|
1024
1172
|
end
|
1025
1173
|
|
@@ -1028,8 +1176,14 @@ module ActiveRecord
|
|
1028
1176
|
(self.class.default_scopes?(all_queries: true) || self.class.global_current_scope)
|
1029
1177
|
end
|
1030
1178
|
|
1031
|
-
def
|
1032
|
-
|
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
|
1033
1187
|
end
|
1034
1188
|
|
1035
1189
|
# A hook to be overridden by association modules.
|
@@ -1041,7 +1195,7 @@ module ActiveRecord
|
|
1041
1195
|
end
|
1042
1196
|
|
1043
1197
|
def _delete_row
|
1044
|
-
self.class._delete_record(
|
1198
|
+
self.class._delete_record(_query_constraints_hash)
|
1045
1199
|
end
|
1046
1200
|
|
1047
1201
|
def _touch_row(attribute_names, time)
|
@@ -1057,7 +1211,7 @@ module ActiveRecord
|
|
1057
1211
|
def _update_row(attribute_names, attempted_action = "update")
|
1058
1212
|
self.class._update_record(
|
1059
1213
|
attributes_with_values(attribute_names),
|
1060
|
-
|
1214
|
+
_query_constraints_hash
|
1061
1215
|
)
|
1062
1216
|
end
|
1063
1217
|
|
@@ -1093,11 +1247,16 @@ module ActiveRecord
|
|
1093
1247
|
def _create_record(attribute_names = self.attribute_names)
|
1094
1248
|
attribute_names = attributes_for_create(attribute_names)
|
1095
1249
|
|
1096
|
-
|
1097
|
-
|
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
|
1098
1255
|
)
|
1099
1256
|
|
1100
|
-
|
1257
|
+
returning_columns.zip(returning_values).each do |column, value|
|
1258
|
+
_write_attribute(column, value) if !_read_attribute(column)
|
1259
|
+
end if returning_values
|
1101
1260
|
|
1102
1261
|
@new_record = false
|
1103
1262
|
@previously_new_record = true
|
@@ -1114,7 +1273,7 @@ module ActiveRecord
|
|
1114
1273
|
def _raise_record_not_destroyed
|
1115
1274
|
@_association_destroy_exception ||= nil
|
1116
1275
|
key = self.class.primary_key
|
1117
|
-
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{
|
1276
|
+
raise @_association_destroy_exception || RecordNotDestroyed.new("Failed to destroy #{self.class} with #{key}=#{id}", self)
|
1118
1277
|
ensure
|
1119
1278
|
@_association_destroy_exception = nil
|
1120
1279
|
end
|
@@ -1129,11 +1288,5 @@ module ActiveRecord
|
|
1129
1288
|
persisted?, new_record?, or destroyed? before touching.
|
1130
1289
|
MSG
|
1131
1290
|
end
|
1132
|
-
|
1133
|
-
# The name of the method used to touch a +belongs_to+ association when the
|
1134
|
-
# +:touch+ option is used.
|
1135
|
-
def belongs_to_touch_method
|
1136
|
-
:touch
|
1137
|
-
end
|
1138
1291
|
end
|
1139
1292
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Promise < BasicObject
|
5
|
+
undef_method :==, :!, :!=
|
6
|
+
|
7
|
+
def initialize(future_result, block) # :nodoc:
|
8
|
+
@future_result = future_result
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns whether the associated query is still being executed or not.
|
13
|
+
def pending?
|
14
|
+
@future_result.pending?
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the query result.
|
18
|
+
# If the query wasn't completed yet, accessing +#value+ will block until the query completes.
|
19
|
+
# If the query failed, +#value+ will raise the corresponding error.
|
20
|
+
def value
|
21
|
+
return @value if defined? @value
|
22
|
+
|
23
|
+
result = @future_result.result
|
24
|
+
@value = if @block
|
25
|
+
@block.call(result)
|
26
|
+
else
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a new +ActiveRecord::Promise+ that will apply the passed block
|
32
|
+
# when the value is accessed:
|
33
|
+
#
|
34
|
+
# Post.async_pick(:title).then { |title| title.upcase }.value
|
35
|
+
# # => "POST TITLE"
|
36
|
+
def then(&block)
|
37
|
+
Promise.new(@future_result, @block ? @block >> block : block)
|
38
|
+
end
|
39
|
+
|
40
|
+
[:class, :respond_to?, :is_a?].each do |method|
|
41
|
+
define_method(method, ::Object.instance_method(method))
|
42
|
+
end
|
43
|
+
|
44
|
+
def inspect # :nodoc:
|
45
|
+
"#<ActiveRecord::Promise status=#{status}>"
|
46
|
+
end
|
47
|
+
|
48
|
+
def pretty_print(q) # :nodoc:
|
49
|
+
q.text(inspect)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def status
|
54
|
+
if @future_result.pending?
|
55
|
+
:pending
|
56
|
+
elsif @future_result.canceled?
|
57
|
+
:canceled
|
58
|
+
else
|
59
|
+
:complete
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Complete < self # :nodoc:
|
64
|
+
attr_reader :value
|
65
|
+
|
66
|
+
def initialize(value)
|
67
|
+
@value = value
|
68
|
+
end
|
69
|
+
|
70
|
+
def then
|
71
|
+
Complete.new(yield @value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def pending?
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def status
|
80
|
+
:complete
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -26,32 +26,14 @@ module ActiveRecord
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def self.run
|
29
|
-
|
30
|
-
|
31
|
-
if ActiveRecord.legacy_connection_handling
|
32
|
-
ActiveRecord::Base.connection_handlers.each do |key, handler|
|
33
|
-
pools.concat(handler.connection_pool_list.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
34
|
-
end
|
35
|
-
else
|
36
|
-
pools.concat(ActiveRecord::Base.connection_handler.all_connection_pools.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! })
|
37
|
-
end
|
38
|
-
|
39
|
-
pools
|
29
|
+
ActiveRecord::Base.connection_handler.each_connection_pool.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
|
40
30
|
end
|
41
31
|
|
42
32
|
def self.complete(pools)
|
43
|
-
pools.each { |pool| pool.disable_query_cache! }
|
33
|
+
pools.each { |pool| pool.disable_query_cache! unless pool.discarded? }
|
44
34
|
|
45
|
-
|
46
|
-
|
47
|
-
handler.connection_pool_list.each do |pool|
|
48
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
49
|
-
end
|
50
|
-
end
|
51
|
-
else
|
52
|
-
ActiveRecord::Base.connection_handler.all_connection_pools.each do |pool|
|
53
|
-
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
54
|
-
end
|
35
|
+
ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
|
36
|
+
pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
|
55
37
|
end
|
56
38
|
end
|
57
39
|
|