Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use built-in ActiveRecord URL resolver #294

Merged
merged 1 commit into from
Feb 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
All notable changes to this project will be documented in this file.

## Unreleased
- Use ActiveRecord URL resolver instead of copying definition [#294](https://github.com/instacart/makara/pull/294) Matt Larraz
- Deprecated the term `master` in favor of `primary` [#290](https://github.com/instacart/makara/pull/290) Matt Larraz
- Deprecated the term `slave` in favor of `replica` [#286](https://github.com/instacart/makara/pull/286) Matt Larraz
- Drop support for Ruby < 2.5 and ActiveRecord < 5.2 [#281](https://github.com/instacart/makara/pull/281) Matt Larraz
Expand Down
96 changes: 5 additions & 91 deletions lib/makara/config_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,99 +37,13 @@ class ConfigParser
master_ttl: :primary_ttl
}.freeze

# ConnectionUrlResolver is borrowed from Rails 4-2 since its location and implementation
# vary slightly among Rails versions, but the behavior is the same. Thus, borrowing the
# class should be the most future-safe way to parse a database url.
#
# 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.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.reject { |_,value| value.blank? }
config.map { |key,value| config[key] = CGI.unescape(value) if value.is_a? String }
config
end

private

def uri
@uri
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("=") }]
ConnectionUrlResolver =
if ::ActiveRecord::VERSION::STRING >= "6.1.0"
::ActiveRecord::DatabaseConfigurations::ConnectionUrlResolver
else
::ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver
end

def raw_config
if uri.opaque
query_hash.merge({
"adapter" => @adapter,
"database" => uri.opaque })
else
query_hash.merge({
"adapter" => @adapter,
"username" => uri.user,
"password" => uri.password,
"port" => uri.port,
"database" => database_from_path,
"host" => uri.host })
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.sub(%r{^/}, "")
end
end
end

# NOTE: url format must be, e.g.
# url: mysql2://...
# NOT
Expand Down