Skip to content

Commit 22a26d7

Browse files
authored
Merge pull request rails#50064 from Shopify/refactor-adapter-lookup
2 parents 73c090f + 009c7e7 commit 22a26d7

31 files changed

+351
-292
lines changed

activerecord/lib/active_record/connection_adapters.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,65 @@ module ActiveRecord
44
module ConnectionAdapters
55
extend ActiveSupport::Autoload
66

7+
@adapters = {}
8+
9+
class << self
10+
# Registers a custom database adapter.
11+
#
12+
# Can also be used to define aliases.
13+
#
14+
# == Example
15+
#
16+
# ActiveRecord::ConnectionAdapters.register("megadb", "MegaDB::ActiveRecordAdapter", "mega_db/active_record_adapter")
17+
#
18+
# ActiveRecord::ConnectionAdapters.register("mysql", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter")
19+
#
20+
def register(name, class_name, path = class_name.underscore)
21+
@adapters[name] = [class_name, path]
22+
end
23+
24+
def resolve(adapter_name) # :nodoc:
25+
# Require the adapter itself and give useful feedback about
26+
# 1. Missing adapter gems and
27+
# 2. Adapter gems' missing dependencies.
28+
class_name, path_to_adapter = @adapters[adapter_name]
29+
30+
unless class_name
31+
raise AdapterNotFound, "database configuration specifies nonexistent '#{adapter_name}' adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile."
32+
end
33+
34+
unless Object.const_defined?(class_name)
35+
begin
36+
require path_to_adapter
37+
rescue LoadError => error
38+
# We couldn't require the adapter itself. Raise an exception that
39+
# points out config typos and missing gems.
40+
if error.path == path_to_adapter
41+
# We can assume that a non-builtin adapter was specified, so it's
42+
# either misspelled or missing from Gemfile.
43+
raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Ensure that the necessary adapter gem is in the Gemfile. #{error.message}", error.backtrace
44+
45+
# Bubbled up from the adapter require. Prefix the exception message
46+
# with some guidance about how to address it and reraise.
47+
else
48+
raise LoadError, "Error loading the '#{adapter_name}' Active Record adapter. Missing a gem it depends on? #{error.message}", error.backtrace
49+
end
50+
end
51+
end
52+
53+
begin
54+
Object.const_get(class_name)
55+
rescue NameError => error
56+
raise AdapterNotFound, "Could not load the #{class_name} Active Record adapter (#{error.message})."
57+
end
58+
end
59+
end
60+
61+
register "sqlite3", "ActiveRecord::ConnectionAdapters::SQLite3Adapter", "active_record/connection_adapters/sqlite3_adapter"
62+
register "mysql2", "ActiveRecord::ConnectionAdapters::Mysql2Adapter", "active_record/connection_adapters/mysql2_adapter"
63+
register "trilogy", "ActiveRecord::ConnectionAdapters::TrilogyAdapter", "active_record/connection_adapters/trilogy_adapter"
64+
register "postgresql", "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter", "active_record/connection_adapters/postgresql_adapter"
65+
766
eager_autoload do
867
autoload :AbstractAdapter
968
end

activerecord/lib/active_record/connection_adapters/abstract/connection_handler.rb

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -324,32 +324,6 @@ def resolve_pool_config(config, connection_name, role, shard)
324324
db_config = Base.configurations.resolve(config)
325325

326326
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
327-
328-
# Require the adapter itself and give useful feedback about
329-
# 1. Missing adapter gems and
330-
# 2. Adapter gems' missing dependencies.
331-
path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
332-
begin
333-
require path_to_adapter
334-
rescue LoadError => e
335-
# We couldn't require the adapter itself. Raise an exception that
336-
# points out config typos and missing gems.
337-
if e.path == path_to_adapter
338-
# We can assume that a non-builtin adapter was specified, so it's
339-
# either misspelled or missing from Gemfile.
340-
raise LoadError, "Could not load the '#{db_config.adapter}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
341-
342-
# Bubbled up from the adapter require. Prefix the exception message
343-
# with some guidance about how to address it and reraise.
344-
else
345-
raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
346-
end
347-
end
348-
349-
unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
350-
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
351-
end
352-
353327
ConnectionAdapters::PoolConfig.new(connection_name, db_config, role, shard)
354328
end
355329

activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ def remove_connection_from_thread_cache(conn, owner_thread = conn.owner)
682682
alias_method :release, :remove_connection_from_thread_cache
683683

684684
def new_connection
685-
connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
685+
connection = db_config.new_connection
686686
connection.pool = self
687687
connection
688688
rescue ConnectionNotEstablished => ex

activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,6 @@
77
require "mysql2"
88

99
module ActiveRecord
10-
module ConnectionHandling # :nodoc:
11-
def mysql2_adapter_class
12-
ConnectionAdapters::Mysql2Adapter
13-
end
14-
15-
# Establishes a connection to the database that's used by all Active Record objects.
16-
def mysql2_connection(config)
17-
mysql2_adapter_class.new(config)
18-
end
19-
end
20-
2110
module ConnectionAdapters
2211
# = Active Record MySQL2 Adapter
2312
class Mysql2Adapter < AbstractMysqlAdapter

activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,6 @@
2020
require "active_record/connection_adapters/postgresql/utils"
2121

2222
module ActiveRecord
23-
module ConnectionHandling # :nodoc:
24-
def postgresql_adapter_class
25-
ConnectionAdapters::PostgreSQLAdapter
26-
end
27-
28-
# Establishes a connection to the database that's used by all Active Record objects
29-
def postgresql_connection(config)
30-
postgresql_adapter_class.new(config)
31-
end
32-
end
33-
3423
module ConnectionAdapters
3524
# = Active Record PostgreSQL Adapter
3625
#

activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,6 @@
1515
require "sqlite3"
1616

1717
module ActiveRecord
18-
module ConnectionHandling # :nodoc:
19-
def sqlite3_adapter_class
20-
ConnectionAdapters::SQLite3Adapter
21-
end
22-
23-
def sqlite3_connection(config)
24-
sqlite3_adapter_class.new(config)
25-
end
26-
end
27-
2818
module ConnectionAdapters # :nodoc:
2919
# = Active Record SQLite3 Adapter
3020
#

activerecord/lib/active_record/connection_adapters/trilogy_adapter.rb

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,6 @@
88
require "active_record/connection_adapters/trilogy/database_statements"
99

1010
module ActiveRecord
11-
module ConnectionHandling # :nodoc:
12-
def trilogy_adapter_class
13-
ConnectionAdapters::TrilogyAdapter
14-
end
15-
16-
# Establishes a connection to the database that's used by all Active Record objects.
17-
def trilogy_connection(config)
18-
configuration = config.dup
19-
20-
# Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
21-
# matched rather than number of rows updated.
22-
configuration[:found_rows] = true
23-
24-
trilogy_adapter_class.new(configuration)
25-
end
26-
end
27-
2811
module ConnectionAdapters
2912
class TrilogyAdapter < AbstractMysqlAdapter
3013
ER_BAD_DB_ERROR = 1049
@@ -90,6 +73,16 @@ def initialize_type_map(m)
9073
end
9174
end
9275

76+
def initialize(config, *)
77+
config = config.dup
78+
79+
# Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
80+
# matched rather than number of rows updated.
81+
config[:found_rows] = true
82+
83+
super
84+
end
85+
9386
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
9487

9588
def supports_json?

activerecord/lib/active_record/database_configurations/database_config.rb

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,24 @@ class DatabaseConfigurations
88
class DatabaseConfig # :nodoc:
99
attr_reader :env_name, :name
1010

11+
def self.new(...)
12+
instance = super
13+
instance.adapter_class # Ensure resolution happens early
14+
instance
15+
end
16+
1117
def initialize(env_name, name)
1218
@env_name = env_name
1319
@name = name
20+
@adapter_class = nil
1421
end
1522

16-
def adapter_method
17-
"#{adapter}_connection"
23+
def adapter_class
24+
@adapter_class ||= ActiveRecord::ConnectionAdapters.resolve(adapter)
1825
end
1926

20-
def adapter_class_method
21-
"#{adapter}_adapter_class"
27+
def new_connection
28+
adapter_class.new(configuration_hash)
2229
end
2330

2431
def host

activerecord/lib/active_record/database_configurations/hash_config.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def idle_timeout
104104
end
105105

106106
def adapter
107-
configuration_hash[:adapter]
107+
configuration_hash[:adapter]&.to_s
108108
end
109109

110110
# The path to the schema cache dump file for a database.

activerecord/test/active_record/connection_adapters/fake_adapter.rb

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)