ros-apartment 2.6.1 → 2.8.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/changelog.yml +63 -0
  3. data/.rubocop.yml +74 -4
  4. data/.rubocop_todo.yml +50 -13
  5. data/.story_branch.yml +1 -0
  6. data/.travis.yml +5 -0
  7. data/CHANGELOG.md +962 -0
  8. data/Gemfile +1 -1
  9. data/Guardfile +0 -15
  10. data/HISTORY.md +159 -68
  11. data/README.md +30 -3
  12. data/Rakefile +4 -2
  13. data/TODO.md +0 -1
  14. data/gemfiles/rails_5_0.gemfile +1 -1
  15. data/gemfiles/rails_5_1.gemfile +1 -1
  16. data/gemfiles/rails_5_2.gemfile +1 -1
  17. data/gemfiles/rails_6_0.gemfile +1 -1
  18. data/gemfiles/rails_master.gemfile +1 -1
  19. data/lib/apartment.rb +13 -12
  20. data/lib/apartment/active_record/connection_handling.rb +3 -0
  21. data/lib/apartment/active_record/internal_metadata.rb +0 -2
  22. data/lib/apartment/active_record/schema_migration.rb +0 -2
  23. data/lib/apartment/adapters/abstract_adapter.rb +3 -4
  24. data/lib/apartment/adapters/abstract_jdbc_adapter.rb +2 -1
  25. data/lib/apartment/adapters/jdbc_postgresql_adapter.rb +2 -1
  26. data/lib/apartment/adapters/mysql2_adapter.rb +5 -0
  27. data/lib/apartment/adapters/postgresql_adapter.rb +34 -7
  28. data/lib/apartment/console.rb +2 -8
  29. data/lib/apartment/custom_console.rb +2 -2
  30. data/lib/apartment/log_subscriber.rb +29 -0
  31. data/lib/apartment/railtie.rb +24 -21
  32. data/lib/apartment/tasks/enhancements.rb +1 -1
  33. data/lib/apartment/tasks/task_helper.rb +40 -0
  34. data/lib/apartment/tenant.rb +3 -16
  35. data/lib/apartment/version.rb +1 -1
  36. data/lib/generators/apartment/install/templates/apartment.rb +7 -1
  37. data/lib/tasks/apartment.rake +18 -46
  38. data/{apartment.gemspec → ros-apartment.gemspec} +3 -4
  39. metadata +17 -14
  40. data/.github/workflows/.rubocop-linter.yml +0 -22
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/log_subscriber'
4
+
5
+ module Apartment
6
+ class LogSubscriber < ActiveRecord::LogSubscriber
7
+ def sql(event)
8
+ super(event)
9
+ end
10
+
11
+ private
12
+
13
+ def debug(progname = nil, &block)
14
+ progname = " #{apartment_log}#{progname}" unless progname.nil?
15
+
16
+ super(progname, &block)
17
+ end
18
+
19
+ def apartment_log
20
+ database = color("[#{Apartment.connection.current_database}] ", ActiveSupport::LogSubscriber::MAGENTA, true)
21
+ schema = nil
22
+ unless Apartment.connection.schema_search_path.nil?
23
+ schema = color("[#{Apartment.connection.schema_search_path.tr('"', '')}] ",
24
+ ActiveSupport::LogSubscriber::YELLOW, true)
25
+ end
26
+ "#{database}#{schema}"
27
+ end
28
+ end
29
+ end
@@ -6,6 +6,7 @@ require 'apartment/reloader'
6
6
 
7
7
  module Apartment
8
8
  class Railtie < Rails::Railtie
9
+
9
10
  #
10
11
  # Set up our default config options
11
12
  # Do this before the app initializers run so we don't override custom settings
@@ -19,33 +20,38 @@ module Apartment
19
20
  config.prepend_environment = false
20
21
  config.append_environment = false
21
22
  config.tenant_presence_check = true
23
+ config.active_record_log = false
22
24
  end
23
25
 
24
26
  ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
25
27
  end
26
28
 
27
- # Make sure Apartment is reconfigured when the code is reloaded
29
+ # Hook into ActionDispatch::Reloader to ensure Apartment is properly initialized
30
+ # Note that this doens't entirely work as expected in Development, because this is called before classes are reloaded
31
+ # See the middleware/console declarations below to help with this. Hope to fix that soon.
32
+ #
28
33
  config.to_prepare do
