ros-apartment 2.3.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE.md +21 -0
  3. data/.gitignore +15 -0
  4. data/.pryrc +3 -0
  5. data/.rspec +4 -0
  6. data/.travis.yml +65 -0
  7. data/Appraisals +71 -0
  8. data/Gemfile +10 -0
  9. data/Guardfile +24 -0
  10. data/HISTORY.md +398 -0
  11. data/README.md +576 -0
  12. data/Rakefile +128 -0
  13. data/TODO.md +51 -0
  14. data/apartment.gemspec +46 -0
  15. data/docker-compose.yml +33 -0
  16. data/gemfiles/rails_4_2.gemfile +23 -0
  17. data/gemfiles/rails_5_0.gemfile +22 -0
  18. data/gemfiles/rails_5_1.gemfile +22 -0
  19. data/gemfiles/rails_5_2.gemfile +18 -0
  20. data/gemfiles/rails_6_0.gemfile +22 -0
  21. data/gemfiles/rails_master.gemfile +22 -0
  22. data/lib/apartment.rb +118 -0
  23. data/lib/apartment/adapters/abstract_adapter.rb +269 -0
  24. data/lib/apartment/adapters/abstract_jdbc_adapter.rb +18 -0
  25. data/lib/apartment/adapters/jdbc_mysql_adapter.rb +19 -0
  26. data/lib/apartment/adapters/jdbc_postgresql_adapter.rb +56 -0
  27. data/lib/apartment/adapters/mysql2_adapter.rb +71 -0
  28. data/lib/apartment/adapters/postgis_adapter.rb +12 -0
  29. data/lib/apartment/adapters/postgresql_adapter.rb +236 -0
  30. data/lib/apartment/adapters/sqlite3_adapter.rb +56 -0
  31. data/lib/apartment/console.rb +12 -0
  32. data/lib/apartment/deprecation.rb +10 -0
  33. data/lib/apartment/elevators/domain.rb +22 -0
  34. data/lib/apartment/elevators/first_subdomain.rb +17 -0
  35. data/lib/apartment/elevators/generic.rb +32 -0
  36. data/lib/apartment/elevators/host.rb +30 -0
  37. data/lib/apartment/elevators/host_hash.rb +22 -0
  38. data/lib/apartment/elevators/subdomain.rb +62 -0
  39. data/lib/apartment/migrator.rb +51 -0
  40. data/lib/apartment/railtie.rb +67 -0
  41. data/lib/apartment/reloader.rb +21 -0
  42. data/lib/apartment/tasks/enhancements.rb +57 -0
  43. data/lib/apartment/tenant.rb +66 -0
  44. data/lib/apartment/version.rb +3 -0
  45. data/lib/generators/apartment/install/USAGE +5 -0
  46. data/lib/generators/apartment/install/install_generator.rb +10 -0
  47. data/lib/generators/apartment/install/templates/apartment.rb +109 -0
  48. data/lib/tasks/apartment.rake +145 -0
  49. data/spec/adapters/jdbc_mysql_adapter_spec.rb +19 -0
  50. data/spec/adapters/jdbc_postgresql_adapter_spec.rb +41 -0
  51. data/spec/adapters/mysql2_adapter_spec.rb +59 -0
  52. data/spec/adapters/postgresql_adapter_spec.rb +61 -0
  53. data/spec/adapters/sqlite3_adapter_spec.rb +83 -0
  54. data/spec/apartment_spec.rb +11 -0
  55. data/spec/config/database.yml.sample +49 -0
  56. data/spec/dummy/Rakefile +7 -0
  57. data/spec/dummy/app/controllers/application_controller.rb +6 -0
  58. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  59. data/spec/dummy/app/models/company.rb +3 -0
  60. data/spec/dummy/app/models/user.rb +3 -0
  61. data/spec/dummy/app/views/application/index.html.erb +1 -0
  62. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  63. data/spec/dummy/config.ru +4 -0
  64. data/spec/dummy/config/application.rb +49 -0
  65. data/spec/dummy/config/boot.rb +11 -0
  66. data/spec/dummy/config/database.yml.sample +44 -0
  67. data/spec/dummy/config/environment.rb +5 -0
  68. data/spec/dummy/config/environments/development.rb +28 -0
  69. data/spec/dummy/config/environments/production.rb +51 -0
  70. data/spec/dummy/config/environments/test.rb +34 -0
  71. data/spec/dummy/config/initializers/apartment.rb +4 -0
  72. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  73. data/spec/dummy/config/initializers/inflections.rb +10 -0
  74. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  75. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  76. data/spec/dummy/config/initializers/session_store.rb +8 -0
  77. data/spec/dummy/config/locales/en.yml +5 -0
  78. data/spec/dummy/config/routes.rb +3 -0
  79. data/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb +39 -0
  80. data/spec/dummy/db/migrate/20111202022214_create_table_books.rb +14 -0
  81. data/spec/dummy/db/migrate/20180415260934_create_public_tokens.rb +13 -0
  82. data/spec/dummy/db/schema.rb +55 -0
  83. data/spec/dummy/db/seeds.rb +5 -0
  84. data/spec/dummy/db/seeds/import.rb +5 -0
  85. data/spec/dummy/public/404.html +26 -0
  86. data/spec/dummy/public/422.html +26 -0
  87. data/spec/dummy/public/500.html +26 -0
  88. data/spec/dummy/public/favicon.ico +0 -0
  89. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  90. data/spec/dummy/script/rails +6 -0
  91. data/spec/dummy_engine/.gitignore +8 -0
  92. data/spec/dummy_engine/Gemfile +15 -0
  93. data/spec/dummy_engine/Rakefile +34 -0
  94. data/spec/dummy_engine/bin/rails +12 -0
  95. data/spec/dummy_engine/config/initializers/apartment.rb +51 -0
  96. data/spec/dummy_engine/dummy_engine.gemspec +24 -0
  97. data/spec/dummy_engine/lib/dummy_engine.rb +4 -0
  98. data/spec/dummy_engine/lib/dummy_engine/engine.rb +4 -0
  99. data/spec/dummy_engine/lib/dummy_engine/version.rb +3 -0
  100. data/spec/dummy_engine/test/dummy/Rakefile +6 -0
  101. data/spec/dummy_engine/test/dummy/config.ru +4 -0
  102. data/spec/dummy_engine/test/dummy/config/application.rb +22 -0
  103. data/spec/dummy_engine/test/dummy/config/boot.rb +5 -0
  104. data/spec/dummy_engine/test/dummy/config/database.yml +25 -0
  105. data/spec/dummy_engine/test/dummy/config/environment.rb +5 -0
  106. data/spec/dummy_engine/test/dummy/config/environments/development.rb +37 -0
  107. data/spec/dummy_engine/test/dummy/config/environments/production.rb +78 -0
  108. data/spec/dummy_engine/test/dummy/config/environments/test.rb +39 -0
  109. data/spec/dummy_engine/test/dummy/config/initializers/assets.rb +8 -0
  110. data/spec/dummy_engine/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  111. data/spec/dummy_engine/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  112. data/spec/dummy_engine/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  113. data/spec/dummy_engine/test/dummy/config/initializers/inflections.rb +16 -0
  114. data/spec/dummy_engine/test/dummy/config/initializers/mime_types.rb +4 -0
  115. data/spec/dummy_engine/test/dummy/config/initializers/session_store.rb +3 -0
  116. data/spec/dummy_engine/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  117. data/spec/dummy_engine/test/dummy/config/locales/en.yml +23 -0
  118. data/spec/dummy_engine/test/dummy/config/routes.rb +56 -0
  119. data/spec/dummy_engine/test/dummy/config/secrets.yml +22 -0
  120. data/spec/examples/connection_adapter_examples.rb +42 -0
  121. data/spec/examples/generic_adapter_custom_configuration_example.rb +95 -0
  122. data/spec/examples/generic_adapter_examples.rb +163 -0
  123. data/spec/examples/schema_adapter_examples.rb +234 -0
  124. data/spec/integration/apartment_rake_integration_spec.rb +107 -0
  125. data/spec/integration/query_caching_spec.rb +81 -0
  126. data/spec/integration/use_within_an_engine_spec.rb +28 -0
  127. data/spec/schemas/v1.rb +16 -0
  128. data/spec/schemas/v2.rb +43 -0
  129. data/spec/schemas/v3.rb +49 -0
  130. data/spec/spec_helper.rb +61 -0
  131. data/spec/support/apartment_helpers.rb +43 -0
  132. data/spec/support/capybara_sessions.rb +15 -0
  133. data/spec/support/config.rb +10 -0
  134. data/spec/support/contexts.rb +52 -0
  135. data/spec/support/requirements.rb +35 -0
  136. data/spec/support/setup.rb +46 -0
  137. data/spec/tasks/apartment_rake_spec.rb +129 -0
  138. data/spec/tenant_spec.rb +190 -0
  139. data/spec/unit/config_spec.rb +112 -0
  140. data/spec/unit/elevators/domain_spec.rb +32 -0
  141. data/spec/unit/elevators/first_subdomain_spec.rb +24 -0
  142. data/spec/unit/elevators/generic_spec.rb +54 -0
  143. data/spec/unit/elevators/host_hash_spec.rb +32 -0
  144. data/spec/unit/elevators/host_spec.rb +89 -0
  145. data/spec/unit/elevators/subdomain_spec.rb +76 -0
  146. data/spec/unit/migrator_spec.rb +77 -0
  147. data/spec/unit/reloader_spec.rb +24 -0
  148. metadata +487 -0
