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.
Files changed (78) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +22 -0
  5. data/HISTORY.md +133 -0
  6. data/README.md +152 -0
  7. data/Rakefile +79 -0
  8. data/apartment.gemspec +32 -0
  9. data/lib/apartment.rb +69 -0
  10. data/lib/apartment/adapters/abstract_adapter.rb +176 -0
  11. data/lib/apartment/adapters/jdbcpostgresql_adapter.rb +115 -0
  12. data/lib/apartment/adapters/mysql_adapter.rb +18 -0
  13. data/lib/apartment/adapters/postgresql_adapter.rb +114 -0
  14. data/lib/apartment/console.rb +12 -0
  15. data/lib/apartment/database.rb +57 -0
  16. data/lib/apartment/delayed_job/active_record.rb +20 -0
  17. data/lib/apartment/delayed_job/enqueue.rb +20 -0
  18. data/lib/apartment/delayed_job/hooks.rb +25 -0
  19. data/lib/apartment/delayed_job/requirements.rb +23 -0
  20. data/lib/apartment/elevators/subdomain.rb +27 -0
  21. data/lib/apartment/migrator.rb +23 -0
  22. data/lib/apartment/railtie.rb +54 -0
  23. data/lib/apartment/reloader.rb +24 -0
  24. data/lib/apartment/version.rb +3 -0
  25. data/lib/tasks/apartment.rake +70 -0
  26. data/spec/adapters/mysql_adapter_spec.rb +36 -0
  27. data/spec/adapters/postgresql_adapter_spec.rb +137 -0
  28. data/spec/apartment_spec.rb +11 -0
  29. data/spec/config/database.yml +13 -0
  30. data/spec/dummy/Rakefile +7 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +6 -0
  32. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  33. data/spec/dummy/app/models/company.rb +3 -0
  34. data/spec/dummy/app/models/user.rb +3 -0
  35. data/spec/dummy/app/views/application/index.html.erb +1 -0
  36. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  37. data/spec/dummy/config.ru +4 -0
  38. data/spec/dummy/config/application.rb +47 -0
  39. data/spec/dummy/config/boot.rb +10 -0
  40. data/spec/dummy/config/database.yml +16 -0
  41. data/spec/dummy/config/environment.rb +5 -0
  42. data/spec/dummy/config/environments/development.rb +26 -0
  43. data/spec/dummy/config/environments/production.rb +49 -0
  44. data/spec/dummy/config/environments/test.rb +35 -0
  45. data/spec/dummy/config/initializers/apartment.rb +4 -0
  46. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  47. data/spec/dummy/config/initializers/inflections.rb +10 -0
  48. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  49. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  50. data/spec/dummy/config/initializers/session_store.rb +8 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/routes.rb +3 -0
  53. data/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb +37 -0
  54. data/spec/dummy/db/migrate/20111202022214_create_table_books.rb +13 -0
  55. data/spec/dummy/db/schema.rb +48 -0
  56. data/spec/dummy/db/seeds.rb +8 -0
  57. data/spec/dummy/db/test.sqlite3 +0 -0
  58. data/spec/dummy/lib/fake_dj_class.rb +6 -0
  59. data/spec/dummy/public/404.html +26 -0
  60. data/spec/dummy/public/422.html +26 -0
  61. data/spec/dummy/public/500.html +26 -0
  62. data/spec/dummy/public/favicon.ico +0 -0
  63. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  64. data/spec/dummy/script/rails +6 -0
  65. data/spec/integration/apartment_rake_integration_spec.rb +74 -0
  66. data/spec/integration/database_integration_spec.rb +200 -0
  67. data/spec/integration/delayed_job_integration_spec.rb +100 -0
  68. data/spec/integration/middleware/subdomain_elevator_spec.rb +63 -0
  69. data/spec/spec_helper.rb +31 -0
  70. data/spec/support/apartment_helpers.rb +32 -0
  71. data/spec/support/capybara_sessions.rb +15 -0
  72. data/spec/support/config.rb +11 -0
  73. data/spec/tasks/apartment_rake_spec.rb +118 -0
  74. data/spec/unit/config_spec.rb +78 -0
  75. data/spec/unit/middleware/subdomain_elevator_spec.rb +20 -0
  76. data/spec/unit/migrator_spec.rb +87 -0
  77. data/spec/unit/reloader_spec.rb +22 -0
  78. 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
@@ -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,15 @@
1
+ module RSpec
2
+ module Integration
3
+ module CapybaraSessions
4
+
5
+ def in_new_session(&block)
6
+ yield new_session
7
+ end
8
+
9
+ def new_session
10
+ Capybara::Session.new(Capybara.current_driver, Capybara.app)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Apartment
2
+
3
+ module Test
4
+
5
+ def self.config
6
+ @config ||= YAML.load_file('spec/config/database.yml')
7
+ end
8
+
9
+ end
10
+
11
+ 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