activerecord 7.1.5.1 → 8.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- 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 +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- 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/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -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/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -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 +92 -295
- 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/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- 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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- 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 +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- 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 +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- 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 +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- 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 +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -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/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
data/lib/active_record/core.rb
CHANGED
@@ -73,7 +73,7 @@ module ActiveRecord
|
|
73
73
|
end
|
74
74
|
self.configurations = {}
|
75
75
|
|
76
|
-
# Returns fully resolved ActiveRecord::DatabaseConfigurations object
|
76
|
+
# Returns a fully resolved ActiveRecord::DatabaseConfigurations object.
|
77
77
|
def self.configurations
|
78
78
|
@@configurations
|
79
79
|
end
|
@@ -89,6 +89,7 @@ module ActiveRecord
|
|
89
89
|
class_attribute :belongs_to_required_by_default, instance_accessor: false
|
90
90
|
|
91
91
|
class_attribute :strict_loading_by_default, instance_accessor: false, default: false
|
92
|
+
class_attribute :strict_loading_mode, instance_accessor: false, default: :all
|
92
93
|
|
93
94
|
class_attribute :has_many_inversing, instance_accessor: false, default: false
|
94
95
|
|
@@ -102,6 +103,21 @@ module ActiveRecord
|
|
102
103
|
|
103
104
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
104
105
|
|
106
|
+
##
|
107
|
+
# :singleton-method:
|
108
|
+
#
|
109
|
+
# Specifies the attributes that will be included in the output of the
|
110
|
+
# #inspect method:
|
111
|
+
#
|
112
|
+
# Post.attributes_for_inspect = [:id, :title]
|
113
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"
|
114
|
+
#
|
115
|
+
# When set to `:all` inspect will list all the record's attributes:
|
116
|
+
#
|
117
|
+
# Post.attributes_for_inspect = :all
|
118
|
+
# Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
119
|
+
class_attribute :attributes_for_inspect, instance_accessor: false, default: :all
|
120
|
+
|
105
121
|
def self.application_record_class? # :nodoc:
|
106
122
|
if ActiveRecord.application_record_class
|
107
123
|
self == ActiveRecord.application_record_class
|
@@ -186,6 +202,17 @@ module ActiveRecord
|
|
186
202
|
false
|
187
203
|
end
|
188
204
|
|
205
|
+
# Intended to behave like `.current_preventing_writes` given the class name as input.
|
206
|
+
# See PoolConfig and ConnectionHandler::ConnectionDescriptor.
|
207
|
+
def self.preventing_writes?(class_name) # :nodoc:
|
208
|
+
connected_to_stack.reverse_each do |hash|
|
209
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
|
210
|
+
return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].any? { |klass| klass.name == class_name }
|
211
|
+
end
|
212
|
+
|
213
|
+
false
|
214
|
+
end
|
215
|
+
|
189
216
|
def self.connected_to_stack # :nodoc:
|
190
217
|
if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
|
191
218
|
connected_to_stack
|
@@ -350,8 +377,8 @@ module ActiveRecord
|
|
350
377
|
super
|
351
378
|
elsif abstract_class?
|
352
379
|
"#{super}(abstract)"
|
353
|
-
elsif !connected?
|
354
|
-
"#{super} (call '#{super}.
|
380
|
+
elsif !schema_loaded? && !connected?
|
381
|
+
"#{super} (call '#{super}.load_schema' to load schema informations)"
|
355
382
|
elsif table_exists?
|
356
383
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
357
384
|
"#{super}(#{attr_list})"
|
@@ -366,14 +393,14 @@ module ActiveRecord
|
|
366
393
|
end
|
367
394
|
|
368
395
|
def predicate_builder # :nodoc:
|
369
|
-
@predicate_builder ||= PredicateBuilder.new(
|
396
|
+
@predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
|
370
397
|
end
|
371
398
|
|
372
399
|
def type_caster # :nodoc:
|
373
400
|
TypeCaster::Map.new(self)
|
374
401
|
end
|
375
402
|
|
376
|
-
def cached_find_by_statement(key, &block) # :nodoc:
|
403
|
+
def cached_find_by_statement(connection, key, &block) # :nodoc:
|
377
404
|
cache = @find_by_statement_cache[connection.prepared_statements]
|
378
405
|
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
379
406
|
end
|
@@ -411,26 +438,24 @@ module ActiveRecord
|
|
411
438
|
end
|
412
439
|
end
|
413
440
|
|
414
|
-
def table_metadata
|
415
|
-
TableMetadata.new(self, arel_table)
|
416
|
-
end
|
417
|
-
|
418
441
|
def cached_find_by(keys, values)
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
442
|
+
with_connection do |connection|
|
443
|
+
statement = cached_find_by_statement(connection, keys) { |params|
|
444
|
+
wheres = keys.index_with do |key|
|
445
|
+
if key.is_a?(Array)
|
446
|
+
[key.map { params.bind }]
|
447
|
+
else
|
448
|
+
params.bind
|
449
|
+
end
|
425
450
|
end
|
426
|
-
|
427
|
-
|
428
|
-
}
|
451
|
+
where(wheres).limit(1)
|
452
|
+
}
|
429
453
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
454
|
+
statement.execute(values.flatten, connection, allow_retry: true).then do |r|
|
455
|
+
r.first
|
456
|
+
rescue TypeError
|
457
|
+
raise ActiveRecord::StatementInvalid
|
458
|
+
end
|
434
459
|
end
|
435
460
|
end
|
436
461
|
end
|
@@ -450,7 +475,7 @@ module ActiveRecord
|
|
450
475
|
init_internals
|
451
476
|
initialize_internals_callback
|
452
477
|
|
453
|
-
|
478
|
+
super
|
454
479
|
|
455
480
|
yield self if block_given?
|
456
481
|
_run_initialize_callbacks
|
@@ -523,12 +548,7 @@ module ActiveRecord
|
|
523
548
|
|
524
549
|
##
|
525
550
|
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
|
551
|
+
@attributes = init_attributes(other)
|
532
552
|
|
533
553
|
_run_initialize_callbacks
|
534
554
|
|
@@ -540,6 +560,18 @@ module ActiveRecord
|
|
540
560
|
super
|
541
561
|
end
|
542
562
|
|
563
|
+
def init_attributes(_) # :nodoc:
|
564
|
+
attrs = @attributes.deep_dup
|
565
|
+
|
566
|
+
if self.class.composite_primary_key?
|
567
|
+
@primary_key.each { |key| attrs.reset(key) }
|
568
|
+
else
|
569
|
+
attrs.reset(@primary_key)
|
570
|
+
end
|
571
|
+
|
572
|
+
attrs
|
573
|
+
end
|
574
|
+
|
543
575
|
# Populate +coder+ with attributes about this record that should be
|
544
576
|
# serialized. The structure of +coder+ defined in this method is
|
545
577
|
# guaranteed to match the structure of +coder+ passed to the #init_with
|
@@ -706,11 +738,29 @@ module ActiveRecord
|
|
706
738
|
@strict_loading_mode == :all
|
707
739
|
end
|
708
740
|
|
709
|
-
#
|
741
|
+
# Prevents records from being written to the database:
|
742
|
+
#
|
743
|
+
# customer = Customer.new
|
744
|
+
# customer.readonly!
|
745
|
+
# customer.save # raises ActiveRecord::ReadOnlyRecord
|
746
|
+
#
|
747
|
+
# customer = Customer.first
|
748
|
+
# customer.readonly!
|
749
|
+
# customer.update(name: 'New Name') # raises ActiveRecord::ReadOnlyRecord
|
750
|
+
#
|
751
|
+
# Read-only records cannot be deleted from the database either:
|
710
752
|
#
|
711
753
|
# customer = Customer.first
|
712
754
|
# customer.readonly!
|
713
|
-
# customer.
|
755
|
+
# customer.destroy # raises ActiveRecord::ReadOnlyRecord
|
756
|
+
#
|
757
|
+
# Please, note that the objects themselves are still mutable in memory:
|
758
|
+
#
|
759
|
+
# customer = Customer.new
|
760
|
+
# customer.readonly!
|
761
|
+
# customer.name = 'New Name' # OK
|
762
|
+
#
|
763
|
+
# but you won't be able to persist the changes.
|
714
764
|
def readonly!
|
715
765
|
@readonly = true
|
716
766
|
end
|
@@ -719,21 +769,28 @@ module ActiveRecord
|
|
719
769
|
self.class.connection_handler
|
720
770
|
end
|
721
771
|
|
722
|
-
# Returns the
|
772
|
+
# Returns the attributes of the record as a nicely formatted string.
|
773
|
+
#
|
774
|
+
# Post.first.inspect
|
775
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
776
|
+
#
|
777
|
+
# The attributes can be limited by setting <tt>.attributes_for_inspect</tt>.
|
778
|
+
#
|
779
|
+
# Post.attributes_for_inspect = [:id, :title]
|
780
|
+
# Post.first.inspect
|
781
|
+
# #=> "#<Post id: 1, title: "Hello, World!">"
|
723
782
|
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
|
783
|
+
inspect_with_attributes(attributes_for_inspect)
|
784
|
+
end
|
735
785
|
|
736
|
-
|
786
|
+
# Returns all attributes of the record as a nicely formatted string,
|
787
|
+
# ignoring <tt>.attributes_for_inspect</tt>.
|
788
|
+
#
|
789
|
+
# Post.first.full_inspect
|
790
|
+
# #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"
|
791
|
+
#
|
792
|
+
def full_inspect
|
793
|
+
inspect_with_attributes(all_attributes_for_inspect)
|
737
794
|
end
|
738
795
|
|
739
796
|
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
@@ -741,17 +798,17 @@ module ActiveRecord
|
|
741
798
|
def pretty_print(pp)
|
742
799
|
return super if custom_inspect_method_defined?
|
743
800
|
pp.object_address_group(self) do
|
744
|
-
if
|
745
|
-
attr_names =
|
801
|
+
if @attributes
|
802
|
+
attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
|
746
803
|
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
804
|
+
attr_name = attr_name.to_s
|
747
805
|
pp.breakable " "
|
748
806
|
pp.group(1) do
|
749
807
|
pp.text attr_name
|
750
808
|
pp.text ":"
|
751
809
|
pp.breakable
|
752
|
-
value =
|
753
|
-
|
754
|
-
pp.pp value
|
810
|
+
value = attribute_for_inspect(attr_name)
|
811
|
+
pp.text value
|
755
812
|
end
|
756
813
|
end
|
757
814
|
else
|
@@ -786,10 +843,9 @@ module ActiveRecord
|
|
786
843
|
|
787
844
|
@primary_key = klass.primary_key
|
788
845
|
@strict_loading = klass.strict_loading_by_default
|
789
|
-
@strict_loading_mode =
|
846
|
+
@strict_loading_mode = klass.strict_loading_mode
|
790
847
|
|
791
848
|
klass.define_attribute_methods
|
792
|
-
klass.generate_alias_attributes
|
793
849
|
end
|
794
850
|
|
795
851
|
def initialize_internals_callback
|
@@ -809,5 +865,30 @@ module ActiveRecord
|
|
809
865
|
def inspection_filter
|
810
866
|
self.class.inspection_filter
|
811
867
|
end
|
868
|
+
|
869
|
+
def inspect_with_attributes(attributes_to_list)
|
870
|
+
inspection = if @attributes
|
871
|
+
attributes_to_list.filter_map do |name|
|
872
|
+
name = name.to_s
|
873
|
+
if _has_attribute?(name)
|
874
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
875
|
+
end
|
876
|
+
end.join(", ")
|
877
|
+
else
|
878
|
+
"not initialized"
|
879
|
+
end
|
880
|
+
|
881
|
+
"#<#{self.class} #{inspection}>"
|
882
|
+
end
|
883
|
+
|
884
|
+
def attributes_for_inspect
|
885
|
+
self.class.attributes_for_inspect == :all ? all_attributes_for_inspect : self.class.attributes_for_inspect
|
886
|
+
end
|
887
|
+
|
888
|
+
def all_attributes_for_inspect
|
889
|
+
return [] unless @attributes
|
890
|
+
|
891
|
+
attribute_names
|
892
|
+
end
|
812
893
|
end
|
813
894
|
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
|
@@ -27,7 +28,7 @@ module ActiveRecord
|
|
27
28
|
# # For the Post with id #1, reset the comments_count
|
28
29
|
# Post.reset_counters(1, :comments)
|
29
30
|
#
|
30
|
-
# # Like above, but also touch the
|
31
|
+
# # Like above, but also touch the updated_at and/or updated_on
|
31
32
|
# # attributes.
|
32
33
|
# Post.reset_counters(1, :comments, touch: true)
|
33
34
|
def reset_counters(id, *counters, touch: nil)
|
@@ -181,14 +182,26 @@ module ActiveRecord
|
|
181
182
|
def counter_cache_column?(name) # :nodoc:
|
182
183
|
_counter_cache_columns.include?(name)
|
183
184
|
end
|
185
|
+
|
186
|
+
def load_schema! # :nodoc:
|
187
|
+
super
|
188
|
+
|
189
|
+
association_names = _reflections.filter_map do |name, reflection|
|
190
|
+
next unless reflection.belongs_to? && reflection.counter_cache_column
|
191
|
+
|
192
|
+
name.to_sym
|
193
|
+
end
|
194
|
+
|
195
|
+
self.counter_cached_association_names |= association_names
|
196
|
+
end
|
184
197
|
end
|
185
198
|
|
186
199
|
private
|
187
200
|
def _create_record(attribute_names = self.attribute_names)
|
188
201
|
id = super
|
189
202
|
|
190
|
-
|
191
|
-
association.increment_counters
|
203
|
+
counter_cached_association_names.each do |association_name|
|
204
|
+
association(association_name).increment_counters
|
192
205
|
end
|
193
206
|
|
194
207
|
id
|
@@ -198,7 +211,9 @@ module ActiveRecord
|
|
198
211
|
affected_rows = super
|
199
212
|
|
200
213
|
if affected_rows > 0
|
201
|
-
|
214
|
+
counter_cached_association_names.each do |association_name|
|
215
|
+
association = association(association_name)
|
216
|
+
|
202
217
|
unless destroyed_by_association && _foreign_keys_equal?(destroyed_by_association.foreign_key, association.reflection.foreign_key)
|
203
218
|
association.decrement_counters
|
204
219
|
end
|
@@ -208,12 +223,6 @@ module ActiveRecord
|
|
208
223
|
affected_rows
|
209
224
|
end
|
210
225
|
|
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
226
|
def _foreign_keys_equal?(fkey1, fkey2)
|
218
227
|
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
219
228
|
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
|
@@ -84,6 +95,14 @@ module ActiveRecord
|
|
84
95
|
def schema_cache_path
|
85
96
|
raise NotImplementedError
|
86
97
|
end
|
98
|
+
|
99
|
+
def use_metadata_table?
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
102
|
+
|
103
|
+
def seeds?
|
104
|
+
raise NotImplementedError
|
105
|
+
end
|
87
106
|
end
|
88
107
|
end
|
89
108
|
end
|
@@ -1,53 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
class DatabaseConfigurations
|
5
|
-
#
|
7
|
+
# # Active Record Database Hash Config
|
6
8
|
#
|
7
|
-
# A
|
8
|
-
#
|
9
|
+
# A `HashConfig` object is created for each database configuration entry that is
|
10
|
+
# created from a hash.
|
9
11
|
#
|
10
12
|
# A hash config:
|
11
13
|
#
|
12
|
-
#
|
14
|
+
# { "development" => { "database" => "db_name" } }
|
13
15
|
#
|
14
16
|
# Becomes:
|
15
17
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
+
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
|
19
|
+
# @env_name="development", @name="primary", @config={database: "db_name"}>
|
18
20
|
#
|
19
21
|
# See ActiveRecord::DatabaseConfigurations for more info.
|
20
22
|
class HashConfig < DatabaseConfig
|
21
23
|
attr_reader :configuration_hash
|
22
24
|
|
23
|
-
|
24
|
-
# Initialize a new +HashConfig+ object
|
25
|
+
# Initialize a new `HashConfig` object
|
25
26
|
#
|
26
|
-
#
|
27
|
+
# #### Parameters
|
28
|
+
#
|
29
|
+
# * `env_name` - The Rails environment, i.e. "development".
|
30
|
+
# * `name` - The db config name. In a standard two-tier database configuration
|
31
|
+
# this will default to "primary". In a multiple database three-tier database
|
32
|
+
# configuration this corresponds to the name used in the second tier, for
|
33
|
+
# example "primary_readonly".
|
34
|
+
# * `configuration_hash` - The config hash. This is the hash that contains the
|
35
|
+
# database adapter, name, and other important information for database
|
36
|
+
# connections.
|
27
37
|
#
|
28
|
-
# * <tt>:env_name</tt> - The \Rails environment, i.e. "development".
|
29
|
-
# * <tt>:name</tt> - The db config name. In a standard two-tier
|
30
|
-
# database configuration this will default to "primary". In a multiple
|
31
|
-
# database three-tier database configuration this corresponds to the name
|
32
|
-
# used in the second tier, for example "primary_readonly".
|
33
|
-
# * <tt>:config</tt> - The config hash. This is the hash that contains the
|
34
|
-
# database adapter, name, and other important information for database
|
35
|
-
# connections.
|
36
38
|
def initialize(env_name, name, configuration_hash)
|
37
39
|
super(env_name, name)
|
38
40
|
@configuration_hash = configuration_hash.symbolize_keys.freeze
|
39
41
|
end
|
40
42
|
|
41
43
|
# Determines whether a database configuration is for a replica / readonly
|
42
|
-
# connection. If the
|
43
|
-
# return
|
44
|
+
# connection. If the `replica` key is present in the config, `replica?` will
|
45
|
+
# return `true`.
|
44
46
|
def replica?
|
45
47
|
configuration_hash[:replica]
|
46
48
|
end
|
47
49
|
|
48
|
-
# The migrations paths for a database configuration. If the
|
49
|
-
#
|
50
|
-
# will return its value.
|
50
|
+
# The migrations paths for a database configuration. If the `migrations_paths`
|
51
|
+
# key is present in the config, `migrations_paths` will return its value.
|
51
52
|
def migrations_paths
|
52
53
|
configuration_hash[:migrations_paths]
|
53
54
|
end
|
@@ -92,8 +93,8 @@ module ActiveRecord
|
|
92
93
|
(configuration_hash[:checkout_timeout] || 5).to_f
|
93
94
|
end
|
94
95
|
|
95
|
-
#
|
96
|
-
# also be useful if someone wants a very low
|
96
|
+
# `reaping_frequency` is configurable mostly for historical reasons, but it
|
97
|
+
# could also be useful if someone wants a very low `idle_timeout`.
|
97
98
|
def reaping_frequency
|
98
99
|
configuration_hash.fetch(:reaping_frequency, 60)&.to_f
|
99
100
|
end
|
@@ -104,12 +105,11 @@ module ActiveRecord
|
|
104
105
|
end
|
105
106
|
|
106
107
|
def adapter
|
107
|
-
configuration_hash[:adapter]
|
108
|
+
configuration_hash[:adapter]&.to_s
|
108
109
|
end
|
109
110
|
|
110
|
-
# The path to the schema cache dump file for a database.
|
111
|
-
#
|
112
|
-
# default will be derived.
|
111
|
+
# The path to the schema cache dump file for a database. If omitted, the
|
112
|
+
# filename will be read from ENV or a default will be derived.
|
113
113
|
def schema_cache_path
|
114
114
|
configuration_hash[:schema_cache_path]
|
115
115
|
end
|
@@ -130,14 +130,22 @@ module ActiveRecord
|
|
130
130
|
Base.configurations.primary?(name)
|
131
131
|
end
|
132
132
|
|
133
|
-
# Determines whether
|
134
|
-
#
|
133
|
+
# Determines whether the db:prepare task should seed the database from db/seeds.rb.
|
134
|
+
#
|
135
|
+
# If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
|
136
|
+
# will return `true` for the primary database and `false` for all other configs.
|
137
|
+
def seeds?
|
138
|
+
configuration_hash.fetch(:seeds, primary?)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Determines whether to dump the schema/structure files and the filename that
|
142
|
+
# should be used.
|
135
143
|
#
|
136
|
-
# If
|
137
|
-
#
|
144
|
+
# If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
|
145
|
+
# will not be dumped.
|
138
146
|
#
|
139
|
-
# If the config option is set that will be used. Otherwise
|
140
|
-
#
|
147
|
+
# If the config option is set that will be used. Otherwise Rails will generate
|
148
|
+
# the filename from the database config name.
|
141
149
|
def schema_dump(format = ActiveRecord.schema_format)
|
142
150
|
if configuration_hash.key?(:schema_dump)
|
143
151
|
if config = configuration_hash[:schema_dump]
|
@@ -154,6 +162,10 @@ module ActiveRecord
|
|
154
162
|
!replica? && !!configuration_hash.fetch(:database_tasks, true)
|
155
163
|
end
|
156
164
|
|
165
|
+
def use_metadata_table? # :nodoc:
|
166
|
+
configuration_hash.fetch(:use_metadata_table, true)
|
167
|
+
end
|
168
|
+
|
157
169
|
private
|
158
170
|
def schema_file_type(format)
|
159
171
|
case format
|
@@ -41,10 +41,29 @@ module ActiveRecord
|
|
41
41
|
super(env_name, name, configuration_hash)
|
42
42
|
|
43
43
|
@url = url
|
44
|
-
@configuration_hash = @configuration_hash.merge(build_url_hash)
|
44
|
+
@configuration_hash = @configuration_hash.merge(build_url_hash)
|
45
|
+
|
46
|
+
if @configuration_hash[:schema_dump] == "false"
|
47
|
+
@configuration_hash[:schema_dump] = false
|
48
|
+
end
|
49
|
+
|
50
|
+
if @configuration_hash[:query_cache] == "false"
|
51
|
+
@configuration_hash[:query_cache] = false
|
52
|
+
end
|
53
|
+
|
54
|
+
to_boolean!(@configuration_hash, :replica)
|
55
|
+
to_boolean!(@configuration_hash, :database_tasks)
|
56
|
+
|
57
|
+
@configuration_hash.freeze
|
45
58
|
end
|
46
59
|
|
47
60
|
private
|
61
|
+
def to_boolean!(configuration_hash, key)
|
62
|
+
if configuration_hash[key].is_a?(String)
|
63
|
+
configuration_hash[key] = configuration_hash[key] != "false"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
48
67
|
# Return a Hash that can be merged into the main config that represents
|
49
68
|
# the passed in url
|
50
69
|
def build_url_hash
|