activerecord-tenanted 0.2.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8caa70e9efec8b621843583547b9c3b2849bbe6923789c68944bc3dd2de4da8
4
- data.tar.gz: 6c6e9bb4e6b7a879c8d6c9ad8e6ee441493aa5b06cbb35f0e3254481555cd3d9
3
+ metadata.gz: 0c816167bb3121714715c597882f45c9b586ac562545b344417038a7cfcc61f0
4
+ data.tar.gz: 2f948cd900f8b8728fb02b05209cb85fa1da86340ecd0819de3e56db0ea5f618
5
5
  SHA512:
6
- metadata.gz: 48772fda086b35feb27532973e33d8284372af39bba95c0e69c8386ac8a8b893d4970d02fd1b47fe5fd76d5af948e845564885c3bcbf6c70ec3dc010952748d7
7
- data.tar.gz: 4b0b7cccadd0a5f2acd373b0b152d81c5a632b6ed4bfdf22371300fb5809fccc150202f87fc3b28c05afe50e1c35f3907fcd34b856406d1dff9077fca2d8e180
6
+ metadata.gz: 29d74ec62e44edbfdd28dbad5a708b5bdd4816e88b758bc48a4b6244f2ca9641138a8ac3d25aeae84b9e901c17ca748d6a42016c3df2ae9d4e6818d0738a4f0e
7
+ data.tar.gz: ec01de5118d1c22ded517ab0228789664fa6864260c315f1ec4c51f0d198bc3507af7e373bc02e40d8f9fbef5a4d594d9c5609d6dbd435fdbbbd0691422de029
data/Rakefile CHANGED
@@ -7,3 +7,13 @@ require "bundler/gem_tasks"
7
7
  task :clean do
8
8
  FileUtils.rm_f(Dir.glob("test/dummy/log/*.log"), verbose: true)
9
9
  end
10
+
11
+ desc "Regenerate tables of contents in some files"
12
+ task "toc" do
13
+ require "mkmf"
14
+ if find_executable0("markdown-toc")
15
+ sh "markdown-toc --maxdepth=3 -i GUIDE.md"
16
+ else
17
+ puts "WARN: cannot find markdown-toc, skipping. install with 'npm install markdown-toc'"
18
+ end
19
+ end
@@ -8,9 +8,6 @@ module ActiveRecord
8
8
  class_methods do
9
9
  def initialize(...)
10
10
  super
11
-
12
- @tenanted_config_name = nil
13
- @tenanted_subtenant_of = nil
14
11
  end
15
12
 
16
13
  def tenanted(config_name = "primary")
@@ -20,7 +17,7 @@ module ActiveRecord
20
17
  prepend Tenant
21
18
 
22
19
  self.connection_class = true
23
- @tenanted_config_name = config_name
20
+ self.tenanted_config_name = config_name
24
21
 
25
22
  unless tenanted_root_config.configuration_hash[:tenanted]
26
23
  raise Error, "The '#{tenanted_config_name}' database is not configured as tenanted."
@@ -30,7 +27,7 @@ module ActiveRecord
30
27
  def subtenant_of(class_name)
31
28
  prepend Subtenant
32
29
 
33
- @tenanted_subtenant_of = class_name
30
+ self.tenanted_subtenant_of_klass_name = class_name
34
31
  end
35
32
 