29
- Apartment::Tenant.reinitialize
30
- end
34
+ next if ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ }
35
+ next if ARGV.any? { |arg| arg == 'webpacker:compile' }
36
+ next if ENV["APARTMENT_DISABLE_INIT"]
31
37
 
32
- #
33
- # Ensure that Apartment::Tenant.init is called when
34
- # a new connection is requested.
35
- #
36
- module ApartmentInitializer
37
- def connection
38
- super.tap do
39
- Apartment::Tenant.init_once
38
+ begin
39
+ Apartment.connection_class.connection_pool.with_connection do
40
+ Apartment::Tenant.init
40
41
  end
42
+ rescue ::ActiveRecord::NoDatabaseError
43
+ # Since `db:create` and other tasks invoke this block from Rails 5.2.0,
44
+ # we need to swallow the error to execute `db:create` properly.
41
45
  end
46
+ end
42
47
 
43
- def arel_table
44
- Apartment::Tenant.init_once
45
- super
48
+ config.after_initialize do |app|
49
+ # NOTE: Load the custom log subscriber if enabled
50
+ if Apartment.active_record_log
51
+ ActiveSupport::Notifications.unsubscribe 'sql.active_record'
52
+ Apartment::LogSubscriber.attach_to :active_record
46
53
  end
47
54
  end
48
- ActiveRecord::Base.singleton_class.prepend ApartmentInitializer
49
55
 
50
56
  #
51
57
  # Ensure rake tasks are loaded
@@ -56,10 +62,8 @@ module Apartment
56
62
  end
57
63
 
58
64
  #
59
- # The following initializers are a workaround to the fact that I can't
60
- # properly hook into the rails reloader
61
- # Note this is technically valid for any environment where cache_classes
62
- # is false, for us, it's just development
65
+ # The following initializers are a workaround to the fact that I can't properly hook into the rails reloader
66
+ # Note this is technically valid for any environment where cache_classes is false, for us, it's just development
63
67
  #
64
68
  if Rails.env.development?
65
69
 
@@ -68,8 +72,7 @@ module Apartment
68
72
  app.config.middleware.use Apartment::Reloader
69
73
  end
70
74
 
71
- # Overrides reload! to also call Apartment::Tenant.init as well so that the
72
- # reloaded classes have the proper table_names
75
+ # Overrides reload! to also call Apartment::Tenant.init as well so that the reloaded classes have the proper table_names
73
76
  console do
74
77
  require 'apartment/console'
75
78
  end
@@ -7,7 +7,7 @@ module Apartment
7
7
  class RakeTaskEnhancer
8
8
  module TASKS
9
9
  ENHANCE_BEFORE = %w[db:drop].freeze
10
- ENHANCE_AFTER = %w[db:create db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed].freeze
10
+ ENHANCE_AFTER = %w[db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed].freeze
11
11
  freeze
12
12
  end
13
13
 
@@ -0,0 +1,40 @@
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
+ block.call(tenant)
8
+ end
9
+ end
10
+
11
+ def self.tenants_without_default
12
+ tenants - [Apartment.default_tenant]
13
+ end
14
+
15
+ def self.tenants
16
+ ENV['DB'] ? ENV['DB'].split(',').map(&:strip) : Apartment.tenant_names || []
17
+ end
18
+
19
+ def self.warn_if_tenants_empty
20
+ return unless tenants.empty? && ENV['IGNORE_EMPTY_TENANTS'] != 'true'
21
+
22
+ puts <<-WARNING
23
+ [WARNING] - The list of tenants to migrate appears to be empty. This could mean a few things:
24
+
25
+ 1. You may not have created any, in which case you can ignore this message
26
+ 2. You've run `apartment:migrate` directly without loading the Rails environment
27
+ * `apartment:migrate` is now deprecated. Tenants will automatically be migrated with `db:migrate`
28
+
29
+ Note that your tenants currently haven't been migrated. You'll need to run `db:migrate` to rectify this.
30
+ WARNING
31
+ end
32
+
33
+ def self.create_tenant(tenant_name)
34
+ puts("Creating #{tenant_name} tenant")
35
+ Apartment::Tenant.create(tenant_name)
36
+ rescue Apartment::TenantExists => e
37
+ puts "Tried to create already existing tenant: #{e}"
38
+ end
39
+ end
40
+ end
@@ -15,20 +15,6 @@ module Apartment
15
15
 
16
16
  attr_writer :config
17
17
 
