README.md in apartment-2.0.0 vs README.md in apartment-2.1.0

- old
+ new

@@ -92,17 +92,22 @@ ### Switching Tenants To switch tenants using Apartment, use the following command: ```ruby -Apartment::Tenant.switch!('tenant_name') +Apartment::Tenant.switch('tenant_name') do + # ... +end ``` When switch is called, all requests coming to ActiveRecord will be routed to the tenant -you specify (with the exception of excluded models, see below). To return to the 'root' -tenant, call switch with no arguments. +you specify (with the exception of excluded models, see below). The tenant is automatically +switched back at the end of the block to what it was before. +There is also `switch!` which doesn't take a block, but it's recommended to use `switch`. +To return to the default tenant, you can call `switch` with no arguments. + ### Switching Tenants per request You can have Apartment route to the appropriate tenant by adding some Rack middleware. Apartment can support many different "Elevators" that can take care of this routing to your data. @@ -115,11 +120,11 @@ elevator below. Note that in this case you will **need** to require the elevator manually in your `application.rb` like so ```ruby # config/application.rb -require 'apartment/elevators/subdomain' # or 'domain' or 'generic' +require 'apartment/elevators/subdomain' # or 'domain', 'first_subdomain', 'host' ``` #### Switch on subdomain In house, we use the subdomain elevator, which analyzes the subdomain of the request and switches to a tenant schema of the same name. It can be used like so: @@ -153,32 +158,37 @@ config.middleware.use Apartment::Elevators::FirstSubdomain end end ``` -If you want to exclude a domain, for example if you don't want your application to treate www like a subdomain, in an initializer in your application, you can set the following: +If you want to exclude a domain, for example if you don't want your application to treat www like a subdomain, in an initializer in your application, you can set the following: ```ruby # config/initializers/apartment/subdomain_exclusions.rb Apartment::Elevators::FirstSubdomain.excluded_subdomains = ['www'] ``` This functions much in the same way as the Subdomain elevator. **NOTE:** in fact, at the time of this writing, the `Subdomain` and `FirstSubdomain` elevators both use the first subdomain ([#339](https://github.com/influitive/apartment/issues/339#issuecomment-235578610)). If you need to switch on larger parts of a Subdomain, consider using a Custom Elevator. #### Switch on domain -To switch based on full domain (excluding subdomains *ie 'www'* and top level domains *ie '.com'* ) use the following: +To switch based on full domain (excluding the 'www' subdomains and top level domains *ie '.com'* ) use the following: ```ruby # application.rb module MyApplication class Application < Rails::Application config.middleware.use Apartment::Elevators::Domain end end ``` +Note that if you have several subdomains, then it will match on the first *non-www* subdomain: +- example.com => example +- www.example.com => example +- a.example.com => a + #### Switch on full host using a hash To switch based on full host with a hash to find corresponding tenant name use the following: ```ruby @@ -188,10 +198,35 @@ config.middleware.use Apartment::Elevators::HostHash, {'example.com' => 'example_tenant'} end end ``` +#### Switch on full host, ignoring given first subdomains + +To switch based on full host to find corresponding tenant name use the following: + +```ruby +# application.rb +module MyApplication + class Application < Rails::Application + config.middleware.use Apartment::Elevators::Host + end +end +``` + +If you want to exclude a first-subdomain, for example if you don't want your application to include www in the matching, in an initializer in your application, you can set the following: + +```ruby +Apartment::Elevators::Host.ignored_first_subdomains = ['www'] +``` + +With the above set, these would be the results: +- example.com => example.com +- www.example.com => example.com +- a.example.com => a.example.com +- www.a.example.com => a.example.com + #### Custom Elevator A Generic Elevator exists that allows you to pass a `Proc` (or anything that responds to `call`) to the middleware. This Object will be passed in an `ActionDispatch::Request` object when called for you to do your magic. Apartment will use the return value of this proc to switch to the appropriate tenant. Use like so: ```ruby @@ -242,11 +277,11 @@ This works okay for simple applications, but it's important to consider that you may want to maintain the "selected" tenant through different parts of the Rack application stack. For example, the [Devise](https://github.com/plataformatec/devise) gem adds the `Warden::Manager` middleware at the end of the stack in the examples above, our `Apartment::Elevators::Subdomain` middleware would come after it. Trouble is, Apartment resets the selected tenant after the request is finish, so some redirects (e.g. authentication) in Devise will be run in the context of the "public" tenant. The same issue would also effect a gem such as the [better_errors](https://github.com/charliesome/better_errors) gem which inserts a middleware quite early in the Rails middleware stack. To resolve this issue, consider adding the Apartment middleware at a location in the Rack stack that makes sense for your needs, e.g.: ```ruby -Rails.application.config.middleware.insert_before 'Warden::Manager', 'Apartment::Elevators::Subdomain' +Rails.application.config.middleware.insert_before Warden::Manager, Apartment::Elevators::Subdomain ``` Now work done in the Warden middleware is wrapped in the `Apartment::Tenant.switch` context started in the Generic elevator. ### Dropping Tenants @@ -340,10 +375,12 @@ ActiveRecord::Base.connection.execute 'CREATE SCHEMA IF NOT EXISTS shared_extensions;' # Enable Hstore ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE SCHEMA shared_extensions;' # Enable UUID-OSSP ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA shared_extensions;' + # Grant usage to public + ActiveRecord::Base.connection.execute 'GRANT usage ON SCHEMA shared_extensions to public;' end end Rake::Task["db:create"].enhance do Rake::Task["db:extensions"].invoke @@ -441,10 +478,21 @@ Note that you can disable the default migrating of all tenants with `db:migrate` by setting `Apartment.db_migrate_tenants = false` in your `Rakefile`. Note this must be done *before* the rake tasks are loaded. ie. before `YourApp::Application.load_tasks` is called +#### Parallel Migrations + +Apartment supports parallelizing migrations into multiple threads when +you have a large number of tenants. By default, parallel migrations is +turned off. You can enable this by setting `parallel_migration_threads` to +the number of threads you want to use in your initializer. + +Keep in mind that because migrations are going to access the database, +the number of threads indicated here should be less than the pool size +that Rails will use to connect to your database. + ### Handling Environments By default, when not using postgresql schemas, Apartment will prepend the environment to the tenant name to ensure there is no conflict between your environments. This is mainly for the benefit of your development and test environments. If you wish to turn this option off in production, you could do something like: @@ -479,12 +527,12 @@ hash[tenant.name] = tenant.db_configuration end end ``` -## Delayed::Job +## Background workers -Has been removed. See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) for a better backgrounding experience. +See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) or [apartment-activejob](https://github.com/influitive/apartment-activejob). ## Contributing * In both `spec/dummy/config` and `spec/config`, you will see `database.yml.sample` files * Copy them into the same directory but with the name `database.yml`