/
connection_url_resolver.rb
100 lines (89 loc) · 2.83 KB
/
connection_url_resolver.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# frozen_string_literal: true
require "uri"
require "active_support/core_ext/enumerable"
require "active_support/core_ext/hash/reverse_merge"
module ActiveRecord
class DatabaseConfigurations
# Expands a connection string into a hash.
class ConnectionUrlResolver # :nodoc:
# == Example
#
# url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
# ConnectionUrlResolver.new(url).to_hash
# # => {
# adapter: "postgresql",
# host: "localhost",
# port: 9000,
# database: "foo_test",
# username: "foo",
# password: "bar",
# pool: "5",
# timeout: "3000"
# }
def initialize(url)
raise "Database URL cannot be empty" if url.blank?
@uri = uri_parser.parse(url)
@adapter = @uri.scheme && @uri.scheme.tr("-", "_")
@adapter = "postgresql" if @adapter == "postgres"
if @uri.opaque
@uri.opaque, @query = @uri.opaque.split("?", 2)
else
@query = @uri.query
end
end
# Converts the given URL to a full connection hash.
def to_hash
config = raw_config.compact_blank
config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
private
attr_reader :uri
def uri_parser
@uri_parser ||= URI::Parser.new
end
# Converts the query parameters of the URI into a hash.
#
# "localhost?pool=5&reaping_frequency=2"
# # => { pool: "5", reaping_frequency: "2" }
#
# returns empty hash if no query present.
#
# "localhost"
# # => {}
def query_hash
Hash[(@query || "").split("&").map { |pair| pair.split("=", 2) }].symbolize_keys
end
def raw_config
if uri.opaque
query_hash.merge(
adapter: @adapter,
database: uri.opaque
)
else
query_hash.reverse_merge(
adapter: @adapter,
username: uri.user,
password: uri.password,
port: uri.port,
database: database_from_path,
host: uri.hostname
)
end
end
# Returns name of the database.
def database_from_path
if @adapter == "sqlite3"
# 'sqlite3:/foo' is absolute, because that makes sense. The
# corresponding relative version, 'sqlite3:foo', is handled
# elsewhere, as an "opaque".
uri.path
else
# Only SQLite uses a filename as the "database" name; for
# anything else, a leading slash would be silly.
uri.path.delete_prefix("/")
end
end
end
end
end