class Apartment::Adapters::AbstractAdapter

Attributes

default_tenant[W]

Public Class Methods

new(config) click to toggle source

@constructor @param {Hash} config Database config

# File lib/apartment/adapters/abstract_adapter.rb, line 12
def initialize(config)
  @config = config
end

Public Instance Methods

create(tenant) { || ... } click to toggle source

Create a new tenant, import schema, seed if appropriate

@param {String} tenant Tenant name

# File lib/apartment/adapters/abstract_adapter.rb, line 20
def create(tenant)
  run_callbacks :create do
    create_tenant(tenant)

    switch(tenant) do
      import_database_schema

      # Seed data if appropriate
      seed_data if Apartment.seed_after_create

      yield if block_given?
    end
  end
end
current() click to toggle source

Note alias_method here doesn't work with inheritence apparently ??

# File lib/apartment/adapters/abstract_adapter.rb, line 37
def current
  Apartment.connection.current_database
end
default_schema()
Alias for: default_tenant
default_tenant() click to toggle source

Return the original public tenant

@return {String} default tenant name

# File lib/apartment/adapters/abstract_adapter.rb, line 45
def default_tenant
  @default_tenant || Apartment.default_tenant
end
Also aliased as: default_schema
drop(tenant) click to toggle source

Drop the tenant

@param {String} tenant name

# File lib/apartment/adapters/abstract_adapter.rb, line 54
def drop(tenant)
  with_neutral_connection(tenant) do |conn|
    drop_command(conn, tenant)
  end

rescue *rescuable_exceptions => exception
  raise_drop_tenant_error!(tenant, exception)
end
each(tenants = Apartment.tenant_names) { |tenant| ... } click to toggle source

Iterate over all tenants, switch to tenant and yield tenant name

# File lib/apartment/adapters/abstract_adapter.rb, line 94
def each(tenants = Apartment.tenant_names)
  tenants.each do |tenant|
    switch(tenant){ yield tenant }
  end
end
environmentify(tenant) click to toggle source

Prepend the environment if configured and the environment isn't already there

@param {String} tenant Database name @return {String} tenant name with Rails environment optionally prepended

# File lib/apartment/adapters/abstract_adapter.rb, line 128
def environmentify(tenant)
  unless tenant.include?(Rails.env)
    if Apartment.prepend_environment
      "#{Rails.env}_#{tenant}"
    elsif Apartment.append_environment
      "#{tenant}_#{Rails.env}"
    else
      tenant
    end
  else
    tenant
  end
end
process_excluded_models() click to toggle source

Establish a new connection for each specific excluded model

# File lib/apartment/adapters/abstract_adapter.rb, line 102
def process_excluded_models
  # All other models will shared a connection (at Apartment.connection_class) and we can modify at will
  Apartment.excluded_models.each do |excluded_model|
    process_excluded_model(excluded_model)
  end
end
reset() click to toggle source

Reset the tenant connection to the default

# File lib/apartment/adapters/abstract_adapter.rb, line 111
def reset
  Apartment.establish_connection @config
end
seed()
Alias for: seed_data
seed_data() click to toggle source

Load the rails seed file into the db

# File lib/apartment/adapters/abstract_adapter.rb, line 117
def seed_data
  # Don't log the output of seeding the db
  silence_warnings{ load_or_raise(Apartment.seed_data_file) } if Apartment.seed_data_file
end
Also aliased as: seed
switch(tenant = nil) { || ... } click to toggle source

Connect to tenant, do your biz, switch back to previous tenant

@param {String?} tenant to connect to

# File lib/apartment/adapters/abstract_adapter.rb, line 81
def switch(tenant = nil)
  begin
    previous_tenant = current
    switch!(tenant)
    yield

  ensure
    switch!(previous_tenant) rescue reset
  end
end
switch!(tenant = nil) click to toggle source

Switch to a new tenant

@param {String} tenant name

# File lib/apartment/adapters/abstract_adapter.rb, line 67
def switch!(tenant = nil)
  run_callbacks :switch do
    return reset if tenant.nil?

    connect_to_new(tenant).tap do
      Apartment.connection.clear_query_cache
    end
  end
end

Protected Instance Methods

connect_to_new(tenant) click to toggle source

Connect to new tenant

@param {String} tenant Database name

# File lib/apartment/adapters/abstract_adapter.rb, line 173
def connect_to_new(tenant)
  query_cache_enabled = ActiveRecord::Base.connection.query_cache_enabled

  Apartment.establish_connection multi_tenantify(tenant)
  Apartment.connection.active?   # call active? to manually check if this connection is valid

  Apartment.connection.enable_query_cache! if query_cache_enabled