36
33
  def tenanted?
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tenanted
5
+ module DatabaseConfigurations
6
+ class BaseConfig < ActiveRecord::DatabaseConfigurations::HashConfig
7
+ DEFAULT_MAX_CONNECTION_POOLS = 50
8
+
9
+ attr_accessor :test_worker_id
10
+
11
+ def initialize(...)
12
+ super
13
+ @test_worker_id = nil
14
+ end
15
+
16
+ def database_tasks?
17
+ false
18
+ end
19
+
20
+ def database_for(tenant_name)
21
+ tenant_name = tenant_name.to_s
22
+
23
+ validate_tenant_name(tenant_name)
24
+
25
+ path = sprintf(database, tenant: tenant_name)
26
+
27
+ if test_worker_id
28
+ test_worker_path(path)
29
+ else
30
+ path
31
+ end
32
+ end
33
+
34
+ def database_path_for(tenant_name)
35
+ coerce_path(database_for(tenant_name))
36
+ end
37
+
38
+ def tenants
39
+ glob = database_path_for("*")
40
+ scanner = Regexp.new(database_path_for("(.+)"))
41
+
42
+ Dir.glob(glob).map do |path|
43
+ result = path.scan(scanner).flatten.first
44
+ if result.nil?
45
+ warn "WARN: ActiveRecord::Tenanted: Cannot parse tenant name from filename #{path.inspect}. " \
46
+ "This is a bug, please report it to https://github.com/basecamp/activerecord-tenanted/issues"
47
+ end
48
+ result
49
+ end
50
+ end
51
+
52
+ def new_tenant_config(tenant_name)
53
+ config_name = "#{name}_#{tenant_name}"
54
+ config_hash = configuration_hash.dup.tap do |hash|
55
+ hash[:tenant] = tenant_name
56
+ hash[:database] = database_for(tenant_name)
57
+ hash[:database_path] = database_path_for(tenant_name)
58
+ hash[:tenanted_config_name] = name
59
+ end
60
+ Tenanted::DatabaseConfigurations::TenantConfig.new(env_name, config_name, config_hash)
61
+ end
62
+
63
+ def new_connection
64
+ raise NoTenantError, "Cannot use an untenanted ActiveRecord::Base connection. " \
65
+ "If you have a model that inherits directly from ActiveRecord::Base, " \
66
+ "make sure to use 'subtenant_of'. In development, you may see this error " \
67
+ "if constant reloading is not being done properly."
68
+ end
69
+
70
+ def max_connection_pools
71
+ (configuration_hash[:max_connection_pools] || DEFAULT_MAX_CONNECTION_POOLS).to_i
72
+ end
73
+
74
+ private
75
+ # A sqlite database path can be a file path or a URI (either relative or absolute).
76
+ # We can't parse it as a standard URI in all circumstances, though, see https://sqlite.org/uri.html
77
+ def coerce_path(path)
78
+ if path.start_with?("file:/")
79
+ URI.parse(path).path
80
+ elsif path.start_with?("file:")
81
+ URI.parse(path.sub(/\?.*$/, "")).opaque
82
+ else
83
+ path
84
+ end
85
+ end
86
+
87
+ def validate_tenant_name(tenant_name)
88
+ if tenant_name.match?(%r{[/'"`]})
89
+ raise BadTenantNameError, "Tenant name contains an invalid character: #{tenant_name.inspect}"
90
+ end
91
+ end
92
+
93
+ def test_worker_path(path)
94
+ test_worker_suffix = "_#{test_worker_id}"
95
+
96
+ if path.start_with?("file:") && path.include?("?")
97
+ path.sub(/(\?.*)$/, "#{test_worker_suffix}\\1")
98
+ else
99
+ path + test_worker_suffix
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tenanted
5
+ module DatabaseConfigurations
6
+ class TenantConfig < ActiveRecord::DatabaseConfigurations::HashConfig
7
+ def tenant
8
+ configuration_hash.fetch(:tenant)
9
+ end
10
+
11
+ def new_connection
12
+ ensure_database_directory_exists # adapter doesn't handle this if the database is a URI
13
+ super.tap { |conn| conn.tenant = tenant }
14
+ end
15
+
16
+ def tenanted_config_name
17
+ configuration_hash.fetch(:tenanted_config_name)
18
+ end
19
+
20
+ def primary?
21
+ ActiveRecord::Base.configurations.primary?(tenanted_config_name)
22
+ end
23
+
24
+ def schema_dump(format = ActiveRecord.schema_format)
25
+ if configuration_hash.key?(:schema_dump) || primary?
26
+ super
27
+ else
28
+ "#{tenanted_config_name}_#{schema_file_type(format)}"
29
+ end
30
+ end
31
+
32
+ def default_schema_cache_path(db_dir = "db")
33
+ if primary?
34
+ super
35
+ else
36
+ File.join(db_dir, "#{tenanted_config_name}_schema_cache.yml")
37
+ end
38
+ end
39
+
40
+ def database_path
41
+ configuration_hash[:database_path]
42
+ end
43
+
44
+ private
45
+ def ensure_database_directory_exists
46
+ return unless database_path
47
+
48
+ database_dir = File.dirname(database_path)
49
+ unless File.directory?(database_dir)
50
+ FileUtils.mkdir_p(database_dir)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -5,162 +5,13 @@ require "active_record/database_configurations"
5
5
  module ActiveRecord
6
6
  module Tenanted
7
7
  module DatabaseConfigurations
8
- class RootConfig < ActiveRecord::DatabaseConfigurations::HashConfig
9
- attr_accessor :test_worker_id
10
-
11
- def initialize(...)
12
- super
13
- @test_worker_id = nil
14
- end
15
-
16
- def database_tasks?
17
- false
18
- end
19
-
20
- def database_for(tenant_name)
21
- tenant_name = tenant_name.to_s
22
-
23
- validate_tenant_name(tenant_name)
24
-
25
- path = sprintf(database, tenant: tenant_name)
26
-
27
- if test_worker_id
28
- test_worker_path(path)
29
- else
30
- path
31
- end
32
- end
33
-
34
- def database_path_for(tenant_name)
35
- coerce_path(database_for(tenant_name))
36
- end
37
-
38
- def tenants
39
- glob = database_path_for("*")
40
- scanner = Regexp.new(database_path_for("(.+)"))
41
-
42
- Dir.glob(glob).map do |path|
43
- result = path.scan(scanner).flatten.first
44
- if result.nil?
45
- warn "WARN: ActiveRecord::Tenanted: Cannot parse tenant name from filename #{path.inspect}. " \
46
- "This is a bug, please report it to https://github.com/basecamp/activerecord-tenanted/issues"
47
- end
48
- result
49
- end
50
- end
51
-
52
- def new_tenant_config(tenant_name)
53
- config_name = "#{name}_#{tenant_name}"
54
- config_hash = configuration_hash.dup.tap do |hash|
55
- hash[:tenant] = tenant_name
56
- hash[:database] = database_for(tenant_name)
57
- hash[:database_path] = database_path_for(tenant_name)
58
- hash[:tenanted_config_name] = name
59
- end
60
- Tenanted::DatabaseConfigurations::TenantConfig.new(env_name, config_name, config_hash)
61
- end
62
-
63
- def new_connection
64
- raise NoTenantError, "Cannot use an untenanted ActiveRecord::Base connection. " \
65
- "If you have a model that inherits directly from ActiveRecord::Base, " \
66
- "make sure to use 'subtenant_of'. In development, you may see this error " \
67
- "if constant reloading is not being done properly."
68
- end
69
-
70
- private
71
- # A sqlite database path can be a file path or a URI (either relative or absolute).
72
- # We can't parse it as a standard URI in all circumstances, though, see https://sqlite.org/uri.html
73
- def coerce_path(path)
74
- if path.start_with?("file:/")
75
- URI.parse(path).path
76
- elsif path.start_with?("file:")
77
- URI.parse(path.sub(/\?.*$/, "")).opaque
78
- else
79
- path
80
- end
81
- end
82
-
83
- def validate_tenant_name(tenant_name)
84
- if tenant_name.match?(%r{[/'"`]})
85
- raise BadTenantNameError, "Tenant name contains an invalid character: #{tenant_name.inspect}"
86
- end
87
- end
88
-
89
- def test_worker_path(path)
90
- test_worker_suffix = "_#{test_worker_id}"
91
-
92
- if path.start_with?("file:") && path.include?("?")
93
- path.sub(/(\?.*)$/, "#{test_worker_suffix}\\1")
94
- else
95
- path + test_worker_suffix
96
- end
97
- end
98
- end
99
-
100
- class TenantConfig < ActiveRecord::DatabaseConfigurations::HashConfig
101
- def tenant
102
- configuration_hash.fetch(:tenant)
103
- end
104
-
105
- def new_connection
106
- ensure_database_directory_exists # adapter doesn't handle this if the database is a URI
107
- super.tap { |conn| conn.tenant = tenant }
108
- end
109
-
110
- def tenanted_config_name
111
- configuration_hash.fetch(:tenanted_config_name)
112
- end
113
-
114
- def primary?
115
- ActiveRecord::Base.configurations.primary?(tenanted_config_name)
116
- end
117
-
118
- def schema_dump(format = ActiveRecord.schema_format)
119
- if configuration_hash.key?(:schema_dump) || primary?
120
- super
121
- else
122
- "#{tenanted_config_name}_#{schema_file_type(format)}"
123
- end
124
- end
125
-
126
- def default_schema_cache_path(db_dir = "db")
127
- if primary?
128
- super
129
- else
130
- File.join(db_dir, "#{tenanted_config_name}_schema_cache.yml")
131
- end
132
- end
133
-
134
- def database_path
135
- configuration_hash[:database_path]
136
- end
137
-
138
- private
139
- def ensure_database_directory_exists
140
- return unless database_path
141
-
142
- database_dir = File.dirname(database_path)
143
- unless File.directory?(database_dir)
144
- FileUtils.mkdir_p(database_dir)
145
- end
146
- end
147
- end
148
-
149
- # Invoked by the railtie
150
8
  def self.register_db_config_handler # :nodoc:
151
9
  ActiveRecord::DatabaseConfigurations.register_db_config_handler do |env_name, name, _, config|
152
10
  next unless config.fetch(:tenanted, false)
153
11
 
154
- ActiveRecord::Tenanted::DatabaseConfigurations::RootConfig.new(env_name, name, config)
12
+ ActiveRecord::Tenanted::DatabaseConfigurations::BaseConfig.new(env_name, name, config)
155
13
  end
156
14
  end
157
15
  end
158
16
  end
159
17
  end
160
-
161
- # Do this here instead of the railtie so we register the handlers before Rails's rake tasks get
162
- # loaded. If the handler is not present, then the RootConfigs will not return false from
163
- # `#database_tasks?` and the database tasks will get created anyway.
164
- #
165
- # TODO: This can be moved back into the railtie if https://github.com/rails/rails/pull/54959 is merged.
166
- ActiveRecord::Tenanted::DatabaseConfigurations.register_db_config_handler
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Tenanted
5
+ # Inspired by the lru_redux gem, this LRU queue relies on Concurrent::Hash being ordered
6
+ class LRU # :nodoc:
7
+ def initialize
8
+ @data = Concurrent::Hash.new
9
+ end
10
+
11
+ def [](key)
12
+ found = true
13
+ value = @data.delete(key) { found = false }
14
+ if found
15
+ @data[key] = value
16
+ else
17
+ nil
18
+ end
19
+ end
20
+
21
+ def []=(key, value)
22
+ @data.delete key
23
+ @data[key] = value
24
+ value
25
+ end
26
+
27
+ def size
28
+ @data.size
29
+ end
30
+
31
+ def pop
32
+ key, value = @data.first
33
+ @data.delete(key)
34
+ [ key, value ]
35
+ end
36
+
37
+ def keys
38
+ @data.keys
39
+ end
40
+ end
41
+
42
+ Lru = LRU
43
+ end
44
+ end
@@ -57,6 +57,12 @@ module ActiveRecord
57
57
  # Defaults to "development-tenant" in development and "test-tenant" in test environments.
58
58
  config.active_record_tenanted.default_tenant = Rails.env.local? ? "#{Rails.env}-tenant" : nil
59
59
 
60
+ config.before_configuration do
61
+ ActiveSupport.on_load(:active_record_database_configurations) do
62
+ ActiveRecord::Tenanted::DatabaseConfigurations.register_db_config_handler
63
+ end
64
+ end
65
+
60
66
  config.before_initialize do
61
67
  Rails.application.configure do
62
68
  if config.active_record_tenanted.connection_class.present?
@@ -137,6 +143,10 @@ module ActiveRecord
137
143
  end
138
144
  end
139
145
 
146
+ initializer "active_record_tenanted.action_dispatch", before: "action_dispatch.configure" do
147
+ config.action_dispatch.rescue_responses["ActiveRecord::Tenanted::TenantDoesNotExistError"] = :not_found
148
+ end
149
+
140
150
  config.after_initialize do
141
151
  if defined?(Rails::Console)
142
152
  require "rails/commands/console/irb_console"
@@ -12,8 +12,7 @@ module ActiveRecord
12
12
 
13
13
  def tenanted_subtenant_of
14
14
  # TODO: cache this / speed this up
15
- # but note that we should constantize as late as possible to avoid load order issues
16
- klass = @tenanted_subtenant_of&.constantize || superclass.tenanted_subtenant_of
15
+ klass = tenanted_subtenant_of_klass_name&.constantize
17
16
 
18
17
  raise Error, "Class #{klass} is not tenanted" unless klass.tenanted?
19
18
  raise Error, "Class #{klass} is not a connection class" unless klass.abstract_class?
@@ -26,6 +25,8 @@ module ActiveRecord
26
25
 
27
26
  prepended do
28
27
  prepend TenantCommon
28
+
29
+ cattr_accessor :tenanted_subtenant_of_klass_name
29
30
  end
30
31
 
31
32
  def tenanted?
@@ -16,6 +16,10 @@ module ActiveRecord
16
16
  tenant ? "#{super}?tenant=#{tenant}" : super
17
17
  end
18
18
 
19
+ def inspect
20
+ tenant ? super.sub(/>$/, ", tenant=#{tenant.inspect}>") : super
21
+ end
22
+
19
23
  def to_global_id(options = {})
20
24
  super(options.merge(tenant: tenant))
21
25
  end
@@ -161,7 +165,7 @@ module ActiveRecord
161
165
  end
162
166
 
163
167
  def tenants
164
- # DatabaseConfigurations::RootConfig#tenants returns all tenants whose database files
168
+ # DatabaseConfigurations::BaseConfig#tenants returns all tenants whose database files
165
169
  # exist, but some of those may be getting initially migrated, so we perform an additional
166
170
  # filter on readiness with `tenant_exist?`.
167
171
  tenanted_root_config.tenants.select { |t| tenant_exist?(t) }
@@ -202,10 +206,6 @@ module ActiveRecord
202
206
  ActiveRecord::Base.configurations.resolve(tenanted_config_name.to_sym)
203
207
  end
204
208
 
205
- def tenanted_config_name # :nodoc:
206
- @tenanted_config_name ||= (superclass.respond_to?(:tenanted_config_name) ? superclass.tenanted_config_name : nil)
207
- end
208
-
209
209
  def _create_tenanted_pool(schema_version_check: true) # :nodoc:
210
210
  # ensure all classes use the same connection pool
211
211
  return superclass._create_tenanted_pool unless connection_class?
@@ -228,10 +228,24 @@ module ActiveRecord
228
228
 
229
229
  private
230
230
  def retrieve_connection_pool(strict:)
231
- connection_handler.retrieve_connection_pool(connection_specification_name,
232
- role: current_role,
233
- shard: current_tenant,
234
- strict: strict)
231
+ role = current_role
232
+ shard = current_tenant
233
+ connection_handler.retrieve_connection_pool(connection_specification_name, role:, shard:, strict:).tap do |pool|
234
+ if pool
235
+ tenanted_connection_pools[[ shard, role ]] = pool
236
+ reap_connection_pools
237
+ end
238
+ end
239
+ end
240
+
241
+ def reap_connection_pools
242
+ while tenanted_connection_pools.size > tenanted_root_config.max_connection_pools
243
+ info, _ = *tenanted_connection_pools.pop
244
+ shard, role = *info
245
+
246
+ connection_handler.remove_connection_pool(connection_specification_name, role:, shard:)
247
+ Rails.logger.info " REAPED [tenant=#{shard} role=#{role}] Tenanted connection pool reaped to limit total connection pools"
248
+ end
235
249
  end
236
250
 
237
251
  def log_tenant_tag(tenant_name, &block)
@@ -247,6 +261,9 @@ module ActiveRecord
247
261
  self.default_shard = ActiveRecord::Tenanted::Tenant::UNTENANTED_SENTINEL
248
262
 
249
263
  prepend TenantCommon
264
+
265
+ cattr_accessor :tenanted_config_name
266
+ cattr_accessor(:tenanted_connection_pools) { LRU.new }
250
267
  end
251
268
 
252
269
  def tenanted?
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rack/contrib"
4
-
5
3
  module ActiveRecord
6
4
  module Tenanted
7
5
  #
@@ -34,8 +32,7 @@ module ActiveRecord
34
32
  elsif tenanted_class.tenant_exist?(tenant_name)
35
33
  tenanted_class.with_tenant(tenant_name) { @app.call(env) }
36
34
  else
37
- Rails.logger.info("ActiveRecord::Tenanted::TenantSelector: Tenant not found: #{tenant_name.inspect}")
38
- Rack::NotFound.new(Rails.root.join("public/404.html")).call(env)
35
+ raise ActiveRecord::Tenanted::TenantDoesNotExistError, "Tenant not found: #{tenant_name.inspect}"
39
36
  end
40
37
  end
41
38
 
@@ -73,9 +73,9 @@ module ActiveRecord
73
73
  def transactional_tests_for_pool?(pool)
74
74
  config = pool.db_config
75
75
 
76
- # Prevent the tenanted RootConfig from creating transactional fixtures on an unnecessary
76
+ # Prevent the tenanted BaseConfig from creating transactional fixtures on an unnecessary
77
77
  # database, which would result in sporadic locking errors.
78
- is_root_config = config.instance_of?(Tenanted::DatabaseConfigurations::RootConfig)
78
+ is_root_config = config.instance_of?(Tenanted::DatabaseConfigurations::BaseConfig)
79
79
 
80
80
  # Any tenanted database that isn't the default test fixture database should not be wrapped
81
81
  # in a transaction, for a couple of reasons:
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Tenanted
5
- VERSION = "0.2.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-tenanted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Dalessio
@@ -15,42 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 8.1.alpha
18
+ version: 8.1.beta
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 8.1.alpha
25
+ version: 8.1.beta
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: railties
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 8.1.alpha
32
+ version: 8.1.beta
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: 8.1.alpha
40
- - !ruby/object:Gem::Dependency
41
- name: rack-contrib
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 2.5.0
47
- type: :runtime
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: 2.5.0
39
+ version: 8.1.beta
54
40
  - !ruby/object:Gem::Dependency
55
41
  name: zeitwerk
56
42
  requirement: !ruby/object:Gem::Requirement
@@ -88,9 +74,12 @@ files:
88
74
  - lib/active_record/tenanted/connection_adapter.rb
89
75
  - lib/active_record/tenanted/console.rb
90
76
  - lib/active_record/tenanted/database_configurations.rb
77
+ - lib/active_record/tenanted/database_configurations/base_config.rb
78
+ - lib/active_record/tenanted/database_configurations/tenant_config.rb
91
79
  - lib/active_record/tenanted/database_tasks.rb
92
80
  - lib/active_record/tenanted/global_id.rb
93
81
  - lib/active_record/tenanted/job.rb
82
+ - lib/active_record/tenanted/lru.rb
94
83
  - lib/active_record/tenanted/mailer.rb
95
84
  - lib/active_record/tenanted/mutex.rb
96
85
  - lib/active_record/tenanted/patches.rb