1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/lib/active_record.rb
Eileen Uchitelle fdf3f0b930 Refactors Active Record connection management
While the three-tier config makes it easier to define databases for
multiple database applications, it quickly became clear to offer full
support for multiple databases we need to change the way the connections
hash was handled.

A three-tier config means that when Rails needed to choose a default
configuration (in the case a user doesn't ask for a specific
configuration) it wasn't clear to Rails which the default was. I
[bandaid fixed this so the rake tasks could work](#32271) but that fix
wasn't correct because it actually doubled up the configuration hashes.

Instead of attemping to manipulate the hashes @tenderlove and I decided
that it made more sense if we converted the hashes to objects so we can
easily ask those object questions. In a three tier config like this:

```
development:
  primary:
    database: "my_primary_db"
  animals:
    database; "my_animals_db"
```

We end up with an object like this:

```
  @configurations=[
    #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
      @env_name="development",@spec_name="primary",
      @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
    #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
      @env_name="development",@spec_name="animals",
      @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
]>
```

The configurations setter takes the database configuration set by your
application and turns them into an
`ActiveRecord::DatabaseConfigurations` object that has one getter -
`@configurations` which is an array of all the database objects.

The configurations getter returns this object by default since it acts
like a hash in most of the cases we need. For example if you need to
access the default `development` database we can simply request it as we
did before:

```
ActiveRecord::Base.configurations["development"]
```

This will return primary development database configuration hash:

```
{ "database" => "my_primary_db" }
```

Internally all of Active Record has been converted to use the new
objects. I've built this to be backwards compatible but allow for
accessing the hash if needed for a deprecation period. To get the
original hash instead of the object you can either add `to_h` on the
configurations call or pass `legacy: true` to `configurations.

```
ActiveRecord::Base.configurations.to_h
=> { "development => { "database" => "my_primary_db" } }

ActiveRecord::Base.configurations(legacy: true)
=> { "development => { "database" => "my_primary_db" } }
```

The new configurations object allows us to iterate over the Active
Record configurations without losing the known environment or
specification name for that configuration. You can also select all the
configs for an env or env and spec. With this we can always ask
any object what environment it belongs to:

```
db_configs = ActiveRecord::Base.configurations.configurations_for("development")
=> #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800
  @configurations=[
    #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
      @env_name="development",@spec_name="primary",
      @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
    #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90
      @env_name="development",@spec_name="animals",
      @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>
]>

db_config.env_name
=> "development"

db_config.spec_name
=> "primary"

db_config.config
=> { "adapter"=>"sqlite3", "database"=>"db/development.sqlite3" }
```

The configurations object is more flexible than the configurations hash
and will allow us to build on top of the connection management in order
to add support for primary/replica connections, sharding, and
constructing queries for associations that live in multiple databases.
2018-08-30 10:06:45 -04:00

189 lines
5.1 KiB
Ruby

# frozen_string_literal: true
#--
# Copyright (c) 2004-2018 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
require "active_support"
require "active_support/rails"
require "active_model"
require "arel"
require "yaml"
require "active_record/version"
require "active_model/attribute_set"
module ActiveRecord
extend ActiveSupport::Autoload
autoload :Base
autoload :Callbacks
autoload :Core
autoload :ConnectionHandling
autoload :CounterCache
autoload :DynamicMatchers
autoload :Enum
autoload :InternalMetadata
autoload :Explain
autoload :Inheritance
autoload :Integration
autoload :Migration
autoload :Migrator, "active_record/migration"
autoload :ModelSchema
autoload :NestedAttributes
autoload :NoTouching
autoload :TouchLater
autoload :Persistence
autoload :QueryCache
autoload :Querying
autoload :CollectionCacheKey
autoload :ReadonlyAttributes
autoload :RecordInvalid, "active_record/validations"
autoload :Reflection
autoload :RuntimeRegistry
autoload :Sanitization
autoload :Schema
autoload :SchemaDumper
autoload :SchemaMigration
autoload :Scoping
autoload :Serialization
autoload :StatementCache
autoload :Store
autoload :Suppressor
autoload :Timestamp
autoload :Transactions
autoload :Translation
autoload :Validations
autoload :SecureToken
eager_autoload do
autoload :ActiveRecordError, "active_record/errors"
autoload :ConnectionNotEstablished, "active_record/errors"
autoload :ConnectionAdapters, "active_record/connection_adapters/abstract_adapter"
autoload :Aggregations
autoload :Associations
autoload :AttributeAssignment
autoload :AttributeMethods
autoload :AutosaveAssociation
autoload :LegacyYamlAdapter
autoload :Relation
autoload :AssociationRelation
autoload :NullRelation
autoload_under "relation" do
autoload :QueryMethods
autoload :FinderMethods
autoload :Calculations
autoload :PredicateBuilder
autoload :SpawnMethods
autoload :Batches
autoload :Delegation
end
autoload :Result
autoload :TableMetadata
autoload :Type
end
module Coders
autoload :YAMLColumn, "active_record/coders/yaml_column"
autoload :JSON, "active_record/coders/json"
end
module AttributeMethods
extend ActiveSupport::Autoload
eager_autoload do
autoload :BeforeTypeCast
autoload :Dirty
autoload :PrimaryKey
autoload :Query
autoload :Read
autoload :TimeZoneConversion
autoload :Write
autoload :Serialization
end
end
module Locking
extend ActiveSupport::Autoload
eager_autoload do
autoload :Optimistic
autoload :Pessimistic
end
end
module ConnectionAdapters
extend ActiveSupport::Autoload
eager_autoload do
autoload :AbstractAdapter
end
end
module Scoping
extend ActiveSupport::Autoload
eager_autoload do
autoload :Named
autoload :Default
end
end
module Tasks
extend ActiveSupport::Autoload
autoload :DatabaseTasks
autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
autoload :PostgreSQLDatabaseTasks,
"active_record/tasks/postgresql_database_tasks"
end
autoload :TestDatabases, "active_record/test_databases"
autoload :TestFixtures, "active_record/fixtures"
def self.eager_load!
super
ActiveRecord::Locking.eager_load!
ActiveRecord::Scoping.eager_load!
ActiveRecord::Associations.eager_load!
ActiveRecord::AttributeMethods.eager_load!
ActiveRecord::ConnectionAdapters.eager_load!
end
end
ActiveSupport.on_load(:active_record) do
Arel::Table.engine = self
end
ActiveSupport.on_load(:i18n) do
I18n.load_path << File.expand_path("active_record/locale/en.yml", __dir__)
end
YAML.load_tags["!ruby/object:ActiveRecord::AttributeSet"] = "ActiveModel::AttributeSet"
YAML.load_tags["!ruby/object:ActiveRecord::Attribute::FromDatabase"] = "ActiveModel::Attribute::FromDatabase"
YAML.load_tags["!ruby/object:ActiveRecord::LazyAttributeHash"] = "ActiveModel::LazyAttributeHash"