Add deprecation and specific exceptions to warn when an adapter is using the legacy registration format in 7.2

This commit is contained in:
Kevin McPhillips 2023-11-23 11:02:34 -05:00
parent 9c22f35440
commit 79fb41ddd9
4 changed files with 142 additions and 3 deletions

View File

@ -31,6 +31,62 @@ module ActiveRecord
class_name, path_to_adapter = @adapters[adapter_name.to_s]
unless class_name
# To provide better error messages for adapters expecting the pre-7.2 adapter registration API, we attempt
# to load the adapter file from the old location which was required by convention, and then raise an error
# describing how to upgrade the adapter to the new API.
legacy_adapter_path = "active_record/connection_adapters/#{adapter_name}_adapter"
legacy_adapter_connection_method_name = "#{adapter_name}_connection".to_sym
begin
require legacy_adapter_path
# If we reach here it means we found the found a file that may be the legacy adapter and should raise.
if ActiveRecord::ConnectionHandling.method_defined?(legacy_adapter_connection_method_name)
# If we find the connection method then we care certain it is a legacy adapter.
deprecation_message = <<~MSG.squish
Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
Rails 7.2 has changed the way Active Record database adapters are loaded. The adapter needs to be
updated to register itself rather than being loaded by convention.
Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
be modified.
See:
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
MSG
exception_message = <<~MSG.squish
Database configuration specifies '#{adapter_name}' adapter but that adapter has not been registered.
Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to
be modified.
MSG
else
# If we do not find the connection method we are much less certain it is a legacy adapter. Even though the
# file exists in the location defined by convenntion, it does not necessarily mean that file is supposed
# to define the adapter the legacy way. So raise an error that explains both possibilities.
deprecation_message = <<~MSG.squish
Database configuration specifies nonexistent '#{adapter_name}' adapter.
Available adapters are: #{@adapters.keys.sort.join(", ")}.
Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
adapter gem to your Gemfile if it's not in the list of available adapters.
Rails 7.2 has changed the way Active Record database adapters are loaded. Ensure that the adapter in
the Gemfile is at the latest version. If it is up to date, the adapter may need to be modified.
See:
https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters.html#method-c-register
MSG
exception_message = <<~MSG.squish
Database configuration specifies nonexistent '#{adapter_name}' adapter.
Available adapters are: #{@adapters.keys.sort.join(", ")}.
Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary
adapter gem to your Gemfile and that it is at its latest version. If it is up to date, the adapter may
need to be modified.
MSG
end
ActiveRecord.deprecator.warn(deprecation_message)
raise AdapterNotFound, exception_message
rescue LoadError => error
# The adapter was not found in the legacy location so fall through to the error handling for a missing adapter.
end
raise AdapterNotFound, <<~MSG.squish
Database configuration specifies nonexistent '#{adapter_name}' adapter.
Available adapters are: #{@adapters.keys.sort.join(", ")}.

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module ActiveRecord
module ConnectionHandling
def fake_legacy_connection(config)
ConnectionAdapters::FakeLegacyAdapter.new nil, logger
end
end
module ConnectionAdapters
class FakeLegacyAdapter < AbstractAdapter
def active?
true
end
end
end
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
module ActiveRecord
module ConnectionAdapters
class FakeMisleadingLegacyAdapter < AbstractAdapter
end
end
end

View File

@ -16,34 +16,92 @@ module ActiveRecord
end
test "#register registers a new database adapter and #resolve can find it and raises if it cannot" do
assert_raises(ActiveRecord::AdapterNotFound) do
exception = assert_raises(ActiveRecord::AdapterNotFound) do
ActiveRecord::ConnectionAdapters.resolve("fake")
end
assert_match(
/Database configuration specifies nonexistent 'fake' adapter. Available adapters are:/,
exception.message
)
ActiveRecord::ConnectionAdapters.register("fake", "FakeActiveRecordAdapter", @fake_adapter_path)
assert_equal "FakeActiveRecordAdapter", ActiveRecord::ConnectionAdapters.resolve("fake").name
end
test "#register allows for symbol key" do
assert_raises(ActiveRecord::AdapterNotFound) do
exception = assert_raises(ActiveRecord::AdapterNotFound) do
ActiveRecord::ConnectionAdapters.resolve("fake")
end
assert_match(
/Database configuration specifies nonexistent 'fake' adapter. Available adapters are:/,
exception.message
)
ActiveRecord::ConnectionAdapters.register(:fake, "FakeActiveRecordAdapter", @fake_adapter_path)
assert_equal "FakeActiveRecordAdapter", ActiveRecord::ConnectionAdapters.resolve("fake").name
end
test "#resolve allows for symbol key" do
assert_raises(ActiveRecord::AdapterNotFound) do
exception = assert_raises(ActiveRecord::AdapterNotFound) do
ActiveRecord::ConnectionAdapters.resolve("fake")
end
assert_match(
/Database configuration specifies nonexistent 'fake' adapter. Available adapters are:/,
exception.message
)
ActiveRecord::ConnectionAdapters.register("fake", "FakeActiveRecordAdapter", @fake_adapter_path)
assert_equal "FakeActiveRecordAdapter", ActiveRecord::ConnectionAdapters.resolve(:fake).name
end
end
class RegistrationIsolatedTest < ActiveRecord::TestCase
include ActiveSupport::Testing::Isolation
def setup
@original_adapters = ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).dup
end
test "#resolve raises if the adapter is using the pre 7.2 adapter registration API" do
exception = assert_raises(ActiveRecord::AdapterNotFound) do
assert_deprecated(ActiveRecord.deprecator) do
ActiveRecord::ConnectionAdapters.resolve("fake_legacy")
end
end
assert_match(
/Database configuration specifies 'fake_legacy' adapter but that adapter has not been registered. Ensure that the adapter in the Gemfile is at the latest version. If it is, then the adapter may need to be modified./,
exception.message
)
ensure
ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).delete("fake_legacy")
end
test "#resolve raises if the adapter maybe is using the pre 7.2 adapter registration API but we are not sure" do
exception = assert_raises(ActiveRecord::AdapterNotFound) do
assert_deprecated(ActiveRecord.deprecator) do
ActiveRecord::ConnectionAdapters.resolve("fake_misleading_legacy")
end
end
assert_match(
/Database configuration specifies nonexistent 'fake_misleading_legacy' adapter. Available adapters are:/,
exception.message
)
assert_match(
/Ensure that the adapter is spelled correctly in config\/database.yml and that you've added the necessary adapter gem to your Gemfile and that it is at its latest version. If it is up to date, the adapter may need to be modified./,
exception.message
)
ensure
ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).delete("fake_misleading_legacy")
end
end
end
end