rescue *rescuable_exceptions => exception
  Apartment::Tenant.reset if reset_on_connection_exception?
  raise_connect_error!(tenant, exception)
end
create_tenant(tenant) click to toggle source

Create the tenant

@param {String} tenant Database name

# File lib/apartment/adapters/abstract_adapter.rb, line 157
def create_tenant(tenant)
  with_neutral_connection(tenant) do |conn|
    create_tenant_command(conn, tenant)
  end
rescue *rescuable_exceptions => exception
  raise_create_tenant_error!(tenant, exception)
end
create_tenant_command(conn, tenant) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 165
def create_tenant_command(conn, tenant)
  conn.create_database(environmentify(tenant), @config)
end
db_connection_config(tenant) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 233
def db_connection_config(tenant)
  Apartment.db_config_for(tenant).clone
end
drop_command(conn, tenant) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 148
def drop_command(conn, tenant)
  # connection.drop_database   note that drop_database will not throw an exception, so manually execute
  conn.execute("DROP DATABASE #{conn.quote_table_name(environmentify(tenant))}")
end
import_database_schema() click to toggle source

Import the database schema

# File lib/apartment/adapters/abstract_adapter.rb, line 187
def import_database_schema
  ActiveRecord::Schema.verbose = false    # do not log schema load output.

  load_or_raise(Apartment.database_schema_file) if Apartment.database_schema_file
end
load_or_abort(file)

Backward compatibility

Alias for: load_or_raise
load_or_raise(file) click to toggle source

Load a file or raise error if it doesn't exists

# File lib/apartment/adapters/abstract_adapter.rb, line 211
def load_or_raise(file)
  if File.exists?(file)
    load(file)
  else
    raise FileNotFound, "#{file} doesn't exist yet"
  end
end
Also aliased as: load_or_abort
multi_tenantify(tenant, with_database = true) click to toggle source

Return a new config that is multi-tenanted @param {String} tenant: Database name @param {Boolean} with_database: if true, use the actual tenant's db name

if false, use the default db name from the db
# File lib/apartment/adapters/abstract_adapter.rb, line 197
def multi_tenantify(tenant, with_database = true)
  db_connection_config(tenant).tap do |config|
    if with_database
      multi_tenantify_with_tenant_db_name(config, tenant)
    end
  end
end
multi_tenantify_with_tenant_db_name(config, tenant) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 205
def multi_tenantify_with_tenant_db_name(config, tenant)
  config[:database] = environmentify(tenant)
end
process_excluded_model(excluded_model) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 144
def process_excluded_model(excluded_model)
  excluded_model.constantize.establish_connection @config
end
raise_connect_error!(tenant, exception) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 261
def raise_connect_error!(tenant, exception)
  raise TenantNotFound, "Error while connecting to tenant #{environmentify(tenant)}: #{ exception.message }"
end
raise_create_tenant_error!(tenant, exception) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 257
def raise_create_tenant_error!(tenant, exception)
  raise TenantExists, "Error while creating tenant #{environmentify(tenant)}: #{ exception.message }"
end
raise_drop_tenant_error!(tenant, exception) click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 253
def raise_drop_tenant_error!(tenant, exception)
  raise TenantNotFound, "Error while dropping tenant #{environmentify(tenant)}: #{ exception.message }"
end
rescuable_exceptions() click to toggle source

Exceptions to rescue from on db operations

# File lib/apartment/adapters/abstract_adapter.rb, line 223
def rescuable_exceptions
  [ActiveRecord::ActiveRecordError] + Array(rescue_from)
end
rescue_from() click to toggle source

Extra exceptions to rescue from

# File lib/apartment/adapters/abstract_adapter.rb, line 229
def rescue_from
  []
end
reset_on_connection_exception?() click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 249
def reset_on_connection_exception?
  false
end
with_neutral_connection(tenant) { |connection| ... } click to toggle source
# File lib/apartment/adapters/abstract_adapter.rb, line 237
def with_neutral_connection(tenant, &block)
   if Apartment.with_multi_server_setup
     # neutral connection is necessary whenever you need to create/remove a database from a server.
     # example: when you use postgresql, you need to connect to the default postgresql database before you create your own.
     SeparateDbConnectionHandler.establish_connection(multi_tenantify(tenant, false))
     yield(SeparateDbConnectionHandler.connection)
     SeparateDbConnectionHandler.connection.close
   else
     yield(Apartment.connection)
   end
 end