activerecord 6.0.0.beta1 → 6.0.1.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +529 -10
- data/README.rdoc +3 -1
- data/lib/active_record.rb +7 -1
- data/lib/active_record/association_relation.rb +15 -6
- data/lib/active_record/associations.rb +4 -3
- data/lib/active_record/associations/association.rb +27 -2
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +5 -2
- data/lib/active_record/associations/builder/collection_association.rb +5 -15
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +5 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -42
- data/lib/active_record/associations/has_many_association.rb +1 -9
- data/lib/active_record/associations/has_many_through_association.rb +4 -11
- data/lib/active_record/associations/join_dependency.rb +14 -9
- data/lib/active_record/associations/join_dependency/join_association.rb +21 -7
- data/lib/active_record/associations/preloader.rb +12 -7
- data/lib/active_record/associations/preloader/association.rb +37 -34
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/attribute_methods.rb +3 -53
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +47 -14
- data/lib/active_record/attribute_methods/primary_key.rb +7 -15
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +3 -9
- data/lib/active_record/attribute_methods/write.rb +6 -12
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +21 -7
- data/lib/active_record/base.rb +0 -1
- data/lib/active_record/callbacks.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +134 -23
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +8 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +105 -70
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +63 -6
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +51 -40
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +95 -30
- data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
- data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -35
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +106 -138
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +3 -3
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +48 -8
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +66 -5
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +18 -5
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -30
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -3
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +98 -89
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +47 -63
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +95 -24
- data/lib/active_record/connection_adapters/schema_cache.rb +32 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +38 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +73 -118
- data/lib/active_record/connection_handling.rb +40 -17
- data/lib/active_record/core.rb +35 -24
- data/lib/active_record/database_configurations.rb +99 -50
- data/lib/active_record/database_configurations/hash_config.rb +11 -11
- data/lib/active_record/database_configurations/url_config.rb +21 -16
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +15 -0
- data/lib/active_record/errors.rb +18 -13
- data/lib/active_record/fixtures.rb +11 -6
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +179 -0
- data/lib/active_record/integration.rb +13 -1
- data/lib/active_record/internal_metadata.rb +5 -1
- data/lib/active_record/locking/optimistic.rb +3 -4
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +62 -44
- data/lib/active_record/migration/command_recorder.rb +28 -14
- data/lib/active_record/migration/compatibility.rb +72 -63
- data/lib/active_record/model_schema.rb +3 -0
- data/lib/active_record/persistence.rb +212 -19
- data/lib/active_record/querying.rb +18 -14
- data/lib/active_record/railtie.rb +9 -1
- data/lib/active_record/railties/collection_cache_association_loading.rb +3 -3
- data/lib/active_record/railties/databases.rake +124 -25
- data/lib/active_record/reflection.rb +18 -32
- data/lib/active_record/relation.rb +185 -35
- data/lib/active_record/relation/calculations.rb +40 -44
- data/lib/active_record/relation/delegation.rb +23 -31
- data/lib/active_record/relation/finder_methods.rb +23 -14
- data/lib/active_record/relation/merger.rb +11 -16
- data/lib/active_record/relation/query_attribute.rb +5 -3
- data/lib/active_record/relation/query_methods.rb +230 -69
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +10 -10
- data/lib/active_record/sanitization.rb +33 -4
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +10 -1
- data/lib/active_record/schema_migration.rb +1 -1
- data/lib/active_record/scoping.rb +6 -7
- data/lib/active_record/scoping/default.rb +7 -15
- data/lib/active_record/scoping/named.rb +10 -2
- data/lib/active_record/statement_cache.rb +2 -2
- data/lib/active_record/store.rb +48 -0
- data/lib/active_record/table_metadata.rb +9 -13
- data/lib/active_record/tasks/database_tasks.rb +109 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -1
- data/lib/active_record/test_databases.rb +1 -16
- data/lib/active_record/test_fixtures.rb +2 -2
- data/lib/active_record/timestamp.rb +35 -19
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +56 -46
- data/lib/active_record/type_caster/connection.rb +16 -10
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/arel.rb +18 -4
- data/lib/arel/insert_manager.rb +3 -3
- data/lib/arel/nodes.rb +2 -1
- data/lib/arel/nodes/and.rb +1 -1
- data/lib/arel/nodes/case.rb +1 -1
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/select_core.rb +16 -12
- data/lib/arel/nodes/unary.rb +1 -0
- data/lib/arel/nodes/values_list.rb +2 -17
- data/lib/arel/select_manager.rb +10 -10
- data/lib/arel/visitors/depth_first.rb +7 -2
- data/lib/arel/visitors/dot.rb +7 -2
- data/lib/arel/visitors/ibm_db.rb +13 -0
- data/lib/arel/visitors/informix.rb +6 -0
- data/lib/arel/visitors/mssql.rb +15 -1
- data/lib/arel/visitors/oracle12.rb +4 -5
- data/lib/arel/visitors/postgresql.rb +4 -10
- data/lib/arel/visitors/to_sql.rb +107 -131
- data/lib/arel/visitors/visitor.rb +9 -5
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +19 -12
- data/lib/active_record/collection_cache_key.rb +0 -53
- data/lib/arel/nodes/values.rb +0 -16
@@ -85,14 +85,14 @@ module ActiveRecord
|
|
85
85
|
# based on the requested role:
|
86
86
|
#
|
87
87
|
# ActiveRecord::Base.connected_to(role: :writing) do
|
88
|
-
# Dog.create! # creates dog using dog connection
|
88
|
+
# Dog.create! # creates dog using dog writing connection
|
89
89
|
# end
|
90
90
|
#
|
91
91
|
# ActiveRecord::Base.connected_to(role: :reading) do
|
92
92
|
# Dog.create! # throws exception because we're on a replica
|
93
93
|
# end
|
94
94
|
#
|
95
|
-
# ActiveRecord::Base.connected_to(role: :
|
95
|
+
# ActiveRecord::Base.connected_to(role: :unknown_role) do
|
96
96
|
# # raises exception due to non-existent role
|
97
97
|
# end
|
98
98
|
#
|
@@ -100,31 +100,44 @@ module ActiveRecord
|
|
100
100
|
# you can use +connected_to+ with a +database+ argument. The +database+ argument
|
101
101
|
# expects a symbol that corresponds to the database key in your config.
|
102
102
|
#
|
103
|
-
# This will connect to a new database for the queries inside the block.
|
104
|
-
#
|
105
103
|
# ActiveRecord::Base.connected_to(database: :animals_slow_replica) do
|
106
104
|
# Dog.run_a_long_query # runs a long query while connected to the +animals_slow_replica+
|
107
105
|
# end
|
108
|
-
|
106
|
+
#
|
107
|
+
# This will connect to a new database for the queries inside the block. By
|
108
|
+
# default the `:writing` role will be used since all connections must be assigned
|
109
|
+
# a role. If you would like to use a different role you can pass a hash to database:
|
110
|
+
#
|
111
|
+
# ActiveRecord::Base.connected_to(database: { readonly_slow: :animals_slow_replica }) do
|
112
|
+
# # runs a long query while connected to the +animals_slow_replica+ using the readonly_slow role.
|
113
|
+
# Dog.run_a_long_query
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# When using the database key a new connection will be established every time. It is not
|
117
|
+
# recommended to use this outside of one-off scripts.
|
118
|
+
def connected_to(database: nil, role: nil, prevent_writes: false, &blk)
|
109
119
|
if database && role
|
110
120
|
raise ArgumentError, "connected_to can only accept a `database` or a `role` argument, but not both arguments."
|
111
121
|
elsif database
|
112
122
|
if database.is_a?(Hash)
|
113
123
|
role, database = database.first
|
114
124
|
role = role.to_sym
|
115
|
-
else
|
116
|
-
role = database.to_sym
|
117
125
|
end
|
118
126
|
|
119
127
|
config_hash = resolve_config_for_connection(database)
|
120
128
|
handler = lookup_connection_handler(role)
|
121
129
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
130
|
+
handler.establish_connection(config_hash)
|
131
|
+
|
132
|
+
with_handler(role, &blk)
|
126
133
|
elsif role
|
127
|
-
|
134
|
+
if role == writing_role
|
135
|
+
with_handler(role.to_sym) do
|
136
|
+
connection_handler.while_preventing_writes(prevent_writes, &blk)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
with_handler(role.to_sym, &blk)
|
140
|
+
end
|
128
141
|
else
|
129
142
|
raise ArgumentError, "must provide a `database` or a `role`."
|
130
143
|
end
|
@@ -154,14 +167,11 @@ module ActiveRecord
|
|
154
167
|
end
|
155
168
|
|
156
169
|
def lookup_connection_handler(handler_key) # :nodoc:
|
170
|
+
handler_key ||= ActiveRecord::Base.writing_role
|
157
171
|
connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
158
172
|
end
|
159
173
|
|
160
174
|
def with_handler(handler_key, &blk) # :nodoc:
|
161
|
-
unless ActiveRecord::Base.connection_handlers.keys.include?(handler_key)
|
162
|
-
raise ArgumentError, "The #{handler_key} role does not exist. Add it by establishing a connection with `connects_to` or use an existing role (#{ActiveRecord::Base.connection_handlers.keys.join(", ")})."
|
163
|
-
end
|
164
|
-
|
165
175
|
handler = lookup_connection_handler(handler_key)
|
166
176
|
swap_connection_handler(handler, &blk)
|
167
177
|
end
|
@@ -170,7 +180,7 @@ module ActiveRecord
|
|
170
180
|
raise "Anonymous class is not allowed." unless name
|
171
181
|
|
172
182
|
config_or_env ||= DEFAULT_ENV.call.to_sym
|
173
|
-
pool_name =
|
183
|
+
pool_name = primary_class? ? "primary" : name
|
174
184
|
self.connection_specification_name = pool_name
|
175
185
|
|
176
186
|
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
|
@@ -180,6 +190,15 @@ module ActiveRecord
|
|
180
190
|
config_hash
|
181
191
|
end
|
182
192
|
|
193
|
+
# Clears the query cache for all connections associated with the current thread.
|
194
|
+
def clear_query_caches_for_current_thread
|
195
|
+
ActiveRecord::Base.connection_handlers.each_value do |handler|
|
196
|
+
handler.connection_pool_list.each do |pool|
|
197
|
+
pool.connection.clear_query_cache if pool.active_connection?
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
183
202
|
# Returns the connection currently associated with the class. This can
|
184
203
|
# also be used to "borrow" the connection to do database work unrelated
|
185
204
|
# to any of the specific Active Records.
|
@@ -197,6 +216,10 @@ module ActiveRecord
|
|
197
216
|
@connection_specification_name
|
198
217
|
end
|
199
218
|
|
219
|
+
def primary_class? # :nodoc:
|
220
|
+
self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
|
221
|
+
end
|
222
|
+
|
200
223
|
# Returns the configuration of the associated connection as a hash:
|
201
224
|
#
|
202
225
|
# ActiveRecord::Base.connection_config
|
data/lib/active_record/core.rb
CHANGED
@@ -124,6 +124,10 @@ module ActiveRecord
|
|
124
124
|
|
125
125
|
mattr_accessor :connection_handlers, instance_accessor: false, default: {}
|
126
126
|
|
127
|
+
mattr_accessor :writing_role, instance_accessor: false, default: :writing
|
128
|
+
|
129
|
+
mattr_accessor :reading_role, instance_accessor: false, default: :reading
|
130
|
+
|
127
131
|
class_attribute :default_connection_handler, instance_writer: false
|
128
132
|
|
129
133
|
self.filter_attributes = []
|
@@ -137,7 +141,6 @@ module ActiveRecord
|
|
137
141
|
end
|
138
142
|
|
139
143
|
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
|
140
|
-
self.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
|
141
144
|
end
|
142
145
|
|
143
146
|
module ClassMethods
|
@@ -157,7 +160,7 @@ module ActiveRecord
|
|
157
160
|
return super if block_given? ||
|
158
161
|
primary_key.nil? ||
|
159
162
|
scope_attributes? ||
|
160
|
-
columns_hash.
|
163
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
161
164
|
|
162
165
|
id = ids.first
|
163
166
|
|
@@ -171,14 +174,14 @@ module ActiveRecord
|
|
171
174
|
|
172
175
|
record = statement.execute([id], connection)&.first
|
173
176
|
unless record
|
174
|
-
raise RecordNotFound.new("Couldn't find #{name} with '#{
|
175
|
-
name, primary_key, id)
|
177
|
+
raise RecordNotFound.new("Couldn't find #{name} with '#{key}'=#{id}", name, key, id)
|
176
178
|
end
|
177
179
|
record
|
178
180
|
end
|
179
181
|
|
180
182
|
def find_by(*args) # :nodoc:
|
181
|
-
return super if scope_attributes? || reflect_on_all_aggregations.any?
|
183
|
+
return super if scope_attributes? || reflect_on_all_aggregations.any? ||
|
184
|
+
columns_hash.key?(inheritance_column) && !base_class?
|
182
185
|
|
183
186
|
hash = args.first
|
184
187
|
|
@@ -265,7 +268,8 @@ module ActiveRecord
|
|
265
268
|
end
|
266
269
|
|
267
270
|
def arel_attribute(name, table = arel_table) # :nodoc:
|
268
|
-
name =
|
271
|
+
name = name.to_s
|
272
|
+
name = attribute_aliases[name] || name
|
269
273
|
table[name]
|
270
274
|
end
|
271
275
|
|
@@ -313,7 +317,7 @@ module ActiveRecord
|
|
313
317
|
# # Instantiates a single new object
|
314
318
|
# User.new(first_name: 'Jamie')
|
315
319
|
def initialize(attributes = nil)
|
316
|
-
|
320
|
+
@new_record = true
|
317
321
|
@attributes = self.class._default_attributes.deep_dup
|
318
322
|
|
319
323
|
init_internals
|
@@ -350,12 +354,10 @@ module ActiveRecord
|
|
350
354
|
# +attributes+ should be an attributes object, and unlike the
|
351
355
|
# `initialize` method, no assignment calls are made per attribute.
|
352
356
|
def init_with_attributes(attributes, new_record = false) # :nodoc:
|
353
|
-
init_internals
|
354
|
-
|
355
357
|
@new_record = new_record
|
356
358
|
@attributes = attributes
|
357
359
|
|
358
|
-
|
360
|
+
init_internals
|
359
361
|
|
360
362
|
yield self if block_given?
|
361
363
|
|
@@ -394,13 +396,13 @@ module ActiveRecord
|
|
394
396
|
##
|
395
397
|
def initialize_dup(other) # :nodoc:
|
396
398
|
@attributes = @attributes.deep_dup
|
397
|
-
@attributes.reset(
|
399
|
+
@attributes.reset(@primary_key)
|
398
400
|
|
399
401
|
_run_initialize_callbacks
|
400
402
|
|
401
403
|
@new_record = true
|
402
404
|
@destroyed = false
|
403
|
-
@_start_transaction_state =
|
405
|
+
@_start_transaction_state = nil
|
404
406
|
@transaction_state = nil
|
405
407
|
|
406
408
|
super
|
@@ -461,6 +463,7 @@ module ActiveRecord
|
|
461
463
|
|
462
464
|
# Returns +true+ if the attributes hash has been frozen.
|
463
465
|
def frozen?
|
466
|
+
sync_with_transaction_state if @transaction_state&.finalized?
|
464
467
|
@attributes.frozen?
|
465
468
|
end
|
466
469
|
|
@@ -473,6 +476,14 @@ module ActiveRecord
|
|
473
476
|
end
|
474
477
|
end
|
475
478
|
|
479
|
+
def present? # :nodoc:
|
480
|
+
true
|
481
|
+
end
|
482
|
+
|
483
|
+
def blank? # :nodoc:
|
484
|
+
false
|
485
|
+
end
|
486
|
+
|
476
487
|
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
|
477
488
|
# attributes will be marked as read only since they cannot be saved.
|
478
489
|
def readonly?
|
@@ -557,34 +568,34 @@ module ActiveRecord
|
|
557
568
|
end
|
558
569
|
|
559
570
|
def init_internals
|
571
|
+
@primary_key = self.class.primary_key
|
560
572
|
@readonly = false
|
561
573
|
@destroyed = false
|
562
574
|
@marked_for_destruction = false
|
563
575
|
@destroyed_by_association = nil
|
564
|
-
@
|
565
|
-
@_start_transaction_state = {}
|
576
|
+
@_start_transaction_state = nil
|
566
577
|
@transaction_state = nil
|
567
|
-
end
|
568
578
|
|
569
|
-
|
579
|
+
self.class.define_attribute_methods
|
570
580
|
end
|
571
581
|
|
572
|
-
def
|
573
|
-
if frozen?
|
574
|
-
@attributes = @attributes.dup
|
575
|
-
end
|
582
|
+
def initialize_internals_callback
|
576
583
|
end
|
577
584
|
|
578
585
|
def custom_inspect_method_defined?
|
579
586
|
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
|
580
587
|
end
|
581
588
|
|
589
|
+
class InspectionMask < DelegateClass(::String)
|
590
|
+
def pretty_print(pp)
|
591
|
+
pp.text __getobj__
|
592
|
+
end
|
593
|
+
end
|
594
|
+
private_constant :InspectionMask
|
595
|
+
|
582
596
|
def inspection_filter
|
583
597
|
@inspection_filter ||= begin
|
584
|
-
mask =
|
585
|
-
def mask.pretty_print(pp)
|
586
|
-
pp.text __getobj__
|
587
|
-
end
|
598
|
+
mask = InspectionMask.new(ActiveSupport::ParameterFilter::FILTERED)
|
588
599
|
ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
|
589
600
|
end
|
590
601
|
end
|
@@ -7,8 +7,10 @@ require "active_record/database_configurations/url_config"
|
|
7
7
|
module ActiveRecord
|
8
8
|
# ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
|
9
9
|
# objects (either a HashConfig or UrlConfig) that are constructed from the
|
10
|
-
# application's database configuration hash or
|
10
|
+
# application's database configuration hash or URL string.
|
11
11
|
class DatabaseConfigurations
|
12
|
+
class InvalidConfigurationError < StandardError; end
|
13
|
+
|
12
14
|
attr_reader :configurations
|
13
15
|
delegate :any?, to: :configurations
|
14
16
|
|
@@ -17,22 +19,22 @@ module ActiveRecord
|
|
17
19
|
end
|
18
20
|
|
19
21
|
# Collects the configs for the environment and optionally the specification
|
20
|
-
# name passed in. To include replica configurations pass
|
22
|
+
# name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
|
21
23
|
#
|
22
24
|
# If a spec name is provided a single DatabaseConfig object will be
|
23
25
|
# returned, otherwise an array of DatabaseConfig objects will be
|
24
26
|
# returned that corresponds with the environment and type requested.
|
25
27
|
#
|
26
|
-
# Options
|
28
|
+
# ==== Options
|
27
29
|
#
|
28
|
-
# <tt>env_name:</tt> The environment name. Defaults to nil which will collect
|
29
|
-
#
|
30
|
-
# <tt>spec_name:</tt> The specification name (
|
31
|
-
#
|
32
|
-
# <tt>include_replicas:</tt> Determines whether to include replicas in
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
30
|
+
# * <tt>env_name:</tt> The environment name. Defaults to +nil+ which will collect
|
31
|
+
# configs for all environments.
|
32
|
+
# * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
|
33
|
+
# to +nil+.
|
34
|
+
# * <tt>include_replicas:</tt> Determines whether to include replicas in
|
35
|
+
# the returned list. Most of the time we're only iterating over the write
|
36
|
+
# connection (i.e. migrations don't need to run for the write and read connection).
|
37
|
+
# Defaults to +false+.
|
36
38
|
def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
|
37
39
|
configs = env_with_configs(env_name)
|
38
40
|
|
@@ -53,7 +55,7 @@ module ActiveRecord
|
|
53
55
|
|
54
56
|
# Returns the config hash that corresponds with the environment
|
55
57
|
#
|
56
|
-
# If the application has multiple databases
|
58
|
+
# If the application has multiple databases +default_hash+ will
|
57
59
|
# return the first config hash for the environment.
|
58
60
|
#
|
59
61
|
# { database: "my_db", adapter: "mysql2" }
|
@@ -65,7 +67,7 @@ module ActiveRecord
|
|
65
67
|
|
66
68
|
# Returns a single DatabaseConfig object based on the requested environment.
|
67
69
|
#
|
68
|
-
# If the application has multiple databases
|
70
|
+
# If the application has multiple databases +find_db_config+ will return
|
69
71
|
# the first DatabaseConfig for the environment.
|
70
72
|
def find_db_config(env)
|
71
73
|
configurations.find do |db_config|
|
@@ -91,6 +93,19 @@ module ActiveRecord
|
|
91
93
|
end
|
92
94
|
alias :blank? :empty?
|
93
95
|
|
96
|
+
def each
|
97
|
+
throw_getter_deprecation(:each)
|
98
|
+
configurations.each { |config|
|
99
|
+
yield [config.env_name, config.config]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
def first
|
104
|
+
throw_getter_deprecation(:first)
|
105
|
+
config = configurations.first
|
106
|
+
[config.env_name, config.config]
|
107
|
+
end
|
108
|
+
|
94
109
|
private
|
95
110
|
def env_with_configs(env = nil)
|
96
111
|
if env
|
@@ -102,83 +117,117 @@ module ActiveRecord
|
|
102
117
|
|
103
118
|
def build_configs(configs)
|
104
119
|
return configs.configurations if configs.is_a?(DatabaseConfigurations)
|
120
|
+
return configs if configs.is_a?(Array)
|
105
121
|
|
106
|
-
|
107
|
-
|
108
|
-
|
122
|
+
db_configs = configs.flat_map do |env_name, config|
|
123
|
+
if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
|
124
|
+
walk_configs(env_name.to_s, config)
|
125
|
+
else
|
126
|
+
build_db_config_from_raw_config(env_name.to_s, "primary", config)
|
127
|
+
end
|
128
|
+
end
|
109
129
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
130
|
+
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
|
131
|
+
|
132
|
+
unless db_configs.find(&:for_current_env?)
|
133
|
+
db_configs << environment_url_config(current_env, "primary", {})
|
114
134
|
end
|
135
|
+
|
136
|
+
merge_db_environment_variables(current_env, db_configs.compact)
|
115
137
|
end
|
116
138
|
|
117
|
-
def walk_configs(env_name,
|
139
|
+
def walk_configs(env_name, config)
|
140
|
+
config.map do |spec_name, sub_config|
|
141
|
+
build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def build_db_config_from_raw_config(env_name, spec_name, config)
|
118
146
|
case config
|
119
147
|
when String
|
120
148
|
build_db_config_from_string(env_name, spec_name, config)
|
121
149
|
when Hash
|
122
150
|
build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
|
151
|
+
else
|
152
|
+
raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
|
123
153
|
end
|
124
154
|
end
|
125
155
|
|
126
156
|
def build_db_config_from_string(env_name, spec_name, config)
|
127
157
|
url = config
|
128
158
|
uri = URI.parse(url)
|
129
|
-
if uri.
|
159
|
+
if uri.scheme
|
130
160
|
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
|
161
|
+
else
|
162
|
+
raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
|
131
163
|
end
|
132
|
-
rescue URI::InvalidURIError
|
133
|
-
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
134
164
|
end
|
135
165
|
|
136
166
|
def build_db_config_from_hash(env_name, spec_name, config)
|
137
|
-
if
|
167
|
+
if config.has_key?("url")
|
168
|
+
url = config["url"]
|
138
169
|
config_without_url = config.dup
|
139
170
|
config_without_url.delete "url"
|
171
|
+
|
140
172
|
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
|
141
|
-
elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
|
142
|
-
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
143
173
|
else
|
144
|
-
|
145
|
-
walk_configs(env_name, sub_spec_name, sub_config)
|
146
|
-
end
|
174
|
+
ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
|
147
175
|
end
|
148
176
|
end
|
149
177
|
|
150
|
-
def
|
151
|
-
|
178
|
+
def merge_db_environment_variables(current_env, configs)
|
179
|
+
configs.map do |config|
|
180
|
+
next config if config.url_config? || config.env_name != current_env
|
152
181
|
|
153
|
-
|
154
|
-
|
155
|
-
configs
|
156
|
-
else
|
157
|
-
configs.map do |config|
|
158
|
-
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, config.spec_name, url, config.config)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
else
|
162
|
-
configs + [ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, "primary", url)]
|
182
|
+
url_config = environment_url_config(current_env, config.spec_name, config.config)
|
183
|
+
url_config || config
|
163
184
|
end
|
164
185
|
end
|
165
186
|
|
166
|
-
def
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
187
|
+
def environment_url_config(env, spec_name, config)
|
188
|
+
url = environment_value_for(spec_name)
|
189
|
+
return unless url
|
190
|
+
|
191
|
+
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
|
192
|
+
end
|
193
|
+
|
194
|
+
def environment_value_for(spec_name)
|
195
|
+
spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
|
196
|
+
url = ENV[spec_env_key]
|
197
|
+
url ||= ENV["DATABASE_URL"] if spec_name == "primary"
|
198
|
+
url
|
199
|
+
end
|
171
200
|
|
201
|
+
def method_missing(method, *args, &blk)
|
172
202
|
case method
|
173
|
-
when :each, :first
|
174
|
-
configurations.send(method, *args, &blk)
|
175
203
|
when :fetch
|
204
|
+
throw_getter_deprecation(method)
|
176
205
|
configs_for(env_name: args.first)
|
177
206
|
when :values
|
207
|
+
throw_getter_deprecation(method)
|
178
208
|
configurations.map(&:config)
|
209
|
+
when :[]=
|
210
|
+
throw_setter_deprecation(method)
|
211
|
+
|
212
|
+
env_name = args[0]
|
213
|
+
config = args[1]
|
214
|
+
|
215
|
+
remaining_configs = configurations.reject { |db_config| db_config.env_name == env_name }
|
216
|
+
new_config = build_configs(env_name => config)
|
217
|
+
new_configs = remaining_configs + new_config
|
218
|
+
|
219
|
+
ActiveRecord::Base.configurations = new_configs
|
179
220
|
else
|
180
|
-
|
221
|
+
raise NotImplementedError, "`ActiveRecord::Base.configurations` in Rails 6 now returns an object instead of a hash. The `#{method}` method is not supported. Please use `configs_for` or consult the documentation for supported methods."
|
181
222
|
end
|
182
223
|
end
|
224
|
+
|
225
|
+
def throw_setter_deprecation(method)
|
226
|
+
ActiveSupport::Deprecation.warn("Setting `ActiveRecord::Base.configurations` with `#{method}` is deprecated. Use `ActiveRecord::Base.configurations=` directly to set the configurations instead.")
|
227
|
+
end
|
228
|
+
|
229
|
+
def throw_getter_deprecation(method)
|
230
|
+
ActiveSupport::Deprecation.warn("`ActiveRecord::Base.configurations` no longer returns a hash. Methods that act on the hash like `#{method}` are deprecated and will be removed in Rails 6.1. Use the `configs_for` method to collect and iterate over the database configurations.")
|
231
|
+
end
|
183
232
|
end
|
184
233
|
end
|