class ActiveRecord::DatabaseConfigurations
registering custom handlers.
conditions of the handler. See ::register_db_config_handler for more on
If you register a custom handler, objects will be created according to the
ActiveRecord::Base.configurations.
HashConfig or UrlConfig. You can retrieve your application’s config by using
The array of DatabaseConfig
objects in an application default to either a
configuration hash or URL string.
objects that are constructed from the application’s databaseActiveRecord::DatabaseConfigurations
returns an array of DatabaseConfig
= Active Record Database Configurations
def self.register_db_config_handler(&block)
For configs that have a +:vitess+ key, a +VitessConfig+ object will be
end
end
configuration_hash.fetch("sharded", false)
def sharded?
class VitessConfig < ActiveRecord::DatabaseConfigurations::UrlConfig
config handlers should only implement methods Active Record does not.
database config classes to avoid having to reimplement all methods. Custom
needs. It is recommended that you inherit from one of the existing
Then define your +VitessConfig+ to respond to the methods your application
handler.
created in your handler registration otherwise all objects will use the custom
Note: applications must handle the condition in which custom config should be
end
VitessConfig.new(env_name, name, config)
next unless config.key?(:vitess)
ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, url, config|
initializer:
to respond to `sharded?`. To implement this define the following in an
example if you are using Vitess, you may want your Vitess configurations
methods your application needs but Active Record doesn't implement. For
objects. This is useful for creating a custom handler that responds to
Allows an application to register a custom handler for database configuration
def self.register_db_config_handler(&block) db_config_handlers << block end
def build_configs(configs)
def build_configs(configs) return configs.configurations if configs.is_a?(DatabaseConfigurations) return configs if configs.is_a?(Array) db_configs = configs.flat_map do |env_name, config| if config.is_a?(Hash) && config.values.all?(Hash) walk_configs(env_name.to_s, config) else build_db_config_from_raw_config(env_name.to_s, "primary", config) end end unless db_configs.find(&:for_current_env?) db_configs << environment_url_config(default_env, "primary", {}) end merge_db_environment_variables(default_env, db_configs.compact) end
def build_configuration_sentence
def build_configuration_sentence configs = configs_for(include_hidden: true) configs.group_by(&:env_name).map do |env, config| names = config.map(&:name) if names.size > 1 "#{env}: #{names.join(", ")}" else env end end.join("\n") end
def build_db_config_from_hash(env_name, name, config)
def build_db_config_from_hash(env_name, name, config) url = config[:url] config_without_url = config.dup config_without_url.delete :url DatabaseConfigurations.db_config_handlers.reverse_each do |handler| config = handler.call(env_name, name, url, config_without_url) return config if config end nil end
def build_db_config_from_raw_config(env_name, name, config)
def build_db_config_from_raw_config(env_name, name, config) case config when String build_db_config_from_string(env_name, name, config) when Hash build_db_config_from_hash(env_name, name, config.symbolize_keys) else raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash." end end
def build_db_config_from_string(env_name, name, config)
def build_db_config_from_string(env_name, name, config) url = config uri = URI.parse(url) if uri.scheme UrlConfig.new(env_name, name, url) else raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash." end end
def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false)
iterating over the primary connections (i.e. migrations don't need to run for the
hidden by database_tasks: false in the returned list. Most of the time we're only
* include_hidden: Determines whether to include replicas and configurations
configs with hashes that contain a particular key.
hash. Useful for selecting configs that use a custom db config handler or finding
* config_key: Selects configs that contain a particular key in the configuration
passed +name+ will be returned.
to +nil+. If no +env_name+ is specified the config for the default env and the
* name: The db config name (i.e. primary, animals, etc.). Defaults
configs for all environments.
* env_name: The environment name. Defaults to +nil+ which will collect
==== Options
returned that corresponds with the environment and type requested.
returned, otherwise an array of +DatabaseConfig+ objects will be
If a name is provided a single +DatabaseConfig+ object will be
name passed in. To include replica configurations pass include_hidden: true.
Collects the configs for the environment and optionally the specification
def configs_for(env_name: nil, name: nil, config_key: nil, include_hidden: false) env_name ||= default_env if name configs = env_with_configs(env_name) unless include_hidden configs = configs.select do |db_config| db_config.database_tasks? end end if config_key configs = configs.select do |db_config| db_config.configuration_hash.key?(config_key) end end if name configs.find do |db_config| db_config.name == name end else configs end end
def default_env
def default_env ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s end
def empty?
def empty? configurations.empty? end
def env_with_configs(env = nil)
def env_with_configs(env = nil) if env configurations.select { |db_config| db_config.env_name == env } else configurations end end
def environment_url_config(env, name, config)
def environment_url_config(env, name, config) url = environment_value_for(name) return unless url UrlConfig.new(env, name, url, config) end
def environment_value_for(name)
def environment_value_for(name) name_env_key = "#{name.upcase}_DATABASE_URL" url = ENV[name_env_key] url ||= ENV["DATABASE_URL"] if name == "primary" url end
def find_db_config(env)
If the application has multiple databases +find_db_config+ will return
Returns a single +DatabaseConfig+ object based on the requested environment.
def find_db_config(env) env = env.to_s configurations.find do |db_config| db_config.for_current_env? && (db_config.env_name == env || db_config.name == env) end || configurations.find do |db_config| db_config.env_name == env end end
def initialize(configurations = {})
def initialize(configurations = {}) @configurations = build_configs(configurations) end
def merge_db_environment_variables(current_env, configs)
def merge_db_environment_variables(current_env, configs) configs.map do |config| next config if config.is_a?(UrlConfig) || config.env_name != current_env url_config = environment_url_config(current_env, config.name, config.configuration_hash) url_config || config end end
def primary?(name) # :nodoc:
file will be named `schema.rb` instead of `primary_schema.rb`.
example, when Rails dumps the schema, the primary configuration's schema
when the application needs to treat one configuration differently. For
as primary. This is used as the "default" configuration and is used
no primary, the first configuration for an environment will be treated
A primary configuration is one that is named primary or if there is
def primary?(name) # :nodoc: return true if name == "primary" first_config = find_db_config(default_env) first_config && name == first_config.name end
def resolve(config) # :nodoc:
# => DatabaseConfigurations::UrlConfig.new(config: {"adapter" => "postgresql", "host" => "localhost", "database" => "foo"})
DatabaseConfigurations.new({}).resolve("postgresql://localhost/foo")
Connection URL.
# => DatabaseConfigurations::HashConfig.new(config: {"adapter" => "sqlite3"})
DatabaseConfigurations.new({}).resolve("adapter" => "sqlite3")
One layer deep hash of connection values.
# => DatabaseConfigurations::HashConfig.new(env_name: "production", config: {})
DatabaseConfigurations.new("production" => {}).resolve(:production)
Symbol representing current environment.
== Examples
Always returns a DatabaseConfiguration::DatabaseConfig
Returns fully resolved connection, accepts hash, string or symbol.
def resolve(config) # :nodoc: return config if DatabaseConfigurations::DatabaseConfig === config case config when Symbol resolve_symbol_connection(config) when Hash, String build_db_config_from_raw_config(default_env, "primary", config) else raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config.inspect}" end end
def resolve_symbol_connection(name)
def resolve_symbol_connection(name) if db_config = find_db_config(name) db_config else raise AdapterNotSpecified, <<~MSG The `#{name}` database is not configured for the `#{default_env}` environment. Available database configurations are: #{build_configuration_sentence} MSG end end
def walk_configs(env_name, config)
def walk_configs(env_name, config) config.map do |name, sub_config| build_db_config_from_raw_config(env_name, name.to_s, sub_config) end end