18
- def init_once
19
- return if @already_initialized
20
-
21
- # To avoid infinite loops in work init is doing,
22
- # we need to set @already_initialized to true
23
- # before init is called
24
- @already_initialized = true
25
- init
26
- end
27
-
28
- def reinitialize
29
- @already_initialized = false
30
- end
31
-
32
18
  # Fetch the proper multi-tenant adapter based on Rails config
33
19
  #
34
20
  # @return {subclass of Apartment::AbstractAdapter}
@@ -51,7 +37,9 @@ module Apartment
51
37
  raise "The adapter `#{adapter_method}` is not yet supported"
52
38
  end
53
39
 
54
- raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter" unless respond_to?(adapter_method)
40
+ unless respond_to?(adapter_method)
41
+ raise AdapterNotFound, "database configuration specifies nonexistent #{config[:adapter]} adapter"
42
+ end
55
43
 
56
44
  send(adapter_method, config)
57
45
  end
@@ -61,7 +49,6 @@ module Apartment
61
49
  #
62
50
  def reload!(config = nil)
63
51
  Thread.current[:apartment_adapter] = nil
64
- reinitialize
65
52
  @config = config
66
53
  end
67
54
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Apartment
4
- VERSION = '2.6.1'
4
+ VERSION = '2.8.1.rc1'
5
5
  end
@@ -23,7 +23,8 @@ Apartment.configure do |config|
23
23
  # You can make this dynamic by providing a Proc object to be called on migrations.
24
24
  # This object should yield either:
25
25
  # - an array of strings representing each Tenant name.
26
- # - a hash which keys are tenant names, and values custom db config (must contain all key/values required in database.yml)
26
+ # - a hash which keys are tenant names, and values custom db config
27
+ # (must contain all key/values required in database.yml)
27
28
  #
28
29
  # config.tenant_names = lambda{ Customer.pluck(:tenant_name) }
29
30
  # config.tenant_names = ['tenant1', 'tenant2']
@@ -96,6 +97,11 @@ Apartment.configure do |config|
96
97
  # the new tenant
97
98
  #
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
99
105
  end
100
106
 
101
107
  # Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that
@@ -1,24 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'apartment/migrator'
4
+ require 'apartment/tasks/task_helper'
4
5
  require 'parallel'
5
6
 
6
7
  apartment_namespace = namespace :apartment do
7
8
  desc 'Create all tenants'
8
9
  task :create do
9
- tenants.each do |tenant|
10
- begin
11
- puts("Creating #{tenant} tenant")
12
- Apartment::Tenant.create(tenant)
13
- rescue Apartment::TenantExists => e
14
- puts e.message
15
- end
10
+ Apartment::TaskHelper.warn_if_tenants_empty
11
+
12
+ Apartment::TaskHelper.tenants.each do |tenant|
13
+ Apartment::TaskHelper.create_tenant(tenant)
16
14
  end
17
15
  end
18
16
 
19
17
  desc 'Drop all tenants'
20
18
  task :drop do
21
- tenants.each do |tenant|
19
+ Apartment::TaskHelper.tenants.each do |tenant|
22
20
  begin
23
21
  puts("Dropping #{tenant} tenant")
24
22
  Apartment::Tenant.drop(tenant)
@@ -30,9 +28,10 @@ apartment_namespace = namespace :apartment do
30
28
 
31
29
  desc 'Migrate all tenants'
32
30
  task :migrate do
33
- warn_if_tenants_empty
34
- each_tenant do |tenant|
31
+ Apartment::TaskHelper.warn_if_tenants_empty
32
+ Apartment::TaskHelper.each_tenant do |tenant|
35
33
  begin
34
+ Apartment::TaskHelper.create_tenant(tenant)
36
35
  puts("Migrating #{tenant} tenant")
37
36
  Apartment::Migrator.migrate tenant
38
37
  rescue Apartment::TenantNotFound => e
@@ -43,10 +42,11 @@ apartment_namespace = namespace :apartment do
43
42
 
44
43
  desc 'Seed all tenants'
45
44
  task :seed do
46
- warn_if_tenants_empty
45
+ Apartment::TaskHelper.warn_if_tenants_empty
47
46
 
48
- each_tenant do |tenant|
47
+ Apartment::TaskHelper.each_tenant do |tenant|
49
48
  begin
49
+ Apartment::TaskHelper.create_tenant(tenant)
50
50
  puts("Seeding #{tenant} tenant")
51
51
  Apartment::Tenant.switch(tenant) do
52
52
  Apartment::Tenant.seed
@@ -59,11 +59,11 @@ apartment_namespace = namespace :apartment do
59
59
 
