activerecord 7.1.3.3 → 7.2.0.beta1
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 +507 -2128
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- 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 +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- 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 +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- 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 +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- 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/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- 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 +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- 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 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- 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 +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -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 +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +70 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -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 +68 -0
- data/lib/active_record/transactions.rb +43 -14
- 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 +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- 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 +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- 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/tree_manager.rb +8 -3
- 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/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +16 -11
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
|
@@ -102,6 +102,9 @@ module ActiveRecord
|
|
102
102
|
|
103
103
|
class_attribute :shard_selector, instance_accessor: false, default: nil
|
104
104
|
|
105
|
+
# Specifies the attributes that will be included in the output of the #inspect method
|
106
|
+
class_attribute :attributes_for_inspect, instance_accessor: false, default: [:id]
|
107
|
+
|
105
108
|
def self.application_record_class? # :nodoc:
|
106
109
|
if ActiveRecord.application_record_class
|
107
110
|
self == ActiveRecord.application_record_class
|
@@ -351,7 +354,7 @@ module ActiveRecord
|
|
351
354
|
elsif abstract_class?
|
352
355
|
"#{super}(abstract)"
|
353
356
|
elsif !connected?
|
354
|
-
"#{super} (call '#{super}.
|
357
|
+
"#{super} (call '#{super}.lease_connection' to establish a connection)"
|
355
358
|
elsif table_exists?
|
356
359
|
attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ", "
|
357
360
|
"#{super}(#{attr_list})"
|
@@ -373,7 +376,7 @@ module ActiveRecord
|
|
373
376
|
TypeCaster::Map.new(self)
|
374
377
|
end
|
375
378
|
|
376
|
-
def cached_find_by_statement(key, &block) # :nodoc:
|
379
|
+
def cached_find_by_statement(connection, key, &block) # :nodoc:
|
377
380
|
cache = @find_by_statement_cache[connection.prepared_statements]
|
378
381
|
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
|
379
382
|
end
|
@@ -396,8 +399,8 @@ module ActiveRecord
|
|
396
399
|
@arel_table = nil
|
397
400
|
@predicate_builder = nil
|
398
401
|
@inspection_filter = nil
|
399
|
-
@filter_attributes
|
400
|
-
@generated_association_methods
|
402
|
+
@filter_attributes ||= nil
|
403
|
+
@generated_association_methods ||= nil
|
401
404
|
end
|
402
405
|
end
|
403
406
|
|
@@ -416,21 +419,23 @@ module ActiveRecord
|
|
416
419
|
end
|
417
420
|
|
418
421
|
def cached_find_by(keys, values)
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
422
|
+
with_connection do |connection|
|
423
|
+
statement = cached_find_by_statement(connection, keys) { |params|
|
424
|
+
wheres = keys.index_with do |key|
|
425
|
+
if key.is_a?(Array)
|
426
|
+
[key.map { params.bind }]
|
427
|
+
else
|
428
|
+
params.bind
|
429
|
+
end
|
425
430
|
end
|
426
|
-
|
427
|
-
|
428
|
-
}
|
431
|
+
where(wheres).limit(1)
|
432
|
+
}
|
429
433
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
+
begin
|
435
|
+
statement.execute(values.flatten, connection, allow_retry: true).first
|
436
|
+
rescue TypeError
|
437
|
+
raise ActiveRecord::StatementInvalid
|
438
|
+
end
|
434
439
|
end
|
435
440
|
end
|
436
441
|
end
|
@@ -450,7 +455,7 @@ module ActiveRecord
|
|
450
455
|
init_internals
|
451
456
|
initialize_internals_callback
|
452
457
|
|
453
|
-
|
458
|
+
super
|
454
459
|
|
455
460
|
yield self if block_given?
|
456
461
|
_run_initialize_callbacks
|
@@ -701,6 +706,11 @@ module ActiveRecord
|
|
701
706
|
@strict_loading_mode == :n_plus_one_only
|
702
707
|
end
|
703
708
|
|
709
|
+
# Returns +true+ if the record uses strict_loading with +:all+ mode enabled.
|
710
|
+
def strict_loading_all?
|
711
|
+
@strict_loading_mode == :all
|
712
|
+
end
|
713
|
+
|
704
714
|
# Marks this record as read only.
|
705
715
|
#
|
706
716
|
# customer = Customer.first
|
@@ -714,21 +724,14 @@ module ActiveRecord
|
|
714
724
|
self.class.connection_handler
|
715
725
|
end
|
716
726
|
|
717
|
-
# Returns the
|
727
|
+
# Returns the attributes specified by <tt>.attributes_for_inspect</tt> as a nicely formatted string.
|
718
728
|
def inspect
|
719
|
-
|
720
|
-
|
721
|
-
inspection = if defined?(@attributes) && @attributes
|
722
|
-
attribute_names.filter_map do |name|
|
723
|
-
if _has_attribute?(name)
|
724
|
-
"#{name}: #{attribute_for_inspect(name)}"
|
725
|
-
end
|
726
|
-
end.join(", ")
|
727
|
-
else
|
728
|
-
"not initialized"
|
729
|
-
end
|
729
|
+
inspect_with_attributes(attributes_for_inspect)
|
730
|
+
end
|
730
731
|
|
731
|
-
|
732
|
+
# Returns the full contents of the record as a nicely formatted string.
|
733
|
+
def full_inspect
|
734
|
+
inspect_with_attributes(attribute_names)
|
732
735
|
end
|
733
736
|
|
734
737
|
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from <tt>pp record</tt>
|
@@ -736,17 +739,17 @@ module ActiveRecord
|
|
736
739
|
def pretty_print(pp)
|
737
740
|
return super if custom_inspect_method_defined?
|
738
741
|
pp.object_address_group(self) do
|
739
|
-
if
|
740
|
-
attr_names =
|
742
|
+
if @attributes
|
743
|
+
attr_names = attributes_for_inspect.select { |name| _has_attribute?(name.to_s) }
|
741
744
|
pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
|
745
|
+
attr_name = attr_name.to_s
|
742
746
|
pp.breakable " "
|
743
747
|
pp.group(1) do
|
744
748
|
pp.text attr_name
|
745
749
|
pp.text ":"
|
746
750
|
pp.breakable
|
747
|
-
value =
|
748
|
-
|
749
|
-
pp.pp value
|
751
|
+
value = attribute_for_inspect(attr_name)
|
752
|
+
pp.text value
|
750
753
|
end
|
751
754
|
end
|
752
755
|
else
|
@@ -784,7 +787,6 @@ module ActiveRecord
|
|
784
787
|
@strict_loading_mode = :all
|
785
788
|
|
786
789
|
klass.define_attribute_methods
|
787
|
-
klass.generate_alias_attributes
|
788
790
|
end
|
789
791
|
|
790
792
|
def initialize_internals_callback
|
@@ -804,5 +806,24 @@ module ActiveRecord
|
|
804
806
|
def inspection_filter
|
805
807
|
self.class.inspection_filter
|
806
808
|
end
|
809
|
+
|
810
|
+
def inspect_with_attributes(attributes_to_list)
|
811
|
+
inspection = if @attributes
|
812
|
+
attributes_to_list.filter_map do |name|
|
813
|
+
name = name.to_s
|
814
|
+
if _has_attribute?(name)
|
815
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
816
|
+
end
|
817
|
+
end.join(", ")
|
818
|
+
else
|
819
|
+
"not initialized"
|
820
|
+
end
|
821
|
+
|
822
|
+
"#<#{self.class} #{inspection}>"
|
823
|
+
end
|
824
|
+
|
825
|
+
def attributes_for_inspect
|
826
|
+
self.class.attributes_for_inspect == :all ? attribute_names : self.class.attributes_for_inspect
|
827
|
+
end
|
807
828
|
end
|
808
829
|
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
|
@@ -65,7 +66,7 @@ module ActiveRecord
|
|
65
66
|
updates.merge!(touch_updates)
|
66
67
|
end
|
67
68
|
|
68
|
-
unscoped.where(primary_key => object.id).update_all(updates) if updates.any?
|
69
|
+
unscoped.where(primary_key => [object.id]).update_all(updates) if updates.any?
|
69
70
|
|
70
71
|
true
|
71
72
|
end
|
@@ -112,6 +113,7 @@ module ActiveRecord
|
|
112
113
|
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
113
114
|
# # WHERE id IN (10, 15)
|
114
115
|
def update_counters(id, counters)
|
116
|
+
id = [id] if composite_primary_key? && id.is_a?(Array) && !id[0].is_a?(Array)
|
115
117
|
unscoped.where!(primary_key => id).update_counters(counters)
|
116
118
|
end
|
117
119
|
|
@@ -180,14 +182,26 @@ module ActiveRecord
|
|
180
182
|
def counter_cache_column?(name) # :nodoc:
|
181
183
|
_counter_cache_columns.include?(name)
|
182
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
|
183
197
|
end
|
184
198
|
|
185
199
|
private
|
186
200
|
def _create_record(attribute_names = self.attribute_names)
|
187
201
|
id = super
|
188
202
|
|
189
|
-
|
190
|
-
association.increment_counters
|
203
|
+
counter_cached_association_names.each do |association_name|
|
204
|
+
association(association_name).increment_counters
|
191
205
|
end
|
192
206
|
|
193
207
|
id
|
@@ -197,9 +211,10 @@ module ActiveRecord
|
|
197
211
|
affected_rows = super
|
198
212
|
|
199
213
|
if affected_rows > 0
|
200
|
-
|
201
|
-
|
202
|
-
|
214
|
+
counter_cached_association_names.each do |association_name|
|
215
|
+
association = association(association_name)
|
216
|
+
|
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
|
205
220
|
end
|
@@ -208,10 +223,8 @@ module ActiveRecord
|
|
208
223
|
affected_rows
|
209
224
|
end
|
210
225
|
|
211
|
-
def
|
212
|
-
|
213
|
-
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
214
|
-
end
|
226
|
+
def _foreign_keys_equal?(fkey1, fkey2)
|
227
|
+
fkey1 == fkey2 || Array(fkey1).map(&:to_sym) == Array(fkey2).map(&:to_sym)
|
215
228
|
end
|
216
229
|
end
|
217
230
|
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,12 @@ module ActiveRecord
|
|
80
79
|
end
|
81
80
|
end
|
82
81
|
|
82
|
+
def resolved_adapter
|
83
|
+
adapter = uri.scheme && @uri.scheme.tr("-", "_")
|
84
|
+
adapter = ActiveRecord.protocol_adapters[adapter] || adapter
|
85
|
+
adapter
|
86
|
+
end
|
87
|
+
|
83
88
|
# Returns name of the database.
|
84
89
|
def database_from_path
|
85
90
|
if @adapter == "sqlite3"
|
@@ -11,14 +11,21 @@ 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 new_connection
|
22
|
+
adapter_class.new(configuration_hash)
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate!
|
26
|
+
adapter_class if adapter
|
27
|
+
|
28
|
+
true
|
22
29
|
end
|
23
30
|
|
24
31
|
def host
|
@@ -84,6 +91,10 @@ module ActiveRecord
|
|
84
91
|
def schema_cache_path
|
85
92
|
raise NotImplementedError
|
86
93
|
end
|
94
|
+
|
95
|
+
def use_metadata_table?
|
96
|
+
raise NotImplementedError
|
97
|
+
end
|
87
98
|
end
|
88
99
|
end
|
89
100
|
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,18 +105,21 @@ 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
|
116
116
|
|
117
|
-
def default_schema_cache_path
|
118
|
-
|
117
|
+
def default_schema_cache_path(db_dir = "db")
|
118
|
+
if primary?
|
119
|
+
File.join(db_dir, "schema_cache.yml")
|
120
|
+
else
|
121
|
+
File.join(db_dir, "#{name}_schema_cache.yml")
|
122
|
+
end
|
119
123
|
end
|
120
124
|
|
121
125
|
def lazy_schema_cache_path
|
@@ -126,14 +130,14 @@ module ActiveRecord
|
|
126
130
|
Base.configurations.primary?(name)
|
127
131
|
end
|
128
132
|
|
129
|
-
# Determines whether to dump the schema/structure files and the
|
130
|
-
#
|
133
|
+
# Determines whether to dump the schema/structure files and the filename that
|
134
|
+
# should be used.
|
131
135
|
#
|
132
|
-
# If
|
133
|
-
#
|
136
|
+
# If `configuration_hash[:schema_dump]` is set to `false` or `nil` the schema
|
137
|
+
# will not be dumped.
|
134
138
|
#
|
135
|
-
# If the config option is set that will be used. Otherwise
|
136
|
-
#
|
139
|
+
# If the config option is set that will be used. Otherwise Rails will generate
|
140
|
+
# the filename from the database config name.
|
137
141
|
def schema_dump(format = ActiveRecord.schema_format)
|
138
142
|
if configuration_hash.key?(:schema_dump)
|
139
143
|
if config = configuration_hash[:schema_dump]
|
@@ -150,6 +154,10 @@ module ActiveRecord
|
|
150
154
|
!replica? && !!configuration_hash.fetch(:database_tasks, true)
|
151
155
|
end
|
152
156
|
|
157
|
+
def use_metadata_table? # :nodoc:
|
158
|
+
configuration_hash.fetch(:use_metadata_table, true)
|
159
|
+
end
|
160
|
+
|
153
161
|
private
|
154
162
|
def schema_file_type(format)
|
155
163
|
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
|
@@ -36,7 +36,7 @@ module ActiveRecord
|
|
36
36
|
#
|
37
37
|
# Let's look at that entry/message/comment example using delegated types:
|
38
38
|
#
|
39
|
-
# # Schema: entries[ id, account_id, creator_id,
|
39
|
+
# # Schema: entries[ id, account_id, creator_id, entryable_type, entryable_id, created_at, updated_at ]
|
40
40
|
# class Entry < ApplicationRecord
|
41
41
|
# belongs_to :account
|
42
42
|
# belongs_to :creator
|
@@ -51,12 +51,12 @@ module ActiveRecord
|
|
51
51
|
# end
|
52
52
|
# end
|
53
53
|
#
|
54
|
-
# # Schema: messages[ id, subject, body ]
|
54
|
+
# # Schema: messages[ id, subject, body, created_at, updated_at ]
|
55
55
|
# class Message < ApplicationRecord
|
56
56
|
# include Entryable
|
57
57
|
# end
|
58
58
|
#
|
59
|
-
# # Schema: comments[ id, content ]
|
59
|
+
# # Schema: comments[ id, content, created_at, updated_at ]
|
60
60
|
# class Comment < ApplicationRecord
|
61
61
|
# include Entryable
|
62
62
|
# end
|
@@ -102,17 +102,37 @@ module ActiveRecord
|
|
102
102
|
# You create a new record that uses delegated typing by creating the delegator and delegatee at the same time,
|
103
103
|
# like so:
|
104
104
|
#
|
105
|
-
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user
|
105
|
+
# Entry.create! entryable: Comment.new(content: "Hello!"), creator: Current.user, account: Current.account
|
106
106
|
#
|
107
107
|
# If you need more complicated composition, or you need to perform dependent validation, you should build a factory
|
108
108
|
# method or class to take care of the complicated needs. This could be as simple as:
|
109
109
|
#
|
110
110
|
# class Entry < ApplicationRecord
|
111
|
-
# def self.create_with_comment(content, creator: Current.user)
|
112
|
-
# create! entryable: Comment.new(content: content), creator: creator
|
111
|
+
# def self.create_with_comment(content, creator: Current.user, account: Current.account)
|
112
|
+
# create! entryable: Comment.new(content: content), creator: creator, account: account
|
113
113
|
# end
|
114
114
|
# end
|
115
115
|
#
|
116
|
+
# == Querying across records
|
117
|
+
#
|
118
|
+
# A consequence of delegated types is that querying attributes spread across multiple classes becomes slightly more
|
119
|
+
# tricky, but not impossible.
|
120
|
+
#
|
121
|
+
# The simplest method is to join the "superclass" to the "subclass" and apply the query parameters (i.e. <tt>#where</tt>)
|
122
|
+
# in appropriate places:
|
123
|
+
#
|
124
|
+
# Comment.joins(:entry).where(comments: { content: 'Hello!' }, entry: { creator: Current.user } )
|
125
|
+
#
|
126
|
+
# For convenience, add a scope on the concern. Now all classes that implement the concern will automatically include
|
127
|
+
# the method:
|
128
|
+
#
|
129
|
+
# # app/models/concerns/entryable.rb
|
130
|
+
# scope :with_entry, ->(attrs) { joins(:entry).where(entry: attrs) }
|
131
|
+
#
|
132
|
+
# Now the query can be shortened significantly:
|
133
|
+
#
|
134
|
+
# Comment.where(content: 'Hello!').with_entry(creator: Current.user)
|
135
|
+
#
|
116
136
|
# == Adding further delegation
|
117
137
|
#
|
118
138
|
# The delegated type shouldn't just answer the question of what the underlying class is called. In fact, that's
|
@@ -219,6 +239,10 @@ module ActiveRecord
|
|
219
239
|
role_type = options[:foreign_type] || "#{role}_type"
|
220
240
|
role_id = options[:foreign_key] || "#{role}_id"
|
221
241
|
|
242
|
+
define_singleton_method "#{role}_types" do
|
243
|
+
types.map(&:to_s)
|
244
|
+
end
|
245
|
+
|
222
246
|
define_method "#{role}_class" do
|
223
247
|
public_send(role_type).constantize
|
224
248
|
end
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
)
|
20
20
|
association_model = association_class.constantize
|
21
21
|
owner_class = owner_model_name.constantize
|
22
|
-
owner = owner_class.find_by(owner_class.primary_key
|
22
|
+
owner = owner_class.find_by(owner_class.primary_key => [owner_id])
|
23
23
|
|
24
24
|
if !owner_destroyed?(owner, ensuring_owner_was_method)
|
25
25
|
raise DestroyAssociationAsyncError, "owner record not destroyed"
|
@@ -12,12 +12,12 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def method_missing(name,
|
15
|
+
def method_missing(name, ...)
|
16
16
|
match = Method.match(self, name)
|
17
17
|
|
18
18
|
if match && match.valid?
|
19
19
|
match.define
|
20
|
-
send(name,
|
20
|
+
send(name, ...)
|
21
21
|
else
|
22
22
|
super
|
23
23
|
end
|
@@ -84,7 +84,7 @@ module ActiveRecord
|
|
84
84
|
def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
85
85
|
encrypted_attributes << name.to_sym
|
86
86
|
|
87
|
-
|
87
|
+
decorate_attributes([name]) do |name, cast_type|
|
88
88
|
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
|
89
89
|
downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
|
90
90
|
|
@@ -221,7 +221,7 @@ module ActiveRecord
|
|
221
221
|
end
|
222
222
|
|
223
223
|
def cant_modify_encrypted_attributes_when_frozen
|
224
|
-
self.class
|
224
|
+
self.class.encrypted_attributes.each do |attribute|
|
225
225
|
errors.add(attribute.to_sym, "can't be modified because it is encrypted") if changed_attributes.include?(attribute)
|
226
226
|
end
|
227
227
|
end
|