synerma-apartment 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.pryrc +5 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +79 -0
  6. data/.ruby-version +1 -0
  7. data/Appraisals +182 -0
  8. data/CODE_OF_CONDUCT.md +71 -0
  9. data/Gemfile +20 -0
  10. data/Guardfile +11 -0
  11. data/README.md +671 -0
  12. data/Rakefile +157 -0
  13. data/legacy_CHANGELOG.md +965 -0
  14. data/lib/apartment/active_record/connection_handling.rb +31 -0
  15. data/lib/apartment/active_record/internal_metadata.rb +9 -0
  16. data/lib/apartment/active_record/postgres/schema_dumper.rb +20 -0
  17. data/lib/apartment/active_record/postgresql_adapter.rb +58 -0
  18. data/lib/apartment/active_record/schema_migration.rb +11 -0
  19. data/lib/apartment/adapters/abstract_adapter.rb +275 -0
  20. data/lib/apartment/adapters/abstract_jdbc_adapter.rb +20 -0
  21. data/lib/apartment/adapters/jdbc_mysql_adapter.rb +19 -0
  22. data/lib/apartment/adapters/jdbc_postgresql_adapter.rb +62 -0
  23. data/lib/apartment/adapters/mysql2_adapter.rb +77 -0
  24. data/lib/apartment/adapters/postgis_adapter.rb +13 -0
  25. data/lib/apartment/adapters/postgresql_adapter.rb +280 -0
  26. data/lib/apartment/adapters/sqlite3_adapter.rb +66 -0
  27. data/lib/apartment/adapters/trilogy_adapter.rb +29 -0
  28. data/lib/apartment/console.rb +24 -0
  29. data/lib/apartment/custom_console.rb +42 -0
  30. data/lib/apartment/deprecation.rb +8 -0
  31. data/lib/apartment/elevators/domain.rb +23 -0
  32. data/lib/apartment/elevators/first_subdomain.rb +18 -0
  33. data/lib/apartment/elevators/generic.rb +33 -0
  34. data/lib/apartment/elevators/host.rb +35 -0
  35. data/lib/apartment/elevators/host_hash.rb +26 -0
  36. data/lib/apartment/elevators/subdomain.rb +66 -0
  37. data/lib/apartment/log_subscriber.rb +45 -0
  38. data/lib/apartment/migrator.rb +46 -0
  39. data/lib/apartment/model.rb +29 -0
  40. data/lib/apartment/railtie.rb +68 -0
  41. data/lib/apartment/tasks/enhancements.rb +55 -0
  42. data/lib/apartment/tasks/task_helper.rb +54 -0
  43. data/lib/apartment/tenant.rb +63 -0
  44. data/lib/apartment/version.rb +5 -0
  45. data/lib/apartment.rb +155 -0
  46. data/lib/generators/apartment/install/USAGE +5 -0
  47. data/lib/generators/apartment/install/install_generator.rb +11 -0
  48. data/lib/generators/apartment/install/templates/apartment.rb +116 -0
  49. data/lib/tasks/apartment.rake +106 -0
  50. data/synerma-apartment.gemspec +40 -0
  51. metadata +198 -0
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/log_subscriber'
4
+
5
+ module Apartment
6
+ # Custom Log subscriber to include database name and schema name in sql logs
7
+ class LogSubscriber < ActiveRecord::LogSubscriber
8
+ # NOTE: for some reason, if the method definition is not here, then the custom debug method is not called
9
+ # rubocop:disable Lint/UselessMethodDefinition
10
+ def sql(event)
11
+ super
12
+ end
13
+ # rubocop:enable Lint/UselessMethodDefinition
14
+
15
+ private
16
+
17
+ def debug(progname = nil, &blk)
18
+ progname = " #{apartment_log}#{progname}" unless progname.nil?
19
+
20
+ super
21
+ end
22
+
23
+ def apartment_log
24
+ database = color("[#{database_name}] ", ActiveSupport::LogSubscriber::MAGENTA, bold: true)
25
+ schema = current_search_path
26
+ schema = color("[#{schema.tr('"', '')}] ", ActiveSupport::LogSubscriber::YELLOW, bold: true) unless schema.nil?
27
+ "#{database}#{schema}"
28
+ end
29
+
30
+ def current_search_path
31
+ if Apartment.connection.respond_to?(:schema_search_path)
32
+ Apartment.connection.schema_search_path
33
+ else
34
+ Apartment::Tenant.current # all others
35
+ end
36
+ end
37
+
38
+ def database_name
39
+ db_name = Apartment.connection.raw_connection.try(:db) # PostgreSQL, PostGIS
40
+ db_name ||= Apartment.connection.raw_connection.try(:query_options)&.dig(:database) # Mysql
41
+ db_name ||= Apartment.connection.current_database # Failover
42
+ db_name
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apartment/tenant'
4
+
5
+ module Apartment
6
+ module Migrator
7
+ extend self
8
+
9
+ # Migrate to latest
10
+ def migrate(database)
11
+ Tenant.switch(database) do
12
+ version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
13
+
14
+ migration_scope_block = ->(migration) { ENV['SCOPE'].blank? || (ENV['SCOPE'] == migration.scope) }
15
+
16
+ if ActiveRecord.version >= Gem::Version.new('7.2.0')
17
+ ActiveRecord::Base.connection_pool.migration_context.migrate(version, &migration_scope_block)
18
+ else
19
+ ActiveRecord::Base.connection.migration_context.migrate(version, &migration_scope_block)
20
+ end
21
+ end
22
+ end
23
+
24
+ # Migrate up/down to a specific version
25
+ def run(direction, database, version)
26
+ Tenant.switch(database) do
27
+ if ActiveRecord.version >= Gem::Version.new('7.2.0')
28
+ ActiveRecord::Base.connection_pool.migration_context.run(direction, version)
29
+ else
30
+ ActiveRecord::Base.connection.migration_context.run(direction, version)
31
+ end
32
+ end
33
+ end
34
+
35
+ # rollback latest migration `step` number of times
36
+ def rollback(database, step = 1)
37
+ Tenant.switch(database) do
38
+ if ActiveRecord.version >= Gem::Version.new('7.2.0')
39
+ ActiveRecord::Base.connection_pool.migration_context.rollback(step)
40
+ else
41
+ ActiveRecord::Base.connection.migration_context.rollback(step)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apartment
4
+ module Model
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods
8
+ # NOTE: key can either be an array of symbols or a single value.
9
+ # E.g. If we run the following query:
10
+ # `Setting.find_by(key: 'something', value: 'amazing')` key will have an array of symbols: `[:key, :something]`
11
+ # while if we run:
12
+ # `Setting.find(10)` key will have the value 'id'
13
+ def cached_find_by_statement(key, &block)
14
+ # Modifying the cache key to have a reference to the current tenant,
15
+ # so the cached statement is referring only to the tenant in which we've
16
+ # executed this
17
+ cache_key = if key.is_a? String
18
+ "#{Apartment::Tenant.current}_#{key}"
19
+ else
20
+ # NOTE: In Rails 6.0.4 we start receiving an ActiveRecord::Reflection::BelongsToReflection
21
+ # as the key, which wouldn't work well with an array.
22
+ [Apartment::Tenant.current] + Array.wrap(key)
23
+ end
24
+ cache = @find_by_statement_cache[connection.prepared_statements]
25
+ cache.compute_if_absent(cache_key) { ActiveRecord::StatementCache.create(connection, &block) }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'apartment/tenant'
5
+
6
+ module Apartment
7
+ class Railtie < Rails::Railtie
8
+ #
9
+ # Set up our default config options
10
+ # Do this before the app initializers run so we don't override custom settings
11
+ #
12
+ config.before_initialize do
13
+ Apartment.configure do |config|
14
+ config.excluded_models = []
15
+ config.use_schemas = true
16
+ config.tenant_names = []
17
+ config.seed_after_create = false
18
+ config.prepend_environment = false
19
+ config.append_environment = false
20
+ config.tenant_presence_check = true
21
+ config.active_record_log = false
22
+ end
23
+
24
+ ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
25
+ end
26
+
27
+ # Hook into ActionDispatch::Reloader to ensure Apartment is properly initialized
28
+ # Note that this doesn't entirely work as expected in Development,
29
+ # because this is called before classes are reloaded
30
+ # See the middleware/console declarations below to help with this. Hope to fix that soon.
31
+ #
32
+ config.to_prepare do
33
+ next if ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ }
34
+ next if ARGV.any?('webpacker:compile')
35
+ next if ENV['APARTMENT_DISABLE_INIT']
36
+
37
+ begin
38
+ Apartment.connection_class.connection_pool.with_connection do
39
+ Apartment::Tenant.init
40
+ end
41
+ rescue ::ActiveRecord::NoDatabaseError
42
+ # Since `db:create` and other tasks invoke this block from Rails 5.2.0,
43
+ # we need to swallow the error to execute `db:create` properly.
44
+ end
45
+ end
46
+
47
+ config.after_initialize do
48
+ # NOTE: Load the custom log subscriber if enabled
49
+ if Apartment.active_record_log
50
+ ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener|
51
+ next unless listener.instance_variable_get('@delegate').is_a?(ActiveRecord::LogSubscriber)
52
+
53
+ ActiveSupport::Notifications.unsubscribe listener
54
+ end
55
+
56
+ Apartment::LogSubscriber.attach_to :active_record
57
+ end
58
+ end
59
+
60
+ #
61
+ # Ensure rake tasks are loaded
62
+ #
63
+ rake_tasks do
64
+ load 'tasks/apartment.rake'
65
+ require 'apartment/tasks/enhancements' if Apartment.db_migrate_tenants
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Require this file to append Apartment rake tasks to ActiveRecord db rake tasks
4
+ # Enabled by default in the initializer
5
+
6
+ module Apartment
7
+ class RakeTaskEnhancer
8
+ module TASKS
9
+ ENHANCE_BEFORE = %w[db:drop].freeze
10
+ ENHANCE_AFTER = %w[db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed].freeze
11
+ freeze
12
+ end
13
+
14
+ # This is a bit convoluted, but helps solve problems when using Apartment within an engine
15
+ # See spec/integration/use_within_an_engine.rb
16
+
17
+ class << self
18
+ def enhance!
19
+ return unless should_enhance?
20
+
21
+ # insert task before
22
+ TASKS::ENHANCE_BEFORE.each do |name|
23
+ task = Rake::Task[name]
24
+ enhance_before_task(task)
25
+ end
26
+
27
+ # insert task after
28
+ TASKS::ENHANCE_AFTER.each do |name|
29
+ task = Rake::Task[name]
30
+ enhance_after_task(task)
31
+ end
32
+ end
33
+
34
+ def should_enhance?
35
+ Apartment.db_migrate_tenants
36
+ end
37
+
38
+ def enhance_before_task(task)
39
+ task.enhance([inserted_task_name(task)])
40
+ end
41
+
42
+ def enhance_after_task(task)
43
+ task.enhance do
44
+ Rake::Task[inserted_task_name(task)].invoke
45
+ end
46
+ end
47
+
48
+ def inserted_task_name(task)
49
+ task.name.sub(/db:/, 'apartment:')
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ Apartment::RakeTaskEnhancer.enhance!
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apartment
4
+ module TaskHelper
5
+ def self.each_tenant(&block)
6
+ Parallel.each(tenants_without_default, in_threads: Apartment.parallel_migration_threads) do |tenant|
7
+ Rails.application.executor.wrap do
8
+ block.call(tenant)
9
+ end
10
+ end
11
+ end
12
+
13
+ def self.tenants_without_default
14
+ tenants - [Apartment.default_tenant]
15
+ end
16
+
17
+ def self.tenants
18
+ ENV['DB'] ? ENV['DB'].split(',').map(&:strip) : Apartment.tenant_names || []
19
+ end
20
+
21
+ def self.warn_if_tenants_empty
22
+ return unless tenants.empty? && ENV['IGNORE_EMPTY_TENANTS'] != 'true'
23
+
24
+ puts <<-WARNING
25
+ [WARNING] - The list of tenants to migrate appears to be empty. This could mean a few things:
26
+
27
+ 1. You may not have created any, in which case you can ignore this message
28
+ 2. You've run `apartment:migrate` directly without loading the Rails environment
29
+ * `apartment:migrate` is now deprecated. Tenants will automatically be migrated with `db:migrate`
30
+
31
+ Note that your tenants currently haven't been migrated. You'll need to run `db:migrate` to rectify this.
32
+ WARNING
33
+ end
34
+
35
+ def self.create_tenant(tenant_name)
36
+ puts("Creating #{tenant_name} tenant")
37
+ Apartment::Tenant.create(tenant_name)
38
+ rescue Apartment::TenantExists => e
39
+ puts "Tried to create already existing tenant: #{e}"
40
+ end
41
+
42
+ def self.migrate_tenant(tenant_name)
43
+ strategy = Apartment.db_migrate_tenant_missing_strategy
44
+ create_tenant(tenant_name) if strategy == :create_tenant
45
+
46
+ puts("Migrating #{tenant_name} tenant")
47
+ Apartment::Migrator.migrate tenant_name
48
+ rescue Apartment::TenantNotFound => e
49
+ raise e if strategy == :raise_exception
50
+
51
+ puts e.message
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Apartment
6
+ # The main entry point to Apartment functions
7
+ #
8
+ module Tenant
9
+ extend self
10
+ extend Forwardable
11
+
12
+ def_delegators :adapter, :create, :drop, :switch, :switch!, :current, :each,
13
+ :reset, :init, :set_callback, :seed, :default_tenant, :environmentify
14
+
15
+ attr_writer :config
16
+
17
+ # Fetch the proper multi-tenant adapter based on Rails config
18
+ #
19
+ # @return {subclass of Apartment::AbstractAdapter}
20
+ #
21
+ def adapter
22
+ Thread.current[:apartment_adapter] ||= begin
23
+ adapter_method = "#{config[:adapter]}_adapter"
24
+
25
+ if defined?(JRUBY_VERSION)
26
+ case config[:adapter]
27
+ when /mysql/
28
+ adapter_method = 'jdbc_mysql_adapter'
29
+ when /postgresql/
30
+ adapter_method = 'jdbc_postgresql_adapter'
31
+ end
32
+ end
33
+
34
+ begin
35
+ require "apartment/adapters/#{adapter_method}"
36
+ rescue LoadError
37
+ raise "The adapter `#{adapter_method}` is not yet supported"
38
+ end
39
+
40
+ unless respond_to?(adapter_method)
41
+ raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter"
42
+ end
43
+
44
+ send(adapter_method, config)
45
+ end
46
+ end
47
+
48
+ # Reset config and adapter so they are regenerated
49
+ #
50
+ def reload!(config = nil)
51
+ Thread.current[:apartment_adapter] = nil
52
+ @config = config
53
+ end
54
+
55
+ private
56
+
57
+ # Fetch the rails database configuration
58
+ #
59
+ def config
60
+ @config ||= Apartment.connection_config
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apartment
4
+ VERSION = '3.1.0'
5
+ end
data/lib/apartment.rb ADDED
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apartment/railtie' if defined?(Rails)
4
+ require 'active_support/core_ext/object/blank'
5
+ require 'forwardable'
6
+ require 'active_record'
7
+ require 'apartment/tenant'
8
+ require 'apartment/deprecation'
9
+
10
+ require_relative 'apartment/log_subscriber'
11
+ require_relative 'apartment/active_record/connection_handling'
12
+ require_relative 'apartment/active_record/schema_migration'
13
+ require_relative 'apartment/active_record/internal_metadata'
14
+
15
+ if ActiveRecord.version.release >= Gem::Version.new('7.1')
16
+ require_relative 'apartment/active_record/postgres/schema_dumper'
17
+ end
18
+
19
+ # Apartment main definitions
20
+ module Apartment
21
+ class << self
22
+ extend Forwardable
23
+
24
+ ACCESSOR_METHODS = %i[use_schemas use_sql seed_after_create prepend_environment default_tenant
25
+ append_environment with_multi_server_setup tenant_presence_check
26
+ active_record_log pg_exclude_clone_tables].freeze
27
+
28
+ WRITER_METHODS = %i[tenant_names database_schema_file excluded_models
29
+ persistent_schemas connection_class
30
+ db_migrate_tenants db_migrate_tenant_missing_strategy seed_data_file
31
+ parallel_migration_threads pg_excluded_names].freeze
32
+
33
+ attr_accessor(*ACCESSOR_METHODS)
34
+ attr_writer(*WRITER_METHODS)
35
+
36
+ def_delegators :connection_class, :connection, :connection_db_config, :establish_connection
37
+
38
+ def connection_config
39
+ connection_db_config.configuration_hash
40
+ end
41
+
42
+ # configure apartment with available options
43
+ def configure
44
+ yield self if block_given?
45
+ end
46
+
47
+ def tenant_names
48
+ extract_tenant_config.keys.map(&:to_s)
49
+ end
50
+
51
+ def tenants_with_config
52
+ extract_tenant_config
53
+ end
54
+
55
+ def tld_length=(_)
56
+ Apartment::DEPRECATOR.warn('`config.tld_length` have no effect because it was removed in https://github.com/influitive/apartment/pull/309')
57
+ end
58
+
59
+ def db_config_for(tenant)
60
+ (tenants_with_config[tenant] || connection_config)
61
+ end
62
+
63
+ # Whether or not db:migrate should also migrate tenants
64
+ # defaults to true
65
+ def db_migrate_tenants
66
+ return @db_migrate_tenants if defined?(@db_migrate_tenants)
67
+
68
+ @db_migrate_tenants = true
69
+ end
70
+
71
+ # How to handle tenant missing on db:migrate
72
+ # defaults to :rescue_exception
73
+ # available options: rescue_exception, raise_exception, create_tenant
74
+ def db_migrate_tenant_missing_strategy
75
+ valid = %i[rescue_exception raise_exception create_tenant]
76
+ value = @db_migrate_tenant_missing_strategy || :rescue_exception
77
+
78
+ return value if valid.include?(value)
79
+
80
+ key_name = 'config.db_migrate_tenant_missing_strategy'
81
+ opt_names = valid.join(', ')
82
+
83
+ raise ApartmentError, "Option #{value} not valid for `#{key_name}`. Use one of #{opt_names}"
84
+ end
85
+
86
+ # Default to empty array
87
+ def excluded_models
88
+ @excluded_models || []
89
+ end
90
+
91
+ def parallel_migration_threads
92
+ @parallel_migration_threads || 0
93
+ end
94
+
95
+ def persistent_schemas
96
+ @persistent_schemas || []
97
+ end
98
+
99
+ def connection_class
100
+ @connection_class || ActiveRecord::Base
101
+ end
102
+
103
+ def database_schema_file
104
+ return @database_schema_file if defined?(@database_schema_file)
105
+
106
+ @database_schema_file = Rails.root.join('db/schema.rb')
107
+ end
108
+
109
+ def seed_data_file
110
+ return @seed_data_file if defined?(@seed_data_file)
111
+
112
+ @seed_data_file = Rails.root.join('db/seeds.rb')
113
+ end
114
+
115
+ def pg_excluded_names
116
+ @pg_excluded_names || []
117
+ end
118
+
119
+ # Reset all the config for Apartment
120
+ def reset
121
+ (ACCESSOR_METHODS + WRITER_METHODS).each do |method|
122
+ remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}")
123
+ end
124
+ end
125
+
126
+ def extract_tenant_config
127
+ return {} unless @tenant_names
128
+
129
+ values = @tenant_names.respond_to?(:call) ? @tenant_names.call : @tenant_names
130
+ unless values.is_a? Hash
131
+ values = values.each_with_object({}) do |tenant, hash|
132
+ hash[tenant] = connection_config
133
+ end
134
+ end
135
+ values.with_indifferent_access
136
+ rescue ActiveRecord::StatementInvalid
137
+ {}
138
+ end
139
+ end
140
+
141
+ # Exceptions
142
+ ApartmentError = Class.new(StandardError)
143
+
144
+ # Raised when apartment cannot find the adapter specified in <tt>config/database.yml</tt>
145
+ AdapterNotFound = Class.new(ApartmentError)
146
+
147
+ # Raised when apartment cannot find the file to be loaded
148
+ FileNotFound = Class.new(ApartmentError)
149
+
150
+ # Tenant specified is unknown
151
+ TenantNotFound = Class.new(ApartmentError)
152
+
153
+ # The Tenant attempting to be created already exists
154
+ TenantExists = Class.new(ApartmentError)
155
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Creates an initializer for apartment.
3
+
4
+ Example:
5
+ `rails generate apartment:install`
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apartment
4
+ class InstallGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ def copy_files
8
+ template 'apartment.rb', File.join('config', 'initializers', 'apartment.rb')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ # You can have Apartment route to the appropriate Tenant by adding some Rack middleware.
4
+ # Apartment can support many different "Elevators" that can take care of this routing to your data.
5
+ # Require whichever Elevator you're using below or none if you have a custom one.
6
+ #
7
+ # require 'apartment/elevators/generic'
8
+ # require 'apartment/elevators/domain'
9
+ require 'apartment/elevators/subdomain'
10
+ # require 'apartment/elevators/first_subdomain'
11
+ # require 'apartment/elevators/host'
12
+
13
+ #
14
+ # Apartment Configuration
15
+ #
16
+ Apartment.configure do |config|
17
+ # Add any models that you do not want to be multi-tenanted, but remain in the global (public) namespace.
18
+ # A typical example would be a Customer or Tenant model that stores each Tenant's information.
19
+ #
20
+ # config.excluded_models = %w{ Tenant }
21
+
22
+ # In order to migrate all of your Tenants you need to provide a list of Tenant names to Apartment.
23
+ # You can make this dynamic by providing a Proc object to be called on migrations.
24
+ # This object should yield either:
25
+ # - an array of strings representing each Tenant name.
26
+ # - a hash which keys are tenant names, and values custom db config
27
+ # (must contain all key/values required in database.yml)
28
+ #
29
+ # config.tenant_names = lambda{ Customer.pluck(:tenant_name) }
30
+ # config.tenant_names = ['tenant1', 'tenant2']
31
+ # config.tenant_names = {
32
+ # 'tenant1' => {
33
+ # adapter: 'postgresql',
34
+ # host: 'some_server',
35
+ # port: 5555,
36
+ # database: 'postgres' # this is not the name of the tenant's db
37
+ # # but the name of the database to connect to before creating the tenant's db
38
+ # # mandatory in postgresql
39
+ # },
40
+ # 'tenant2' => {
41
+ # adapter: 'postgresql',
42
+ # database: 'postgres' # this is not the name of the tenant's db
43
+ # # but the name of the database to connect to before creating the tenant's db
44
+ # # mandatory in postgresql
45
+ # }
46
+ # }
47
+ # config.tenant_names = lambda do
48
+ # Tenant.all.each_with_object({}) do |tenant, hash|
49
+ # hash[tenant.name] = tenant.db_configuration
50
+ # end
51
+ # end
52
+ #
53
+ config.tenant_names = -> { ToDo_Tenant_Or_User_Model.pluck :database }
54
+
55
+ # PostgreSQL:
56
+ # Specifies whether to use PostgreSQL schemas or create a new database per Tenant.
57
+ #
58
+ # MySQL:
59
+ # Specifies whether to switch databases by using `use` statement or re-establish connection.
60
+ #
61
+ # The default behaviour is true.
62
+ #
63
+ # config.use_schemas = true
64
+
65
+ #
66
+ # ==> PostgreSQL only options
67
+
68
+ # Apartment can be forced to use raw SQL dumps instead of schema.rb for creating new schemas.
69
+ # Use this when you are using some extra features in PostgreSQL that can't be represented in
70
+ # schema.rb, like materialized views etc. (only applies with use_schemas set to true).
71
+ # (Note: this option doesn't use db/structure.sql, it creates SQL dump by executing pg_dump)
72
+ #
73
+ # config.use_sql = false
74
+
75
+ # There are cases where you might want some schemas to always be in your search_path
76
+ # e.g when using a PostgreSQL extension like hstore.
77
+ # Any schemas added here will be available along with your selected Tenant.
78
+ #
79
+ # config.persistent_schemas = %w{ hstore }
80
+
81
+ # <== PostgreSQL only options
82
+ #
83
+
84
+ # By default, and only when not using PostgreSQL schemas, Apartment will prepend the environment
85
+ # to the tenant name to ensure there is no conflict between your environments.
86
+ # This is mainly for the benefit of your development and test environments.
87
+ # Uncomment the line below if you want to disable this behaviour in production.
88
+ #
89
+ # config.prepend_environment = !Rails.env.production?
90
+
91
+ # When using PostgreSQL schemas, the database dump will be namespaced, and
92
+ # apartment will substitute the default namespace (usually public) with the
93
+ # name of the new tenant when creating a new tenant. Some items must maintain
94
+ # a reference to the default namespace (ie public) - for instance, a default
95
+ # uuid generation. Uncomment the line below to create a list of namespaced
96
+ # items in the schema dump that should *not* have their namespace replaced by
97
+ # the new tenant
98
+ #
99
+ # config.pg_excluded_names = ["uuid_generate_v4"]
100
+
101
+ # Specifies whether the database and schema (when using PostgreSQL schemas) will prepend in ActiveRecord log.
102
+ # Uncomment the line below if you want to enable this behavior.
103
+ #
104
+ # config.active_record_log = true
105
+ end
106
+
107
+ # Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that
108
+ # you want to switch to.
109
+ # Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request|
110
+ # request.host.split('.').first
111
+ # }
112
+
113
+ # Rails.application.config.middleware.use Apartment::Elevators::Domain
114
+ Rails.application.config.middleware.use Apartment::Elevators::Subdomain
115
+ # Rails.application.config.middleware.use Apartment::Elevators::FirstSubdomain
116
+ # Rails.application.config.middleware.use Apartment::Elevators::Host