dr-apartment 0.14.1
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.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +22 -0
- data/HISTORY.md +133 -0
- data/README.md +152 -0
- data/Rakefile +79 -0
- data/apartment.gemspec +32 -0
- data/lib/apartment.rb +69 -0
- data/lib/apartment/adapters/abstract_adapter.rb +176 -0
- data/lib/apartment/adapters/jdbcpostgresql_adapter.rb +115 -0
- data/lib/apartment/adapters/mysql_adapter.rb +18 -0
- data/lib/apartment/adapters/postgresql_adapter.rb +114 -0
- data/lib/apartment/console.rb +12 -0
- data/lib/apartment/database.rb +57 -0
- data/lib/apartment/delayed_job/active_record.rb +20 -0
- data/lib/apartment/delayed_job/enqueue.rb +20 -0
- data/lib/apartment/delayed_job/hooks.rb +25 -0
- data/lib/apartment/delayed_job/requirements.rb +23 -0
- data/lib/apartment/elevators/subdomain.rb +27 -0
- data/lib/apartment/migrator.rb +23 -0
- data/lib/apartment/railtie.rb +54 -0
- data/lib/apartment/reloader.rb +24 -0
- data/lib/apartment/version.rb +3 -0
- data/lib/tasks/apartment.rake +70 -0
- data/spec/adapters/mysql_adapter_spec.rb +36 -0
- data/spec/adapters/postgresql_adapter_spec.rb +137 -0
- data/spec/apartment_spec.rb +11 -0
- data/spec/config/database.yml +13 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +6 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/company.rb +3 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/application/index.html.erb +1 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +47 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +16 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +26 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +35 -0
- data/spec/dummy/config/initializers/apartment.rb +4 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb +37 -0
- data/spec/dummy/db/migrate/20111202022214_create_table_books.rb +13 -0
- data/spec/dummy/db/schema.rb +48 -0
- data/spec/dummy/db/seeds.rb +8 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/fake_dj_class.rb +6 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/integration/apartment_rake_integration_spec.rb +74 -0
- data/spec/integration/database_integration_spec.rb +200 -0
- data/spec/integration/delayed_job_integration_spec.rb +100 -0
- data/spec/integration/middleware/subdomain_elevator_spec.rb +63 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/apartment_helpers.rb +32 -0
- data/spec/support/capybara_sessions.rb +15 -0
- data/spec/support/config.rb +11 -0
- data/spec/tasks/apartment_rake_spec.rb +118 -0
- data/spec/unit/config_spec.rb +78 -0
- data/spec/unit/middleware/subdomain_elevator_spec.rb +20 -0
- data/spec/unit/migrator_spec.rb +87 -0
- data/spec/unit/reloader_spec.rb +22 -0
- metadata +144 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Apartment::Elevators::Subdomain do
|
4
|
+
|
5
|
+
let(:company){ mock_model(Company, :subdomain => 'foo').as_null_object }
|
6
|
+
let(:domain){ "http://#{company.subdomain}.domain.com" }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Apartment.seed_after_create = false
|
10
|
+
Apartment.use_postgres_schemas = true
|
11
|
+
|
12
|
+
Apartment::Database.create(company.subdomain)
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
Apartment::Test.drop_schema(company.subdomain)
|
17
|
+
end
|
18
|
+
|
19
|
+
context "single request" do
|
20
|
+
it "should switch the db" do
|
21
|
+
ActiveRecord::Base.connection.schema_search_path.should_not == 'foo'
|
22
|
+
|
23
|
+
visit(domain)
|
24
|
+
ActiveRecord::Base.connection.schema_search_path.should == company.subdomain
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "simultaneous requests" do
|
29
|
+
let(:company2){ mock_model(Company, :subdomain => 'bar').as_null_object }
|
30
|
+
let(:domain2){ "http://#{company2.subdomain}.domain.com" }
|
31
|
+
|
32
|
+
before do
|
33
|
+
Apartment::Database.create(company2.subdomain)
|
34
|
+
# Create some users for each db
|
35
|
+
Apartment::Database.process(company.subdomain) do
|
36
|
+
@c1_user_count = (2 + rand(2)).times{ User.create }
|
37
|
+
end
|
38
|
+
|
39
|
+
Apartment::Database.process(company2.subdomain) do
|
40
|
+
@c2_user_count = (@c1_user_count + 2).times{ User.create }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
after do
|
45
|
+
Apartment::Test.drop_schema(company2.subdomain)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should fetch the correct user count for each session based on subdomain" do
|
49
|
+
visit(domain)
|
50
|
+
|
51
|
+
in_new_session do |session|
|
52
|
+
session.visit(domain2)
|
53
|
+
User.count.should == @c2_user_count
|
54
|
+
end
|
55
|
+
|
56
|
+
visit(domain)
|
57
|
+
User.count.should == @c1_user_count
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
# Configure Rails Environment
|
4
|
+
ENV["RAILS_ENV"] = "test"
|
5
|
+
|
6
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
7
|
+
require "rspec/rails"
|
8
|
+
require 'capybara/rspec'
|
9
|
+
require 'capybara/rails'
|
10
|
+
|
11
|
+
ActionMailer::Base.delivery_method = :test
|
12
|
+
ActionMailer::Base.perform_deliveries = true
|
13
|
+
ActionMailer::Base.default_url_options[:host] = "test.com"
|
14
|
+
|
15
|
+
Rails.backtrace_cleaner.remove_silencers!
|
16
|
+
|
17
|
+
# Load support files
|
18
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
19
|
+
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
|
23
|
+
config.include RSpec::Integration::CapybaraSessions, :type => :request
|
24
|
+
|
25
|
+
config.before(:all) do
|
26
|
+
# Ensure that each test starts with a clean connection
|
27
|
+
# Necessary as some tests will leak things like current_schema into the next test
|
28
|
+
ActiveRecord::Base.clear_all_connections!
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Apartment
|
2
|
+
module Test
|
3
|
+
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def reset
|
7
|
+
Apartment.excluded_models = nil
|
8
|
+
Apartment.use_postgres_schemas = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def drop_schema(schema)
|
12
|
+
ActiveRecord::Base.silence{ ActiveRecord::Base.connection.execute("DROP SCHEMA IF EXISTS #{schema} CASCADE") } rescue true
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_schema(schema)
|
16
|
+
ActiveRecord::Base.connection.execute("CREATE SCHEMA #{schema}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_schema
|
20
|
+
silence_stream(STDOUT){ load("#{Rails.root}/db/schema.rb") }
|
21
|
+
end
|
22
|
+
|
23
|
+
def migrate
|
24
|
+
ActiveRecord::Migrator.migrate(Rails.root + ActiveRecord::Migrator.migrations_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def rollback
|
28
|
+
ActiveRecord::Migrator.rollback(Rails.root + ActiveRecord::Migrator.migrations_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
describe "apartment rake tasks" do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@rake = Rake::Application.new
|
8
|
+
Rake.application = @rake
|
9
|
+
load 'tasks/apartment.rake'
|
10
|
+
# stub out rails tasks
|
11
|
+
Rake::Task.define_task('db:migrate')
|
12
|
+
Rake::Task.define_task('db:seed')
|
13
|
+
Rake::Task.define_task('db:rollback')
|
14
|
+
Rake::Task.define_task('db:migrate:up')
|
15
|
+
Rake::Task.define_task('db:migrate:down')
|
16
|
+
Rake::Task.define_task('db:migrate:redo')
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
Rake.application = nil
|
21
|
+
ENV['VERSION'] = nil # linux users reported env variable carrying on between tests
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:version){ '1234' }
|
25
|
+
|
26
|
+
context 'database migration' do
|
27
|
+
|
28
|
+
let(:database_names){ ['company1', 'company2', 'company3'] }
|
29
|
+
let(:db_count){ database_names.length }
|
30
|
+
|
31
|
+
before do
|
32
|
+
Apartment.stub(:database_names).and_return database_names
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "apartment:migrate" do
|
36
|
+
before do
|
37
|
+
ActiveRecord::Migrator.stub(:migrate) # don't care about this
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should migrate public and all multi-tenant dbs" do
|
41
|
+
Apartment::Migrator.should_receive(:migrate).exactly(db_count).times
|
42
|
+
@rake['apartment:migrate'].invoke
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "apartment:migrate:up" do
|
47
|
+
|
48
|
+
context "without a version" do
|
49
|
+
before do
|
50
|
+
ENV['VERSION'] = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
it "requires a version to migrate to" do
|
54
|
+
lambda{
|
55
|
+
@rake['apartment:migrate:up'].invoke
|
56
|
+
}.should raise_error("VERSION is required")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "with version" do
|
61
|
+
|
62
|
+
before do
|
63
|
+
ENV['VERSION'] = version
|
64
|
+
end
|
65
|
+
|
66
|
+
it "migrates up to a specific version" do
|
67
|
+
Apartment::Migrator.should_receive(:run).with(:up, anything, version.to_i).exactly(db_count).times
|
68
|
+
@rake['apartment:migrate:up'].invoke
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "apartment:migrate:down" do
|
74
|
+
|
75
|
+
context "without a version" do
|
76
|
+
before do
|
77
|
+
ENV['VERSION'] = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "requires a version to migrate to" do
|
81
|
+
lambda{
|
82
|
+
@rake['apartment:migrate:down'].invoke
|
83
|
+
}.should raise_error("VERSION is required")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "with version" do
|
88
|
+
|
89
|
+
before do
|
90
|
+
ENV['VERSION'] = version
|
91
|
+
end
|
92
|
+
|
93
|
+
it "migrates up to a specific version" do
|
94
|
+
Apartment::Migrator.should_receive(:run).with(:down, anything, version.to_i).exactly(db_count).times
|
95
|
+
@rake['apartment:migrate:down'].invoke
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "apartment:rollback" do
|
101
|
+
|
102
|
+
let(:step){ '3' }
|
103
|
+
|
104
|
+
it "should rollback dbs" do
|
105
|
+
Apartment::Migrator.should_receive(:rollback).exactly(db_count).times
|
106
|
+
@rake['apartment:rollback'].invoke
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should rollback dbs STEP amt" do
|
110
|
+
Apartment::Migrator.should_receive(:rollback).with(anything, step.to_i).exactly(db_count).times
|
111
|
+
ENV['STEP'] = step
|
112
|
+
@rake['apartment:rollback'].invoke
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Apartment do
|
4
|
+
|
5
|
+
describe "#config" do
|
6
|
+
|
7
|
+
let(:excluded_models){ [Company] }
|
8
|
+
|
9
|
+
after do
|
10
|
+
Apartment::Test.reset
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should yield the Apartment object" do
|
14
|
+
Apartment.configure do |config|
|
15
|
+
config.excluded_models = []
|
16
|
+
config.should == Apartment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set excluded models" do
|
21
|
+
Apartment.configure do |config|
|
22
|
+
config.excluded_models = excluded_models
|
23
|
+
end
|
24
|
+
Apartment.excluded_models.should == excluded_models
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should set postgres_schemas" do
|
28
|
+
Apartment.configure do |config|
|
29
|
+
config.excluded_models = []
|
30
|
+
config.use_postgres_schemas = false
|
31
|
+
end
|
32
|
+
Apartment.use_postgres_schemas.should be_false
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set seed_after_create" do
|
36
|
+
Apartment.configure do |config|
|
37
|
+
config.excluded_models = []
|
38
|
+
config.seed_after_create = true
|
39
|
+
end
|
40
|
+
Apartment.seed_after_create.should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
context "databases" do
|
44
|
+
it "should return object if it doesnt respond_to call" do
|
45
|
+
database_names = ['users', 'companies']
|
46
|
+
|
47
|
+
Apartment.configure do |config|
|
48
|
+
config.excluded_models = []
|
49
|
+
config.database_names = database_names
|
50
|
+
end
|
51
|
+
Apartment.database_names.should == database_names
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should invoke the proc if appropriate" do
|
55
|
+
database_names = lambda{ ['users', 'users'] }
|
56
|
+
database_names.should_receive(:call)
|
57
|
+
|
58
|
+
Apartment.configure do |config|
|
59
|
+
config.excluded_models = []
|
60
|
+
config.database_names = database_names
|
61
|
+
end
|
62
|
+
Apartment.database_names
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return the invoked proc if appropriate" do
|
66
|
+
dbs = lambda{ Company.scoped }
|
67
|
+
|
68
|
+
Apartment.configure do |config|
|
69
|
+
config.excluded_models = []
|
70
|
+
config.database_names = dbs
|
71
|
+
end
|
72
|
+
|
73
|
+
Apartment.database_names.should == Company.scoped
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Apartment::Elevators::Subdomain do
|
4
|
+
|
5
|
+
describe "#subdomain" do
|
6
|
+
it "should parse subdomain" do
|
7
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com')
|
8
|
+
elevator = Apartment::Elevators::Subdomain.new(nil)
|
9
|
+
elevator.subdomain(request).should == 'foo'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return nil when no subdomain" do
|
13
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'bar.com')
|
14
|
+
elevator = Apartment::Elevators::Subdomain.new(nil)
|
15
|
+
elevator.subdomain(request).should be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Apartment::Migrator do
|
4
|
+
|
5
|
+
let(:config){ Apartment::Test.config['connections']['postgresql'].symbolize_keys }
|
6
|
+
let(:schema_name){ 'some_db_schema' }
|
7
|
+
let(:version){ 20110613152810 } # note this is brittle! I've literally just taken the version of the one migration I made... don't change this version
|
8
|
+
|
9
|
+
before do
|
10
|
+
ActiveRecord::Base.establish_connection config
|
11
|
+
Apartment::Database.stub(:config).and_return config # Use postgresql config for this test
|
12
|
+
@original_schema = ActiveRecord::Base.connection.schema_search_path
|
13
|
+
|
14
|
+
Apartment.configure do |config|
|
15
|
+
config.use_postgres_schemas = true
|
16
|
+
config.excluded_models = []
|
17
|
+
config.database_names = [schema_name]
|
18
|
+
end
|
19
|
+
|
20
|
+
Apartment::Database.create schema_name # create the schema
|
21
|
+
migrations_path = Rails.root + ActiveRecord::Migrator.migrations_path # tell AR where the real migrations are
|
22
|
+
ActiveRecord::Migrator.stub(:migrations_path).and_return(migrations_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
Apartment::Test.drop_schema(schema_name)
|
27
|
+
end
|
28
|
+
|
29
|
+
context "postgresql" do
|
30
|
+
|
31
|
+
context "using schemas" do
|
32
|
+
|
33
|
+
describe "#migrate" do
|
34
|
+
it "should connect to new db, then reset when done" do
|
35
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(schema_name).once
|
36
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(@original_schema).once
|
37
|
+
Apartment::Migrator.migrate(schema_name)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should migrate db" do
|
41
|
+
ActiveRecord::Migrator.should_receive(:migrate)
|
42
|
+
Apartment::Migrator.migrate(schema_name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#run" do
|
47
|
+
context "up" do
|
48
|
+
|
49
|
+
it "should connect to new db, then reset when done" do
|
50
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(schema_name).once
|
51
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(@original_schema).once
|
52
|
+
Apartment::Migrator.run(:up, schema_name, version)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should migrate to a version" do
|
56
|
+
ActiveRecord::Migrator.should_receive(:run).with(:up, anything, version)
|
57
|
+
Apartment::Migrator.run(:up, schema_name, version)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "down" do
|
62
|
+
|
63
|
+
it "should connect to new db, then reset when done" do
|
64
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(schema_name).once
|
65
|
+
ActiveRecord::Base.connection.should_receive(:schema_search_path=).with(@original_schema).once
|
66
|
+
Apartment::Migrator.run(:down, schema_name, version)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should migrate to a version" do
|
70
|
+
ActiveRecord::Migrator.should_receive(:run).with(:down, anything, version)
|
71
|
+
Apartment::Migrator.run(:down, schema_name, version)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#rollback" do
|
77
|
+
let(:steps){ 3 }
|
78
|
+
|
79
|
+
it "should rollback the db" do
|
80
|
+
ActiveRecord::Migrator.should_receive(:rollback).with(anything, steps)
|
81
|
+
Apartment::Migrator.rollback(schema_name, steps)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|