activerecord 7.1.5.2 → 8.1.2.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 +567 -2443
- data/README.rdoc +16 -16
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +3 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +31 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +18 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +36 -5
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +268 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +8 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +250 -315
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +20 -27
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +96 -68
- data/lib/active_record/autosave_association.rb +93 -58
- data/lib/active_record/base.rb +5 -7
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
- data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
- data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
- data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +66 -0
- data/lib/active_record/connection_handling.rb +88 -46
- data/lib/active_record/core.rb +142 -58
- data/lib/active_record/counter_cache.rb +52 -18
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +28 -5
- data/lib/active_record/database_configurations/hash_config.rb +103 -41
- data/lib/active_record/database_configurations/url_config.rb +30 -1
- data/lib/active_record/database_configurations.rb +8 -4
- data/lib/active_record/delegated_type.rb +43 -19
- data/lib/active_record/dynamic_matchers.rb +56 -71
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +12 -12
- data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
- data/lib/active_record/encryption/encryptor.rb +58 -22
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +57 -36
- data/lib/active_record/errors.rb +76 -26
- data/lib/active_record/explain.rb +14 -25
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +39 -35
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +5 -3
- data/lib/active_record/insert_all.rb +28 -20
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -34
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +46 -14
- data/lib/active_record/migration/compatibility.rb +40 -25
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +119 -103
- data/lib/active_record/model_schema.rb +66 -78
- data/lib/active_record/nested_attributes.rb +17 -9
- data/lib/active_record/persistence.rb +161 -454
- data/lib/active_record/query_cache.rb +33 -15
- data/lib/active_record/query_logs.rb +106 -39
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +28 -16
- data/lib/active_record/railtie.rb +61 -74
- data/lib/active_record/railties/controller_runtime.rb +19 -5
- data/lib/active_record/railties/databases.rake +64 -78
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +147 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +153 -73
- data/lib/active_record/relation/calculations.rb +146 -96
- data/lib/active_record/relation/delegation.rb +30 -23
- data/lib/active_record/relation/finder_methods.rb +64 -34
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +25 -10
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +351 -124
- data/lib/active_record/relation/spawn_methods.rb +9 -25
- data/lib/active_record/relation/where_clause.rb +17 -30
- data/lib/active_record/relation.rb +590 -124
- data/lib/active_record/result.rb +131 -59
- data/lib/active_record/runtime_registry.rb +44 -21
- data/lib/active_record/sanitization.rb +33 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +66 -31
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +68 -20
- data/lib/active_record/statement_cache.rb +29 -25
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +7 -33
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +118 -90
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +123 -89
- data/lib/active_record/testing/query_assertions.rb +127 -0
- data/lib/active_record/timestamp.rb +6 -4
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +129 -0
- data/lib/active_record/transactions.rb +97 -18
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +15 -2
- data/lib/active_record/type/serialized.rb +12 -7
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +5 -4
- data/lib/active_record/validations/associated.rb +10 -4
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +203 -59
- data/lib/arel/alias_predication.rb +3 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +6 -5
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +2 -4
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +14 -5
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -3
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +56 -12
- data/lib/arel/visitors/sqlite.rb +80 -8
- data/lib/arel/visitors/to_sql.rb +34 -37
- data/lib/arel.rb +10 -4
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +23 -14
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -167
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
data/lib/active_record/core.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/core_ext/enumerable"
|
|
4
|
-
require "active_support/core_ext/module/delegation"
|
|
5
4
|
require "active_support/parameter_filter"
|
|
6
5
|
require "concurrent/map"
|
|
7
6
|
|
|
@@ -73,7 +72,7 @@ module ActiveRecord
|
|
|
73
72
|
end
|
|
74
73
|
self.configurations = {}
|
|
75
74
|
|
|
76
|
-
# Returns fully resolved ActiveRecord::DatabaseConfigurations object
|
|
75
|
+
# Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
|
|
77
76
|
def self.configurations
|
|
78
77
|
@@configurations
|
|
79
78
|
end
|
|
@@ -89,6 +88,7 @@ module ActiveRecord
|
|
|
89
88
|
class_attribute :belongs_to_required_by_default, instance_accessor: false
|
|
90
89
|
|
|
91
90
|
class_attribute :strict_loading_by_default, instance_accessor: false, default: false
|
|
91
|
+
class_attribute :strict_loading_mode, instance_accessor: false, default: :all
|
|
92
92
|
|
|
93
93
|
class_attribute :has_many_inversing, instance_accessor: false, default: false
|
|
94
94
|
|
|
@@ -102,6 +102,21 @@ module ActiveRecord
|
|
|
102
102
|
|
|
103
103
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
|
104
104
|
|
|
105
|
+
##
|
|
106
|
+
# :singleton-method:
|
|
107
|
+
#
|
|
108
|
+
# Specifies the attributes that will be included in the output of the
|
|
109
|
+
# #inspect method:
|
|
110
|
+
#
|
|
111
|
+
# Post.attributes_for_inspect = [:id, :title]
|
|
112
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
|
113
|
+
#
|
|
114
|
+
# When set to +:all+ inspect will list all the record's attributes:
|
|
115
|
+
#
|
|
116
|
+
# Post.attributes_for_inspect = :all
|
|
117
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
118
|
+
class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
|
|
119
|
+
|
|
105
120
|
def self.application_record_class? # :nodoc:
|
|
106
121
|
if ActiveRecord.application_record_class
|
|
107
122
|
self == ActiveRecord.application_record_class
|
|
@@ -186,6 +201,17 @@ module ActiveRecord
|
|
|
186
201
|
false
|
|
187
202
|
end
|
|
188
203
|
|
|
204
|
+
# Intended to behave like `.current_preventing_writes` given the class name as input.
|
|
205
|
+
# See PoolConfig and ConnectionHandler::ConnectionDescriptor.
|
|
206
|
+
def self.preventing_writes?(class_name) # :nodoc:
|
|
207
|
+
connected_to_stack.reverse_each do |hash|
|
|
208
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
|
209
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
false
|
|
213
|
+
end
|
|
214
|
+
|
|
189
215
|
def self.connected_to_stack # :nodoc:
|
|
190
216
|
if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
|
|
191
217
|
connected_to_stack
|
|
@@ -331,6 +357,8 @@ module ActiveRecord
|
|
|
331
357
|
def filter_attributes=(filter_attributes)
|
|
332
358
|
@inspection_filter = nil
|
|
333
359
|
@filter_attributes = filter_attributes
|
|
360
|
+
|
|
361
|
+
FilterAttributeHandler.sensitive_attribute_was_declared(self, filter_attributes)
|
|
334
362
|
end
|
|
335
363
|
|
|
336
364
|
def inspection_filter # :nodoc:
|
|
@@ -350,8 +378,8 @@ module ActiveRecord
|
|
|
350
378
|
super
|
|
351
379
|
elsif abstract_class?
|
|
352
380
|
"#{super}(abstract)"
|
|
353
|
-
elsif !connected?
|
|
354
|
-
"#{super} (call '#{super}.
|
|
381
|
+
elsif !schema_loaded? && !connected?
|
|
382
|
+
"#{super} (call '#{super}.load_schema' to load schema informations)"
|
|
355
383
|
elsif table_exists?
|
|
356
384
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
|
357
385
|
"#{super}(#{attr_list})"
|
|
@@ -366,14 +394,14 @@ module ActiveRecord
|
|
|
366
394
|
end
|
|
367
395
|
|
|
368
396
|
def predicate_builder # :nodoc:
|
|
369
|
-
@predicate_builder ||= PredicateBuilder.new(
|
|
397
|
+
@predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
|
|
370
398
|
end
|
|
371
399
|
|
|
372
400
|
def type_caster # :nodoc:
|
|
373
401
|
TypeCaster::Map.new(self)
|
|
374
402
|
end
|
|
375
403
|
|
|
376
|
-
def cached_find_by_statement(key, &block) # :nodoc:
|
|
404
|
+
def cached_find_by_statement(connection, key, &block) # :nodoc:
|
|
377
405
|
cache = @find_by_statement_cache[connection.prepared_statements]
|
|
378
406
|
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
|
379
407
|
end
|
|
@@ -411,26 +439,24 @@ module ActiveRecord
|
|
|
411
439
|
end
|
|
412
440
|
end
|
|
413
441
|
|
|
414
|
-
def table_metadata
|
|
415
|
-
TableMetadata.new(self, arel_table)
|
|
416
|
-
end
|
|
417
|
-
|
|
418
442
|
def cached_find_by(keys, values)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
443
|
+
with_connection do |connection|
|
|
444
|
+
statement = cached_find_by_statement(connection, keys) { |params|
|
|
445
|
+
wheres = keys.index_with do |key|
|
|
446
|
+
if key.is_a?(Array)
|
|
447
|
+
[key.map { params.bind }]
|
|
448
|
+
else
|
|
449
|
+
params.bind
|
|
450
|
+
end
|
|
425
451
|
end
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
}
|
|
452
|
+
where(wheres).limit(1)
|
|
453
|
+
}
|
|
429
454
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
455
|
+
statement.execute(values.flatten, connection).then do |r|
|
|
456
|
+
r.first
|
|
457
|
+
rescue TypeError
|
|
458
|
+
raise ActiveRecord::StatementInvalid
|
|
459
|
+
end
|
|
434
460
|
end
|
|
435
461
|
end
|
|
436
462
|
end
|
|
@@ -450,7 +476,7 @@ module ActiveRecord
|
|
|
450
476
|
init_internals
|
|
451
477
|
initialize_internals_callback
|
|
452
478
|
|
|
453
|
-
|
|
479
|
+
super
|
|
454
480
|
|
|
455
481
|
yield self if block_given?
|
|
456
482
|
_run_initialize_callbacks
|
|
@@ -523,12 +549,7 @@ module ActiveRecord
|
|
|
523
549
|
|
|
524
550
|
##
|
|
525
551
|
def initialize_dup(other) # :nodoc:
|
|
526
|
-
@attributes =
|
|
527
|
-
if self.class.composite_primary_key?
|
|
528
|
-
@primary_key.each { |key| @attributes.reset(key) }
|
|
529
|
-
else
|
|
530
|
-
@attributes.reset(@primary_key)
|
|
531
|
-
end
|
|
552
|
+
@attributes = init_attributes(other)
|
|
532
553
|
|
|
533
554
|
_run_initialize_callbacks
|
|
534
555
|
|
|
@@ -540,6 +561,18 @@ module ActiveRecord
|
|
|
540
561
|
super
|
|
541
562
|
end
|
|
542
563
|
|
|
564
|
+
def init_attributes(_) # :nodoc:
|
|
565
|
+
attrs = @attributes.deep_dup
|
|
566
|
+
|
|
567
|
+
if self.class.composite_primary_key?
|
|
568
|
+
@primary_key.each { |key| attrs.reset(key) }
|
|
569
|
+
else
|
|
570
|
+
attrs.reset(@primary_key)
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
attrs
|
|
574
|
+
end
|
|
575
|
+
|
|
543
576
|
# Populate +coder+ with attributes about this record that should be
|
|
544
577
|
# serialized. The structure of +coder+ defined in this method is
|
|
545
578
|
# guaranteed to match the structure of +coder+ passed to the #init_with
|
|
@@ -568,7 +601,7 @@ module ActiveRecord
|
|
|
568
601
|
#
|
|
569
602
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
570
603
|
# topic.slice(:title, :author_name)
|
|
571
|
-
# => { "title" => "Budget", "author_name" => "Jason" }
|
|
604
|
+
# # => { "title" => "Budget", "author_name" => "Jason" }
|
|
572
605
|
#
|
|
573
606
|
#--
|
|
574
607
|
# Implemented by ActiveModel::Access#slice.
|
|
@@ -582,7 +615,7 @@ module ActiveRecord
|
|
|
582
615
|
#
|
|
583
616
|
# topic = Topic.new(title: "Budget", author_name: "Jason")
|
|
584
617
|
# topic.values_at(:title, :author_name)
|
|
585
|
-
# => ["Budget", "Jason"]
|
|
618
|
+
# # => ["Budget", "Jason"]
|
|
586
619
|
#
|
|
587
620
|
#--
|
|
588
621
|
# Implemented by ActiveModel::Access#values_at.
|
|
@@ -609,7 +642,7 @@ module ActiveRecord
|
|
|
609
642
|
def hash
|
|
610
643
|
id = self.id
|
|
611
644
|
|
|
612
|
-
if primary_key_values_present?
|
|
645
|
+
if self.class.composite_primary_key? ? primary_key_values_present? : id
|
|
613
646
|
self.class.hash ^ id.hash
|
|
614
647
|
else
|
|
615
648
|
super
|
|
@@ -659,12 +692,14 @@ module ActiveRecord
|
|
|
659
692
|
# Sets the record to strict_loading mode. This will raise an error
|
|
660
693
|
# if the record tries to lazily load an association.
|
|
661
694
|
#
|
|
695
|
+
# NOTE: Strict loading is disabled during validation in order to let the record validate its association.
|
|
696
|
+
#
|
|
662
697
|
# user = User.first
|
|
663
698
|
# user.strict_loading! # => true
|
|
664
699
|
# user.address.city
|
|
665
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
700
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
666
701
|
# user.comments.to_a
|
|
667
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
702
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
668
703
|
#
|
|
669
704
|
# ==== Parameters
|
|
670
705
|
#
|
|
@@ -684,7 +719,7 @@ module ActiveRecord
|
|
|
684
719
|
# user.address.city # => "Tatooine"
|
|
685
720
|
# user.comments.to_a # => [#<Comment:0x00...]
|
|
686
721
|
# user.comments.first.ratings.to_a
|
|
687
|
-
# => ActiveRecord::StrictLoadingViolationError
|
|
722
|
+
# # => ActiveRecord::StrictLoadingViolationError
|
|
688
723
|
def strict_loading!(value = true, mode: :all)
|
|
689
724
|
unless [:all, :n_plus_one_only].include?(mode)
|
|
690
725
|
raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only] but #{mode.inspect} was provided."
|
|
@@ -706,11 +741,29 @@ module ActiveRecord
|
|
|
706
741
|
@strict_loading_mode == :all
|
|
707
742
|
end
|
|
708
743
|
|
|
709
|
-
#
|
|
744
|
+
# Prevents records from being written to the database:
|
|
745
|
+
#
|
|
746
|
+
# customer = Customer.new
|
|
747
|
+
# customer.readonly!
|
|
748
|
+
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
|
749
|
+
#
|
|
750
|
+
# customer = Customer.first
|
|
751
|
+
# customer.readonly!
|
|
752
|
+
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
|
753
|
+
#
|
|
754
|
+
# Read-only records cannot be deleted from the database either:
|
|
710
755
|
#
|
|
711
756
|
# customer = Customer.first
|
|
712
757
|
# customer.readonly!
|
|
713
|
-
# customer.
|
|
758
|
+
# customer.destroy # raises ActiveRecord::ReadOnlyRecord
|
|
759
|
+
#
|
|
760
|
+
# Please, note that the objects themselves are still mutable in memory:
|
|
761
|
+
#
|
|
762
|
+
# customer = Customer.new
|
|
763
|
+
# customer.readonly!
|
|
764
|
+
# customer.name = 'New Name' # OK
|
|
765
|
+
#
|
|
766
|
+
# but you won't be able to persist the changes.
|
|
714
767
|
def readonly!
|
|
715
768
|
@readonly = true
|
|
716
769
|
end
|
|
@@ -719,21 +772,28 @@ module ActiveRecord
|
|
|
719
772
|
self.class.connection_handler
|
|
720
773
|
end
|
|
721
774
|
|
|
722
|
-
# Returns the
|
|
775
|
+
# Returns the attributes of the record as a nicely formatted string.
|
|
776
|
+
#
|
|
777
|
+
# Post.first.inspect
|
|
778
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
779
|
+
#
|
|
780
|
+
# The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
|
|
781
|
+
#
|
|
782
|
+
# Post.attributes_for_inspect = [:id, :title]
|
|
783
|
+
# Post.first.inspect
|
|
784
|
+
# #=> "#<Post id: 1, title: "Hello, World!">"
|
|
723
785
|
def inspect
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
inspection = if defined?(@attributes) && @attributes
|
|
727
|
-
attribute_names.filter_map do |name|
|
|
728
|
-
if _has_attribute?(name)
|
|
729
|
-
"#{name}: #{attribute_for_inspect(name)}"
|
|
730
|
-
end
|
|
731
|
-
end.join(", ")
|
|
732
|
-
else
|
|
733
|
-
"not initialized"
|
|
734
|
-
end
|
|
786
|
+
inspect_with_attributes(attributes_for_inspect)
|
|
787
|
+
end
|
|
735
788
|
|
|
736
|
-
|
|
789
|
+
# Returns all attributes of the record as a nicely formatted string,
|
|
790
|
+
# ignoring <tt>.attributes_for_inspect</tt>.
|
|
791
|
+
#
|
|
792
|
+
# Post.first.full_inspect
|
|
793
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
|
794
|
+
#
|
|
795
|
+
def full_inspect
|
|
796
|
+
inspect_with_attributes(all_attributes_for_inspect)
|
|
737
797
|
end
|
|
738
798
|
|
|
739
799
|
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
|
@@ -741,17 +801,17 @@ module ActiveRecord
|
|
|
741
801
|
def pretty_print(pp)
|
|
742
802
|
return super if custom_inspect_method_defined?
|
|
743
803
|
pp.object_address_group(self) do
|
|
744
|
-
if
|
|
745
|
-
attr_names =
|
|
804
|
+
if @attributes
|
|
805
|
+
attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
|
|
746
806
|
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
|
807
|
+
attr_name = attr_name.to_s
|
|
747
808
|
pp.breakable " "
|
|
748
809
|
pp.group(1) do
|
|
749
810
|
pp.text attr_name
|
|
750
811
|
pp.text ":"
|
|
751
812
|
pp.breakable
|
|
752
|
-
value =
|
|
753
|
-
|
|
754
|
-
pp.pp value
|
|
813
|
+
value = attribute_for_inspect(attr_name)
|
|
814
|
+
pp.text value
|
|
755
815
|
end
|
|
756
816
|
end
|
|
757
817
|
else
|
|
@@ -786,10 +846,9 @@ module ActiveRecord
|
|
|
786
846
|
|
|
787
847
|
@primary_key = klass.primary_key
|
|
788
848
|
@strict_loading = klass.strict_loading_by_default
|
|
789
|
-
@strict_loading_mode =
|
|
849
|
+
@strict_loading_mode = klass.strict_loading_mode
|
|
790
850
|
|
|
791
851
|
klass.define_attribute_methods
|
|
792
|
-
klass.generate_alias_attributes
|
|
793
852
|
end
|
|
794
853
|
|
|
795
854
|
def initialize_internals_callback
|
|
@@ -809,5 +868,30 @@ module ActiveRecord
|
|
|
809
868
|
def inspection_filter
|
|
810
869
|
self.class.inspection_filter
|
|
811
870
|
end
|
|
871
|
+
|
|
872
|
+
def inspect_with_attributes(attributes_to_list)
|
|
873
|
+
inspection = if @attributes
|
|
874
|
+
attributes_to_list.filter_map do |name|
|
|
875
|
+
name = name.to_s
|
|
876
|
+
if _has_attribute?(name)
|
|
877
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
|
878
|
+
end
|
|
879
|
+
end.join(", ")
|
|
880
|
+
else
|
|
881
|
+
"not initialized"
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
"#<#{self.class} #{inspection}>"
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
def attributes_for_inspect
|
|
888
|
+
self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
def all_attributes_for_inspect
|
|
892
|
+
return [] unless @attributes
|
|
893
|
+
|
|
894
|
+
attribute_names
|
|
895
|
+
end
|
|
812
896
|
end
|
|
813
897
|
end
|
|
@@ -7,6 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
|
|
8
8
|
included do
|
|
9
9
|
class_attribute :_counter_cache_columns, instance_accessor: false, default: []
|
|
10
|
+
class_attribute :counter_cached_association_names, instance_writer: false, default: []
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
module ClassMethods
|
|
@@ -16,7 +17,7 @@ module ActiveRecord
|
|
|
16
17
|
#
|
|
17
18
|
# ==== Parameters
|
|
18
19
|
#
|
|
19
|
-
# * +id+ - The id of the object you wish to reset a counter on.
|
|
20
|
+
# * +id+ - The id of the object you wish to reset a counter on or an array of ids.
|
|
20
21
|
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
|
21
22
|
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
|
22
23
|
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
|
@@ -27,13 +28,25 @@ module ActiveRecord
|
|
|
27
28
|
# # For the Post with id #1, reset the comments_count
|
|
28
29
|
# Post.reset_counters(1, :comments)
|
|
29
30
|
#
|
|
30
|
-
# #
|
|
31
|
+
# # For posts with ids #1 and #2, reset the comments_count
|
|
32
|
+
# Post.reset_counters([1, 2], :comments)
|
|
33
|
+
#
|
|
34
|
+
# # Like above, but also touch the updated_at and/or updated_on
|
|
31
35
|
# # attributes.
|
|
32
36
|
# Post.reset_counters(1, :comments, touch: true)
|
|
33
37
|
def reset_counters(id, *counters, touch: nil)
|
|
34
|
-
|
|
38
|
+
ids = if composite_primary_key?
|
|
39
|
+
if id.first.is_a?(Array)
|
|
40
|
+
id
|
|
41
|
+
else
|
|
42
|
+
[id]
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
Array(id)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
updates = Hash.new { |h, k| h[k] = {} }
|
|
35
49
|
|
|
36
|
-
updates = {}
|
|
37
50
|
counters.each do |counter_association|
|
|
38
51
|
has_many_association = _reflect_on_association(counter_association)
|
|
39
52
|
unless has_many_association
|
|
@@ -47,14 +60,22 @@ module ActiveRecord
|
|
|
47
60
|
has_many_association = has_many_association.through_reflection
|
|
48
61
|
end
|
|
49
62
|
|
|
63
|
+
counter_association = counter_association.to_sym
|
|
50
64
|
foreign_key = has_many_association.foreign_key.to_s
|
|
51
65
|
child_class = has_many_association.klass
|
|
52
66
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
|
53
67
|
counter_name = reflection.counter_cache_column
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
counts =
|
|
70
|
+
unscoped
|
|
71
|
+
.joins(counter_association)
|
|
72
|
+
.where(primary_key => ids)
|
|
73
|
+
.group(primary_key)
|
|
74
|
+
.count(:all)
|
|
75
|
+
|
|
76
|
+
ids.each do |id|
|
|
77
|
+
updates[id].merge!(counter_name => counts[id] || 0)
|
|
78
|
+
end
|
|
58
79
|
end
|
|
59
80
|
|
|
60
81
|
if touch
|
|
@@ -62,10 +83,15 @@ module ActiveRecord
|
|
|
62
83
|
names = Array.wrap(names)
|
|
63
84
|
options = names.extract_options!
|
|
64
85
|
touch_updates = touch_attributes_with_time(*names, **options)
|
|
65
|
-
|
|
86
|
+
|
|
87
|
+
updates.each_value do |record_updates|
|
|
88
|
+
record_updates.merge!(touch_updates)
|
|
89
|
+
end
|
|
66
90
|
end
|
|
67
91
|
|
|
68
|
-
|
|
92
|
+
updates.each do |id, record_updates|
|
|
93
|
+
unscoped.where(primary_key => [id]).update_all(record_updates)
|
|
94
|
+
end
|
|
69
95
|
|
|
70
96
|
true
|
|
71
97
|
end
|
|
@@ -181,14 +207,26 @@ module ActiveRecord
|
|
|
181
207
|
def counter_cache_column?(name) # :nodoc:
|
|
182
208
|
_counter_cache_columns.include?(name)
|
|
183
209
|
end
|
|
210
|
+
|
|
211
|
+
def load_schema! # :nodoc:
|
|
212
|
+
super
|
|
213
|
+
|
|
214
|
+
association_names = _reflections.filter_map do |name, reflection|
|
|
215
|
+
next unless reflection.belongs_to? && reflection.counter_cache_column
|
|
216
|
+
|
|
217
|
+
name.to_sym
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
self.counter_cached_association_names |= association_names
|
|
221
|
+
end
|
|
184
222
|
end
|
|
185
223
|
|
|
186
224
|
private
|
|
187
225
|
def _create_record(attribute_names = self.attribute_names)
|
|
188
226
|
id = super
|
|
189
227
|
|
|
190
|
-
|
|
191
|
-
association.increment_counters
|
|
228
|
+
counter_cached_association_names.each do |association_name|
|
|
229
|
+
association(association_name).increment_counters
|
|
192
230
|
end
|
|
193
231
|
|
|
194
232
|
id
|
|
@@ -198,7 +236,9 @@ module ActiveRecord
|
|
|
198
236
|
affected_rows = super
|
|
199
237
|
|
|
200
238
|
if affected_rows > 0
|
|
201
|
-
|
|
239
|
+
counter_cached_association_names.each do |association_name|
|
|
240
|
+
association = association(association_name)
|
|
241
|
+
|
|
202
242
|
unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
|
|
203
243
|
association.decrement_counters
|
|
204
244
|
end
|
|
@@ -208,12 +248,6 @@ module ActiveRecord
|
|
|
208
248
|
affected_rows
|
|
209
249
|
end
|
|
210
250
|
|
|
211
|
-
def each_counter_cached_associations
|
|
212
|
-
_reflections.each do |name, reflection|
|
|
213
|
-
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
251
|
def _foreign_keys_equal?(fkey1, fkey2)
|
|
218
252
|
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
|
219
253
|
end
|
|
@@ -25,8 +25,7 @@ module ActiveRecord
|
|
|
25
25
|
def initialize(url)
|
|
26
26
|
raise "Database URL cannot be empty" if url.blank?
|
|
27
27
|
@uri = uri_parser.parse(url)
|
|
28
|
-
@adapter =
|
|
29
|
-
@adapter = "postgresql" if @adapter == "postgres"
|
|
28
|
+
@adapter = resolved_adapter
|
|
30
29
|
|
|
31
30
|
if @uri.opaque
|
|
32
31
|
@uri.opaque, @query = @uri.opaque.split("?", 2)
|
|
@@ -80,6 +79,14 @@ module ActiveRecord
|
|
|
80
79
|
end
|
|
81
80
|
end
|
|
82
81
|
|
|
82
|
+
def resolved_adapter
|
|
83
|
+
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
|
84
|
+
if adapter && ActiveRecord.protocol_adapters[adapter]
|
|
85
|
+
adapter = ActiveRecord.protocol_adapters[adapter]
|
|
86
|
+
end
|
|
87
|
+
adapter
|
|
88
|
+
end
|
|
89
|
+
|
|
83
90
|
# Returns name of the database.
|
|
84
91
|
def database_from_path
|
|
85
92
|
if @adapter == "sqlite3"
|
|
@@ -11,14 +11,25 @@ module ActiveRecord
|
|
|
11
11
|
def initialize(env_name, name)
|
|
12
12
|
@env_name = env_name
|
|
13
13
|
@name = name
|
|
14
|
+
@adapter_class = nil
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def
|
|
17
|
-
|
|
17
|
+
def adapter_class
|
|
18
|
+
@adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def
|
|
21
|
-
"
|
|
21
|
+
def inspect # :nodoc:
|
|
22
|
+
"#<#{self.class.name} env_name=#{@env_name} name=#{@name} adapter_class=#{adapter_class}>"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def new_connection
|
|
26
|
+
adapter_class.new(configuration_hash)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def validate!
|
|
30
|
+
adapter_class if adapter
|
|
31
|
+
|
|
32
|
+
true
|
|
22
33
|
end
|
|
23
34
|
|
|
24
35
|
def host
|
|
@@ -37,7 +48,11 @@ module ActiveRecord
|
|
|
37
48
|
raise NotImplementedError
|
|
38
49
|
end
|
|
39
50
|
|
|
40
|
-
def
|
|
51
|
+
def min_connections
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def max_connections
|
|
41
56
|
raise NotImplementedError
|
|
42
57
|
end
|
|
43
58
|
|
|
@@ -84,6 +99,14 @@ module ActiveRecord
|
|
|
84
99
|
def schema_cache_path
|
|
85
100
|
raise NotImplementedError
|
|
86
101
|
end
|
|
102
|
+
|
|
103
|
+
def use_metadata_table?
|
|
104
|
+
raise NotImplementedError
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def seeds?
|
|
108
|
+
raise NotImplementedError
|
|
109
|
+
end
|
|
87
110
|
end
|
|
88
111
|
end
|
|
89
112
|
end
|