Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a5401ad
Don't retain connection used during gem initialisation
Nov 14, 2016
959432f
Accept DB config
kakipo Dec 10, 2016
77f8e65
Use public_suffix for smarter subdomain parsing (#309)
Mar 2, 2017
3061e99
Removing deprecated methods.
Mar 12, 2017
194b289
Add Rails ~5.0.0 to travis and remove ~3.2.0
Mar 13, 2017
6431e58
Rewrite adpaters to support flexible switching
Jun 26, 2017
7007f0f
Fix excluded_model reset during tests & railtie test
Jun 28, 2017
724940b
Update readme & generator template
Jun 29, 2017
17edd0e
Improve connection pooling
Aug 18, 2017
323fdfc
Fixed connection handling to be threadsafe
Aug 24, 2017
d242773
Make threads test more stressful
Apr 4, 2018
b742ad0
Run migrations in parallel
Aug 29, 2013
2d9e7e1
Don't fail when default tenant doesn't exist
mikecmpbll Sep 9, 2019
57e65ef
Fix method missing in abstract adapter
mikecmpbll Sep 20, 2019
75a68f6
Fix creating tenants on other hosts
mikecmpbll Sep 23, 2019
184cbce
Use AR 5.2 migration_context
mikecmpbll Oct 24, 2019
89db3a0
Fix unrecoverable invalid tenant loop
mikecmpbll Aug 18, 2020
84fa477
Ensure even resetting doesn't fail
mikecmpbll Aug 18, 2020
eaa572e
Try to only reconnect if broken connection
mikecmpbll Aug 18, 2020
1ff7849
Tidy up logic ...
mikecmpbll Aug 18, 2020
da67f09
A switch should never totally fail
mikecmpbll Aug 31, 2020
ebeb824
Turn logging up to 10...
mikecmpbll Aug 31, 2020
7b00853
Make connection switching resilient
mikecmpbll Aug 31, 2020
4730352
Prevent invalid tenant names from getting through
mikecmpbll Sep 2, 2020
c79c8a4
Access config with_indifferent_access
mikecmpbll Sep 2, 2020
8405fa7
Re-establish the Apartment base state on fork
mikecmpbll Sep 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ pkg/*
*.log
.idea
*.sw[pno]
spec/config/database.yml
spec/dummy/config/database.yml
test/databases.yml
test/dummy/config/database.yml
cookbooks
tmp
spec/dummy/db/*.sqlite3
test/dummy/db/*.sqlite3
.DS_Store
test/debug.log
4 changes: 0 additions & 4 deletions .rspec

This file was deleted.

1 change: 0 additions & 1 deletion .ruby-gemset

This file was deleted.

1 change: 0 additions & 1 deletion .ruby-version

This file was deleted.

10 changes: 1 addition & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
language: ruby
rvm:
- 2.0.0
- 2.1.9
- 2.2.4
- 2.3.1
- jruby-9.0.5.0
gemfile:
- gemfiles/rails_3_2.gemfile
- gemfiles/rails_4_0.gemfile
- gemfiles/rails_4_1.gemfile
- gemfiles/rails_4_2.gemfile
- gemfiles/rails_5_1.gemfile
bundler_args: --without local
before_install:
- gem install bundler -v '> 1.5.0'
env:
RUBY_GC_MALLOC_LIMIT: 90000000
RUBY_FREE_MIN: 200000
matrix:
exclude:
- rvm: 2.2.0
gemfile: gemfiles/rails_3_2.gemfile
fast_finish: true
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ source 'http://rubygems.org'

gemspec

gem 'rails', '>= 3.1.2'
gem 'rails', '>= 5.1.0'

group :local do
gem 'pry'
Expand Down
380 changes: 177 additions & 203 deletions README.md

Large diffs are not rendered by default.

51 changes: 23 additions & 28 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
require 'bundler' rescue 'You must `gem install bundler` and `bundle install` to run rake tasks'
Bundler.setup
Bundler::GemHelper.install_tasks

require 'appraisal'

require "rspec"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec => %w{ db:copy_credentials db:test:prepare }) do |spec|
spec.pattern = "spec/**/*_spec.rb"
# spec.rspec_opts = '--order rand:16996'
require "rake/testtask"
require 'yaml'

Rake::TestTask.new do |t|
t.libs = ["lib"]
t.warning = false
# t.verbose = true
t.test_files = FileList['test/*_test.rb']
end

namespace :spec do
[:tasks, :unit, :adapters, :integration].each do |type|
RSpec::Core::RakeTask.new(type => :spec) do |spec|
spec.pattern = "spec/#{type}/**/*_spec.rb"
end
end
end
require 'appraisal'
# require "#{File.join(File.dirname(__FILE__), 'test', 'test_helper')}"

task :console do
require 'pry'
Expand All @@ -27,18 +21,18 @@ task :console do
Pry.start
end

task :default => :spec
task default: :test

namespace :db do
namespace :test do
task :prepare => %w{postgres:drop_db postgres:build_db mysql:drop_db mysql:build_db}
task prepare: %w{postgres:drop_db postgres:build_db mysql:drop_db mysql:build_db}
end

desc "copy sample database credential files over if real files don't exist"
task :copy_credentials do
require 'fileutils'
apartment_db_file = 'spec/config/database.yml'
rails_db_file = 'spec/dummy/config/database.yml'
apartment_db_file = 'test/databases.yml'
rails_db_file = 'test/dummy/config/database.yml'

FileUtils.copy(apartment_db_file + '.sample', apartment_db_file, :verbose => true) unless File.exists?(apartment_db_file)
FileUtils.copy(rails_db_file + '.sample', rails_db_file, :verbose => true) unless File.exists?(rails_db_file)
Expand All @@ -47,13 +41,14 @@ end

namespace :postgres do
require 'active_record'
require "#{File.join(File.dirname(__FILE__), 'spec', 'support', 'config')}"

desc 'Build the PostgreSQL test databases'
task :build_db do
%x{ createdb -E UTF8 #{pg_config['database']} -U#{pg_config['username']} } rescue "test db already exists"
ActiveRecord::Base.establish_connection pg_config
ActiveRecord::Migrator.migrate('spec/dummy/db/migrate')
ActiveRecord::Migration.suppress_messages do
load(File.join(File.dirname(__FILE__), "test/dummy/db/schema.rb"))
end
end

desc "drop the PostgreSQL test database"
Expand All @@ -66,26 +61,26 @@ end

namespace :mysql do
require 'active_record'
require "#{File.join(File.dirname(__FILE__), 'spec', 'support', 'config')}"

desc 'Build the MySQL test databases'
task :build_db do
%x{ mysqladmin -u #{my_config['username']} --password=#{my_config['password']} create #{my_config['database']} } rescue "test db already exists"
%x{ /usr/local/mysql/bin/mysqladmin -u #{my_config['username']} --password=#{my_config['password']} create #{my_config['database']} } rescue "test db already exists"
ActiveRecord::Base.establish_connection my_config
ActiveRecord::Migrator.migrate('spec/dummy/db/migrate')
ActiveRecord::Migration.suppress_messages do
load(File.join(File.dirname(__FILE__), "test/dummy/db/schema.rb"))
end
end

desc "drop the MySQL test database"
task :drop_db do
puts "dropping database #{my_config['database']}"
%x{ mysqladmin -u #{my_config['username']} --password=#{my_config['password']} drop #{my_config['database']} --force}
%x{ /usr/local/mysql/bin/mysqladmin -u #{my_config['username']} --password=#{my_config['password']} drop #{my_config['database']} --force}
end

end

# TODO clean this up
def config
Apartment::Test.config['connections']
@config ||= YAML.load(ERB.new(IO.read('test/databases.yml')).result)['connections']
end

def pg_config
Expand Down
23 changes: 3 additions & 20 deletions apartment.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,13 @@ Gem::Specification.new do |s|
s.homepage = %q{https://github.com/influitive/apartment}
s.licenses = ["MIT"]

s.post_install_message = <<-MSG
********************************

Apartment Deprecation Warning

`Apartment::Tenant.process` has been deprecated in favour of `Apartment::Tenant.switch`.
You must now always pass a block to `switch`.

To get the previous `switch` behaviour where you can switch to a tenant
without a block, use `Apartment::Tenant.switch!`.
This is to indicate that your call actually has a side affect of changing
the scope of your queries to that tenant.

********************************
MSG

# must be >= 3.1.2 due to bug in prepared_statements
s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0'
s.add_dependency 'activerecord', '>= 5.2.0'
s.add_dependency 'rack', '>= 1.3.6'
s.add_dependency 'public_suffix', '~> 2.0.5'
s.add_dependency 'parallel', '>= 0.7.1'

s.add_development_dependency 'appraisal'
s.add_development_dependency 'rake', '~> 0.9'
s.add_development_dependency 'rspec', '~> 3.4'
s.add_development_dependency 'rspec-rails', '~> 3.4'
s.add_development_dependency 'capybara', '~> 2.0'

if defined?(JRUBY_VERSION)
Expand Down
13 changes: 0 additions & 13 deletions gemfiles/rails_3_2.gemfile

This file was deleted.

12 changes: 0 additions & 12 deletions gemfiles/rails_4_1.gemfile

This file was deleted.

12 changes: 0 additions & 12 deletions gemfiles/rails_4_2.gemfile

This file was deleted.

12 changes: 0 additions & 12 deletions gemfiles/rails_5_0.gemfile

This file was deleted.

2 changes: 1 addition & 1 deletion gemfiles/rails_4_0.gemfile → gemfiles/rails_5_1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source "http://rubygems.org"

gem "rails", "~> 4.0.0"
gem "rails", "~> 5.1.0"

group :local do
gem "pry"
Expand Down
88 changes: 33 additions & 55 deletions lib/apartment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,49 @@
require 'forwardable'
require 'active_record'
require 'apartment/tenant'
require 'apartment/deprecation'

module Apartment

class << self

extend Forwardable

ACCESSOR_METHODS = [:use_schemas, :use_sql, :seed_after_create, :prepend_environment, :append_environment, :with_multi_server_setup ]
WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file]
ACCESSOR_METHODS = [
:use_sql, :seed_after_create, :tenant_decorator,
:force_reconnect_on_switch, :pool_per_config
]
WRITER_METHODS = [
:tenant_names, :database_schema_file, :excluded_models,
:persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants,
:seed_data_file, :default_tenant, :parallel_migration_threads
]
OTHER_METHODS = [:tenant_resolver, :resolver_class]

attr_accessor(*ACCESSOR_METHODS)
attr_writer(*WRITER_METHODS)

def_delegators :connection_class, :connection, :connection_config, :establish_connection
def_delegators :connection_class, :connection, :connection_config,
:establish_connection, :connection_handler

# configure apartment with available options
def configure
yield self if block_given?
end

def tenant_resolver
@tenant_resolver ||= @resolver_class.new(connection_config)
end

def tenant_resolver=(resolver_class)
remove_instance_variable(:@tenant_resolver) if instance_variable_defined?(:@tenant_resolver)
@resolver_class = resolver_class
end

def tenant_names
extract_tenant_config.keys.map(&:to_s)
@tenant_names.respond_to?(:call) ? @tenant_names.call : (@tenant_names || [])
end

def tenants_with_config
extract_tenant_config
end

def db_config_for(tenant)
(tenants_with_config[tenant] || connection_config).with_indifferent_access
end

# Whether or not db:migrate should also migrate tenants
# defaults to true
def db_migrate_tenants
Expand All @@ -49,11 +59,13 @@ def excluded_models
@excluded_models || []
end

def default_schema
@default_schema || "public" # TODO 'public' is postgres specific
def default_tenant
@default_tenant || tenant_resolver.init_config
end

def parallel_migration_threads
@parallel_migration_threads || 0
end
alias :default_tenant :default_schema
alias :default_tenant= :default_schema=

def persistent_schemas
@persistent_schemas || []
Expand All @@ -72,49 +84,15 @@ def database_schema_file
def seed_data_file
return @seed_data_file if defined?(@seed_data_file)

@seed_data_file = "#{Rails.root}/db/seeds.rb"
end

def tld_length
@tld_length || 1
@seed_data_file = Rails.root.join('db', 'seeds.rb')
end

# Reset all the config for Apartment
def reset
(ACCESSOR_METHODS + WRITER_METHODS).each{|method| remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}") }
end

def database_names
Apartment::Deprecation.warn "[Deprecation Warning] `database_names` is now deprecated, please use `tenant_names`"
tenant_names
end

def database_names=(names)
Apartment::Deprecation.warn "[Deprecation Warning] `database_names=` is now deprecated, please use `tenant_names=`"
self.tenant_names=(names)
end

def use_postgres_schemas
Apartment::Deprecation.warn "[Deprecation Warning] `use_postgresql_schemas` is now deprecated, please use `use_schemas`"
use_schemas
end

def use_postgres_schemas=(to_use_or_not_to_use)
Apartment::Deprecation.warn "[Deprecation Warning] `use_postgresql_schemas=` is now deprecated, please use `use_schemas=`"
self.use_schemas = to_use_or_not_to_use
end

def extract_tenant_config
return {} unless @tenant_names
values = @tenant_names.respond_to?(:call) ? @tenant_names.call : @tenant_names
unless values.is_a? Hash
values = values.each_with_object({}) do |tenant, hash|
hash[tenant] = connection_config
end
(ACCESSOR_METHODS + WRITER_METHODS + OTHER_METHODS).each do |method|
remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}")
end
values.with_indifferent_access
rescue ActiveRecord::StatementInvalid
{}

Thread.current[:_apartment_connection_specification_name] = nil
end
end

Expand Down
Loading