60
60
  desc 'Rolls the migration back to the previous version (specify steps w/ STEP=n) across all tenants.'
61
61
  task :rollback do
62
- warn_if_tenants_empty
62
+ Apartment::TaskHelper.warn_if_tenants_empty
63
63
 
64
64
  step = ENV['STEP'] ? ENV['STEP'].to_i : 1
65
65
 
66
- each_tenant do |tenant|
66
+ Apartment::TaskHelper.each_tenant do |tenant|
67
67
  begin
68
68
  puts("Rolling back #{tenant} tenant")
69
69
  Apartment::Migrator.rollback tenant, step
@@ -76,12 +76,12 @@ apartment_namespace = namespace :apartment do
76
76
  namespace :migrate do
77
77
  desc 'Runs the "up" for a given migration VERSION across all tenants.'
78
78
  task :up do
79
- warn_if_tenants_empty
79
+ Apartment::TaskHelper.warn_if_tenants_empty
80
80
 
81
81
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
82
82
  raise 'VERSION is required' unless version
83
83
 
84
- each_tenant do |tenant|
84
+ Apartment::TaskHelper.each_tenant do |tenant|
85
85
  begin
86
86
  puts("Migrating #{tenant} tenant up")
87
87
  Apartment::Migrator.run :up, tenant, version
@@ -93,12 +93,12 @@ apartment_namespace = namespace :apartment do
93
93
 
94
94
  desc 'Runs the "down" for a given migration VERSION across all tenants.'
95
95
  task :down do
96
- warn_if_tenants_empty
96
+ Apartment::TaskHelper.warn_if_tenants_empty
97
97
 
98
98
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
99
99
  raise 'VERSION is required' unless version
100
100
 
101
- each_tenant do |tenant|
101
+ Apartment::TaskHelper.each_tenant do |tenant|
102
102
  begin
103
103
  puts("Migrating #{tenant} tenant down")
104
104
  Apartment::Migrator.run :down, tenant, version
@@ -119,32 +119,4 @@ apartment_namespace = namespace :apartment do
119
119
  end
120
120
  end
121
121
  end
122
-
123
- def each_tenant(&block)
124
- Parallel.each(tenants_without_default, in_threads: Apartment.parallel_migration_threads) do |tenant|
125
- block.call(tenant)
126
- end
127
- end
128
-
129
- def tenants_without_default
130
- tenants - [Apartment.default_schema]
131
- end
132
-
133
- def tenants
134
- ENV['DB'] ? ENV['DB'].split(',').map(&:strip) : Apartment.tenant_names || []
135
- end
136
-
137
- def warn_if_tenants_empty
138
- return unless tenants.empty? && ENV['IGNORE_EMPTY_TENANTS'] != 'true'
139
-
140
- puts <<-WARNING
141
- [WARNING] - The list of tenants to migrate appears to be empty. This could mean a few things:
142
-
143
- 1. You may not have created any, in which case you can ignore this message
144
- 2. You've run `apartment:migrate` directly without loading the Rails environment
145
- * `apartment:migrate` is now deprecated. Tenants will automatically be migrated with `db:migrate`
146
-
147
- Note that your tenants currently haven't been migrated. You'll need to run `db:migrate` to rectify this.
148
- WARNING
149
- end
150
122
  end
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
17
17
  s.files = Dir.chdir(File.expand_path(__dir__)) do
18
18
  `git ls-files -z`.split("\x0").reject do |f|
19
19
  # NOTE: ignore all test related
20
- f.match(%r{^(test|spec|features)/})
20
+ f.match(%r{^(test|spec|features|documentation)/})
21
21
  end
22
22
  end
23
23
  s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
@@ -27,8 +27,7 @@ Gem::Specification.new do |s|
27
27
  s.homepage = 'https://github.com/rails-on-services/apartment'
28
28
  s.licenses = ['MIT']
29
29
 
30
- # must be >= 3.1.2 due to bug in prepared_statements
31
- s.add_dependency 'activerecord', '>= 5.0.0', '< 6.1'
30
+ s.add_dependency 'activerecord', '>= 5.0.0', '< 6.2'
32
31
  s.add_dependency 'parallel', '< 2.0'
33
32
  s.add_dependency 'public_suffix', '>= 2.0.5', '< 5.0'
34
33
  s.add_dependency 'rack', '>= 1.3.6', '< 3.0'
@@ -36,7 +35,7 @@ Gem::Specification.new do |s|
36
35
  s.add_development_dependency 'appraisal', '~> 2.2'
