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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
require 'uri'
module ActiveRecord
module ConnectionAdapters
class ConnectionSpecification #:nodoc:
attr_reader :config, :adapter_method
def initialize(config, adapter_method)
@config, @adapter_method = config, adapter_method
end
def initialize_dup(original)
@config = original.config.dup
end
# 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.parse(url)
@adapter = @uri.scheme
@adapter = "postgresql" if @adapter == "postgres"
@query = @uri.query || ''
end
# Converts the given url to a full connection hash
def to_hash
config = raw_config.reject { |_,value| value.blank? }
config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
config
end
private
def uri
@uri
end
def uri_parser
@uri_parser ||= URI::Parser.new
end
# Converts the query parameters of the uri into a hash
# "localhost?pool=5&reap_frequency=2"
# # => {"pool" => "5", "reap_frequency" => "2"}
#
# returns empty hash if no query present
# "localhost"
# # => {}
def query_hash
Hash[@query.split("&").map { |pair| pair.split("=") }]
end
def raw_config
query_hash.merge({
"adapter" => @adapter,
"username" => uri.user,
"password" => uri.password,
"port" => uri.port,
"database" => database,
"host" => uri.host })
end
# Returns name of the database
# sqlite3 expects this to be a full path or `:memory`
def database
if @adapter == 'sqlite3'
if '/:memory:' == uri.path
':memory:'
else
uri.path
end
else
uri.path.sub(%r{^/},"")
end
end
end
##
# Builds a ConnectionSpecification from user input
class Resolver # :nodoc:
attr_reader :configurations
def initialize(configurations)
@configurations = configurations
end
def resolve(config)
if config
resolve_connection config
elsif defined?(Rails.env)
resolve_env_connection Rails.env.to_sym
else
raise AdapterNotSpecified
end
end
def spec(config)
spec = resolve(config).symbolize_keys
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
begin
require path_to_adapter
rescue Gem::LoadError => e
raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
rescue LoadError => e
raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
end
adapter_method = "#{spec[:adapter]}_connection"
ConnectionSpecification.new(spec, adapter_method)
end
private
def resolve_connection(spec) #:nodoc:
case spec
when Symbol, String
resolve_env_connection spec
when Hash
resolve_hash_connection spec
end
end
def resolve_env_connection(spec) # :nodoc:
# Rails has historically accepted a string to mean either
# an environment key or a url spec, so we have deprecated
# this ambiguous behaviour and in the future this function
# can be removed in favor of resolve_string_connection and
# resolve_symbol_connection.
if config = configurations[spec.to_s]
if spec.is_a?(String)
ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
"for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
end
resolve_connection(config)
elsif spec.is_a?(String)
resolve_string_connection(spec)
else
raise(AdapterNotSpecified, "#{spec} database is not configured")
end
end
def resolve_hash_connection(spec) # :nodoc:
spec
end
def resolve_string_connection(url) # :nodoc:
ConnectionUrlResolver.new(url).to_hash
end
end
end
end
end
|