@@ -0,0 +1,128 @@
1
+ require 'bundler' rescue 'You must `gem install bundler` and `bundle install` to run rake tasks'
2
+ Bundler.setup
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'appraisal'
6
+
7
+ require "rspec"
8
+ require "rspec/core/rake_task"
9
+
10
+ RSpec::Core::RakeTask.new(:spec => %w{ db:copy_credentials db:test:prepare }) do |spec|
11
+ spec.pattern = "spec/**/*_spec.rb"
12
+ # spec.rspec_opts = '--order rand:47078'
13
+ end
14
+
15
+ namespace :spec do
16
+ [:tasks, :unit, :adapters, :integration].each do |type|
17
+ RSpec::Core::RakeTask.new(type => :spec) do |spec|
18
+ spec.pattern = "spec/#{type}/**/*_spec.rb"
19
+ end
20
+ end
21
+ end
22
+
23
+ task :console do
24
+ require 'pry'
25
+ require 'apartment'
26
+ ARGV.clear
27
+ Pry.start
28
+ end
29
+
30
+ task :default => :spec
31
+
32
+ namespace :db do
33
+ namespace :test do
34
+ task :prepare => %w{postgres:drop_db postgres:build_db mysql:drop_db mysql:build_db}
35
+ end
36
+
37
+ desc "copy sample database credential files over if real files don't exist"
38
+ task :copy_credentials do
39
+ require 'fileutils'
40
+ apartment_db_file = 'spec/config/database.yml'
41
+ rails_db_file = 'spec/dummy/config/database.yml'
42
+
43
+ FileUtils.copy(apartment_db_file + '.sample', apartment_db_file, :verbose => true) unless File.exists?(apartment_db_file)
44
+ FileUtils.copy(rails_db_file + '.sample', rails_db_file, :verbose => true) unless File.exists?(rails_db_file)
45
+ end
46
+ end
47
+
48
+ namespace :postgres do
49
+ require 'active_record'
50
+ require "#{File.join(File.dirname(__FILE__), 'spec', 'support', 'config')}"
51
+
52
+ desc 'Build the PostgreSQL test databases'
53
+ task :build_db do
54
+ params = []
55
+ params << "-E UTF8"
56
+ params << pg_config['database']
57
+ params << "-U#{pg_config['username']}"
58
+ params << "-h#{pg_config['host']}" if pg_config['host']
59
+ params << "-p#{pg_config['port']}" if pg_config['port']
60
+ %x{ createdb #{params.join(' ')} } rescue "test db already exists"
61
+ ActiveRecord::Base.establish_connection pg_config
62
+ migrate
63
+ end
64
+
65
+ desc "drop the PostgreSQL test database"
66
+ task :drop_db do
67
+ puts "dropping database #{pg_config['database']}"
68
+ params = []
69
+ params << pg_config['database']
70
+ params << "-U#{pg_config['username']}"
71
+ params << "-h#{pg_config['host']}" if pg_config['host']
72
+ params << "-p#{pg_config['port']}" if pg_config['port']
73
+ %x{ dropdb #{params.join(' ')} }
74
+ end
75
+
76
+ end
77
+
78
+ namespace :mysql do
79
+ require 'active_record'
80
+ require "#{File.join(File.dirname(__FILE__), 'spec', 'support', 'config')}"
81
+
82
+ desc 'Build the MySQL test databases'
83
+ task :build_db do
84
+ params = []
85
+ params << "-h #{my_config['host']}" if my_config['host']
86
+ params << "-u #{my_config['username']}" if my_config['username']
87
+ params << "-p#{my_config['password']}" if my_config['password']
88
+ %x{ mysqladmin #{params.join(' ')} create #{my_config['database']} } rescue "test db already exists"
89
+ ActiveRecord::Base.establish_connection my_config
90
+ migrate
91
+ end
92
+
93
+ desc "drop the MySQL test database"
94
+ task :drop_db do
95
+ puts "dropping database #{my_config['database']}"
96
+ params = []
97
+ params << "-h #{my_config['host']}" if my_config['host']
98
+ params << "-u #{my_config['username']}" if my_config['username']
99
+ params << "-p#{my_config['password']}" if my_config['password']
100
+ %x{ mysqladmin #{params.join(' ')} drop #{my_config['database']} --force}
101
+ end
102
+
103
+ end
104
+
105
+ # TODO clean this up
106
+ def config
107
+ Apartment::Test.config['connections']
108
+ end
109
+
110
+ def pg_config
111
+ config['postgresql']
112
+ end
113
+
114
+ def my_config
115
+ config['mysql']
116
+ end
117
+
118
+ def activerecord_below_5_2?
119
+ ActiveRecord.version.release < Gem::Version.new('5.2.0')
120
+ end
121
+
122
+ def migrate
123
+ if activerecord_below_5_2?
124
+ ActiveRecord::Migrator.migrate('spec/dummy/db/migrate')
125
+ else
126
+ ActiveRecord::MigrationContext.new('spec/dummy/db/migrate').migrate
127
+ end
128
+ end
data/TODO.md ADDED
@@ -0,0 +1,51 @@
1
+ # Apartment TODOs
2
+
3
+ ### Below is a list of tasks in the approximate order to be completed of for Apartment
4
+ ### Any help along the way is greatly appreciated (on any items, not particularly in order)
5
+
6
+ 1. Apartment was originally written (and TDD'd) with just Postgresql in mind. Different adapters were added at a later date.
7
+ As such, the test suite is a bit of a mess. There's no formal structure for fully integration testing all adapters to ensure
8
+ proper quality and prevent regressions.
9
+
10
+ There's also a test order dependency as some tests run assuming a db connection and if that test randomly ran before a previous
11
+ one that makes the connection, it would fail.
12
+
13
+ I'm proposing the first thing to be done is to write up a standard, high livel integration test case that can be applied to all adapters
14
+ and makes no assumptions about implementation. It should ensure that each adapter conforms to the Apartment Interface and CRUD's properly.
15
+ It would be nice if a user can 'register' an adapter such that it would automatically be tested (nice to have). Otherwise one could just use
16
+ a shared behaviour to run through all of this.
17
+
18
+ Then, I'd like to see all of the implementation specific tests just in their own test file for each adapter (ie the postgresql schema adapter checks a lot of things with `schema_search_path`)
19
+
20
+ This should ensure that going forward nothing breaks, and we should *ideally* be able to randomize the test order
21
+
22
+ 2. <del>`Apartment::Database` is the wrong abstraction. When dealing with a multi-tenanted system, users shouldn't thing about 'Databases', they should
23
+ think about Tenants. I proprose that we deprecate the `Apartment::Database` constant in favour of `Apartment::Tenant` for a nicer abstraction. See
24
+ http://myronmars.to/n/dev-blog/2011/09/deprecating-constants-and-classes-in-ruby for ideas on how to achieve this.</del>
25
+
26
+ 4. Apartment::Database.process should be deprecated in favour of just passing a block to `switch`
27
+ 5. Apartment::Database.switch should be renamed to switch! to indicate that using it on its own has side effects
28
+
29
+ 6. Migrations right now can be a bit of a pain. Apartment currently migrates a single tenant completely up to date, then goes onto the next. If one of these
30
+ migrations fails on a tenant, the previous one does NOT get reverted and leaves you in an awkward state. Ideally we'd want to wrap all of the migrations in
31
+ a transaction so if one fails, the whole thing reverts. Once we can ensure an all-or-nothing approach to migrations, we can optimize the migration strategy
32
+ to not even iterate over the tenants if there are no migrations to run on public.
33
+
34
+ 7. Apartment has be come one of the most popular/robust Multi-tenant gems for Rails, but it still doesn't work for everyone's use case. It's fairly limited in implementation to either schema based (ie postgresql schemas) or connection based. I'd like to abstract out these implementation details such that one could write a pluggable strategy for Apartment and choose it based on a config selection (something like `config.strategy = :schema`). The next implementation I'd like to see is a scoped based approach that uses a `tenant_id` scoping on all records for multi-tenancy. This is probably the most popular multi-tenant approach and is db independent and really the simplest mechanism for a type of multi-tenancy.
35
+
36
+ 8. Right now excluded tables still live in all tenanted environments. This is basically because it doesn't matter if they're there, we always query from the public.
37
+ It's a bit of an annoyance though and confuses lots of people. I'd love to see only tenanted tables in the tenants and only excluded tables in the public tenant.
38
+ This will be hard because Rails uses public to generate schema.rb. One idea is to have an `excluded` schema that holds all the excluded models and the public can
39
+ maintain everything.
40
+
41
+ 9. This one is pretty lofty, but I'd also like to abstract out the fact that Apartment uses ActiveRecord. With the new DataMapper coming out soon and other popular
42
+ DBMS's (ie. mongo, couch etc...), it'd be nice if Apartment could be the de-facto interface for multi-tenancy on these systems.
43
+
44
+
45
+ ===================
46
+
47
+ Quick TODOs
48
+
49
+ 1. `default_tenant` should be up to the adapter, not the Apartment class, deprecate `default_schema`
50
+ 2. deprecation.rb rescues everything, we have a hard dependency on ActiveSupport so this is unnecessary
51
+ 3.
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $: << File.expand_path("../lib", __FILE__)
3
+ require "apartment/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{ros-apartment}
7
+ s.version = Apartment::VERSION
8
+
9
+ s.authors = ["Ryan Brunner", "Brad Robertson"]
10
+ s.summary = %q{A Ruby gem for managing database multitenancy}
11
+ s.description = %q{Apartment allows Rack applications to deal with database multitenancy through ActiveRecord}
12
+ s.email = ["ryan@influitive.com", "brad@influitive.com"]
13
+ s.files = `git ls-files`.split($/)
14
+ s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+ s.require_paths = ["lib"]
17
+
18
+ s.homepage = %q{https://github.com/rails-on-services/apartment}
19
+ s.licenses = ["MIT"]
20
+
21
+ # must be >= 3.1.2 due to bug in prepared_statements
22
+ s.add_dependency 'activerecord', '>= 3.1.2', '< 6.1'
23
+ s.add_dependency 'rack', '>= 1.3.6'
24
+ s.add_dependency 'public_suffix', '>= 2'
25
+ s.add_dependency 'parallel', '>= 0.7.1'
26
+
27
+ s.add_development_dependency 'appraisal'
28
+ s.add_development_dependency 'rake', '~> 0.9'
29
+ s.add_development_dependency 'rspec', '~> 3.4'
30
+ s.add_development_dependency 'rspec-rails', '~> 3.4'
31
+ s.add_development_dependency 'capybara', '~> 2.0'
32
+ s.add_development_dependency 'bundler', '>= 1.3', '< 2.0'
33
+
34
+ if defined?(JRUBY_VERSION)
35
+ s.add_development_dependency 'activerecord-jdbc-adapter'
36
+ s.add_development_dependency 'activerecord-jdbcpostgresql-adapter'
37
+ s.add_development_dependency 'activerecord-jdbcmysql-adapter'
38
+ s.add_development_dependency 'jdbc-postgres'
39
+ s.add_development_dependency 'jdbc-mysql'
40
+ s.add_development_dependency 'jruby-openssl'
41
+ else
42
+ s.add_development_dependency 'mysql2'
43
+ s.add_development_dependency 'pg'
44
+ s.add_development_dependency 'sqlite3', '~> 1.3.6'
45
+ end
46
+ end
@@ -0,0 +1,33 @@
1
+ version: '2.3'
2
+ services:
3
+ postgresql:
4
+ image: postgres:9.5.12
5
+ environment:
6
+ POSTGRES_PASSWORD: ""
7
+ ports:
8
+ - "5432:5432"
9
+ healthcheck:
10
+ test: pg_isready -U postgres
11
+ start_period: 10s
12
+ interval: 10s
13
+ timeout: 30s
14
+ retries: 3
15
+ mysql:
16
+ image: mysql:5.7
17
+ environment:
18
+ MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
19
+ ports:
20
+ - "3306:3306"
21
+ healthcheck:
22
+ test: mysqladmin -h 127.0.0.1 -uroot ping
23
+ start_period: 15s
24
+ interval: 10s
25
+ timeout: 30s
26
+ retries: 3
27
+ healthcheck:
28
+ image: busybox
29
+ depends_on:
30
+ postgresql:
31
+ condition: service_healthy
32
+ mysql:
33
+ condition: service_healthy
@@ -0,0 +1,23 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 4.2.0"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :ruby do
13
+ gem "pg", "< 1.0.0"
14
+ gem "mysql2", "~> 0.4.0"
15
+ end
16
+
17
+ platforms :jruby do
18
+ gem "activerecord-jdbc-adapter", "~> 1.3"
19
+ gem "activerecord-jdbcpostgresql-adapter", "~> 1.3"
20
+ gem "activerecord-jdbcmysql-adapter", "~> 1.3"
21
+ end
22
+
23
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 5.0.0"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :ruby do
13
+ gem "pg", "< 1.0.0"
14
+ end
15
+
16
+ platforms :jruby do
17
+ gem "activerecord-jdbc-adapter", "~> 50.0"
18
+ gem "activerecord-jdbcpostgresql-adapter", "~> 50.0"
19
+ gem "activerecord-jdbcmysql-adapter", "~> 50.0"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 5.1.0"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :ruby do
13
+ gem "pg", "< 1.0.0"
14
+ end
15
+
16
+ platforms :jruby do
17
+ gem "activerecord-jdbc-adapter", "~> 51.0"
18
+ gem "activerecord-jdbcpostgresql-adapter", "~> 51.0"
19
+ gem "activerecord-jdbcmysql-adapter", "~> 51.0"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 5.2.0"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :jruby do
13
+ gem "activerecord-jdbc-adapter", "~> 52.0"
14
+ gem "activerecord-jdbcpostgresql-adapter", "~> 52.0"
15
+ gem "activerecord-jdbcmysql-adapter", "~> 52.0"
16
+ end
17
+
18
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0.rc1"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :ruby do
13
+ gem "sqlite3", "~> 1.4"
14
+ end
15
+
16
+ platforms :jruby do
17
+ gem "activerecord-jdbc-adapter", "~> 60.0.rc1"
18
+ gem "activerecord-jdbcpostgresql-adapter", "~> 60.0.rc1"
19
+ gem "activerecord-jdbcmysql-adapter", "~> 60.0.rc1"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,22 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "rails", git: "https://github.com/rails/rails.git"
6
+
7
+ group :local do
8
+ gem "pry"
9
+ gem "guard-rspec", "~> 4.2"
10
+ end
11
+
12
+ platforms :ruby do
13
+ gem "sqlite3", "~> 1.4"
14
+ end
15
+
16
+ platforms :jruby do
17
+ gem "activerecord-jdbc-adapter", "~> 52.0"
18
+ gem "activerecord-jdbcpostgresql-adapter", "~> 52.0"
19
+ gem "activerecord-jdbcmysql-adapter", "~> 52.0"
20
+ end
21
+
22
+ gemspec path: "../"
@@ -0,0 +1,118 @@
1
+ require 'apartment/railtie' if defined?(Rails)
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'forwardable'
4
+ require 'active_record'
5
+ require 'apartment/tenant'
6
+
7
+ module Apartment
8
+
9
+ class << self
10
+
11
+ extend Forwardable
12
+
13
+ ACCESSOR_METHODS = [:use_schemas, :use_sql, :seed_after_create, :prepend_environment, :append_environment, :with_multi_server_setup]
14
+ WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file, :parallel_migration_threads, :pg_excluded_names]
15
+
16
+ attr_accessor(*ACCESSOR_METHODS)
17
+ attr_writer(*WRITER_METHODS)
18
+
19
+ def_delegators :connection_class, :connection, :connection_config, :establish_connection
20
+
21
+ # configure apartment with available options
22
+ def configure
23
+ yield self if block_given?
24
+ end
25
+
26
+ def tenant_names
27
+ extract_tenant_config.keys.map(&:to_s)
28
+ end
29
+
30
+ def tenants_with_config
31
+ extract_tenant_config
32
+ end
33
+
34
+ def db_config_for(tenant)
35
+ (tenants_with_config[tenant] || connection_config).with_indifferent_access
36
+ end
37
+
38
+ # Whether or not db:migrate should also migrate tenants
39
+ # defaults to true
40
+ def db_migrate_tenants
41
+ return @db_migrate_tenants if defined?(@db_migrate_tenants)
42
+
43
+ @db_migrate_tenants = true
44
+ end
45
+
46
+ # Default to empty array
47
+ def excluded_models
48
+ @excluded_models || []
49
+ end
50
+
51
+ def default_schema
52
+ @default_schema || "public" # TODO 'public' is postgres specific
53
+ end
54
+
55
+ def parallel_migration_threads
56
+ @parallel_migration_threads || 0
57
+ end
58
+ alias :default_tenant :default_schema
59
+ alias :default_tenant= :default_schema=
60
+
61
+ def persistent_schemas
62
+ @persistent_schemas || []
63
+ end
64
+
65
+ def connection_class
66
+ @connection_class || ActiveRecord::Base
67
+ end
68
+
69
+ def database_schema_file
70
+ return @database_schema_file if defined?(@database_schema_file)
71
+
72
+ @database_schema_file = Rails.root.join('db', 'schema.rb')
73
+ end
74
+
75
+ def seed_data_file
76
+ return @seed_data_file if defined?(@seed_data_file)
77
+
78
+ @seed_data_file = "#{Rails.root}/db/seeds.rb"
79
+ end
80
+
81
+ def pg_excluded_names
82
+ @pg_excluded_names || []
83
+ end
84
+
85
+ # Reset all the config for Apartment
86
+ def reset
87
+ (ACCESSOR_METHODS + WRITER_METHODS).each{|method| remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}") }
88
+ end
89
+
90
+ def extract_tenant_config
91
+ return {} unless @tenant_names
92
+ values = @tenant_names.respond_to?(:call) ? @tenant_names.call : @tenant_names
93
+ unless values.is_a? Hash
94
+ values = values.each_with_object({}) do |tenant, hash|
95
+ hash[tenant] = connection_config
96
+ end
97
+ end
98
+ values.with_indifferent_access
99
+ rescue ActiveRecord::StatementInvalid
100
+ {}
101
+ end
102
+ end
103
+
104
+ # Exceptions
105
+ ApartmentError = Class.new(StandardError)
106
+
107
+ # Raised when apartment cannot find the adapter specified in <tt>config/database.yml</tt>
108
+ AdapterNotFound = Class.new(ApartmentError)
109
+
110
+ # Raised when apartment cannot find the file to be loaded
111
+ FileNotFound = Class.new(ApartmentError)
112
+
113
+ # Tenant specified is unknown
114
+ TenantNotFound = Class.new(ApartmentError)
115
+
116
+ # The Tenant attempting to be created already exists
117
+ TenantExists = Class.new(ApartmentError)
118
+ end