37
36
  s.add_development_dependency 'bundler', '>= 1.3', '< 3.0'
38
37
  s.add_development_dependency 'capybara', '~> 2.0'
39
- s.add_development_dependency 'rake', '~> 0.9'
38
+ s.add_development_dependency 'rake', '~> 13.0'
40
39
  s.add_development_dependency 'rspec', '~> 3.4'
41
40
  s.add_development_dependency 'rspec-rails', '~> 3.4'
42
41
 
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ros-apartment
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.1
4
+ version: 2.8.1.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Brunner
8
8
  - Brad Robertson
9
9
  - Rui Baltazar
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-06-02 00:00:00.000000000 Z
13
+ date: 2020-12-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 5.0.0
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
- version: '6.1'
24
+ version: '6.2'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -31,7 +31,7 @@ dependencies:
31
31
  version: 5.0.0
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
- version: '6.1'
34
+ version: '6.2'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: parallel
37
37
  requirement: !ruby/object:Gem::Requirement
@@ -140,14 +140,14 @@ dependencies:
140
140
  requirements:
141
141
  - - "~>"
142
142
  - !ruby/object:Gem::Version
143
- version: '0.9'
143
+ version: '13.0'
144
144
  type: :development
145
145
  prerelease: false
146
146
  version_requirements: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - "~>"
149
149
  - !ruby/object:Gem::Version
150
- version: '0.9'
150
+ version: '13.0'
151
151
  - !ruby/object:Gem::Dependency
152
152
  name: rspec
153
153
  requirement: !ruby/object:Gem::Requirement
@@ -229,7 +229,7 @@ extensions: []
229
229
  extra_rdoc_files: []
230
230
  files:
231
231
  - ".github/ISSUE_TEMPLATE.md"
232
- - ".github/workflows/.rubocop-linter.yml"
232
+ - ".github/workflows/changelog.yml"
233
233
  - ".gitignore"
234
234
  - ".pryrc"
235
235
  - ".rspec"
@@ -238,13 +238,13 @@ files:
238
238
  - ".story_branch.yml"
239
239
  - ".travis.yml"
240
240
  - Appraisals
241
+ - CHANGELOG.md
241
242
  - Gemfile
242
243
  - Guardfile
243
244
  - HISTORY.md
244
245
  - README.md
245
246
  - Rakefile
246
247
  - TODO.md
247
- - apartment.gemspec
248
248
  - docker-compose.yml
249
249
  - gemfiles/rails_4_2.gemfile
250
250
  - gemfiles/rails_5_0.gemfile
@@ -273,22 +273,25 @@ files:
273
273
  - lib/apartment/elevators/host.rb
274
274
  - lib/apartment/elevators/host_hash.rb
275
275
  - lib/apartment/elevators/subdomain.rb
276
+ - lib/apartment/log_subscriber.rb
276
277
  - lib/apartment/migrator.rb
277
278
  - lib/apartment/model.rb
278
279
  - lib/apartment/railtie.rb
279
280
  - lib/apartment/reloader.rb
280
281
  - lib/apartment/tasks/enhancements.rb
282
+ - lib/apartment/tasks/task_helper.rb
281
283
  - lib/apartment/tenant.rb
282
284
  - lib/apartment/version.rb
283
285
  - lib/generators/apartment/install/USAGE
284
286
  - lib/generators/apartment/install/install_generator.rb
285
287
  - lib/generators/apartment/install/templates/apartment.rb
286
288
  - lib/tasks/apartment.rake
289
+ - ros-apartment.gemspec
287
290
  homepage: https://github.com/rails-on-services/apartment
288
291
  licenses:
289
292
  - MIT
290
293
  metadata: {}
291
- post_install_message:
294
+ post_install_message:
292
295
  rdoc_options: []
293
296
  require_paths:
294
297
  - lib
@@ -299,12 +302,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
299
302
  version: '0'
300
303
  required_rubygems_version: !ruby/object:Gem::Requirement
301
304
  requirements:
302
- - - ">="
305
+ - - ">"
303
306
  - !ruby/object:Gem::Version
304
- version: '0'
307
+ version: 1.3.1
305
308
  requirements: []
306
- rubygems_version: 3.0.3
307
- signing_key:
309
+ rubygems_version: 3.1.4
310
+ signing_key:
308
311
  specification_version: 4
309
312
  summary: A Ruby gem for managing database multitenancy. Apartment Gem drop in replacement
310
313
  test_files: []