apartment 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +21 -0
- data/.travis.yml +4 -7
- data/HISTORY.md +5 -0
- data/README.md +57 -9
- data/apartment.gemspec +2 -6
- data/lib/apartment.rb +5 -1
- data/lib/apartment/adapters/postgresql_adapter.rb +3 -2
- data/lib/apartment/elevators/domain.rb +3 -1
- data/lib/apartment/elevators/host.rb +30 -0
- data/lib/apartment/elevators/subdomain.rb +1 -1
- data/lib/apartment/tasks/enhancements.rb +36 -15
- data/lib/apartment/version.rb +1 -1
- data/lib/generators/apartment/install/templates/apartment.rb +2 -0
- data/lib/tasks/apartment.rake +24 -5
- data/spec/tasks/apartment_rake_spec.rb +9 -0
- data/spec/unit/elevators/host_spec.rb +89 -0
- metadata +24 -8
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6db463e2bcd7fcb50414759a87c1b77ccb959e04
|
4
|
+
data.tar.gz: c614f5ba93fd4e5e779f07a08aaafa4118bd5025
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bacabc90c5c61c06b266bfa9bfb4d0cf0f3dedfcdba36362894848b065a5bcd6b174badf9d696a97aeadb7589d2063c3e28ef6249ec54bae721fa7914538fbc
|
7
|
+
data.tar.gz: b2a1c68f71969fd0e87bc0cfaebdcfd7f99ca54a5e815c2ae6dce17ab165c6352aa64624a29cad9311dd03925795c298828f35179448153d334f8a0c87b50469
|
@@ -0,0 +1,21 @@
|
|
1
|
+
## Steps to reproduce
|
2
|
+
|
3
|
+
## Expected behavior
|
4
|
+
|
5
|
+
## Actual behavior
|
6
|
+
|
7
|
+
## System configuration
|
8
|
+
|
9
|
+
<!-- Please let us know as far as you can. -->
|
10
|
+
|
11
|
+
* Database: (Tell us what database and its version you use.)
|
12
|
+
|
13
|
+
* Apartment version:
|
14
|
+
|
15
|
+
* Apartment config (in `config/initializers/apartment.rb` or so):
|
16
|
+
|
17
|
+
* `use_schemas`: (`true` or `false`)
|
18
|
+
|
19
|
+
* Rails (or ActiveRecord) version:
|
20
|
+
|
21
|
+
* Ruby version:
|
data/.travis.yml
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.0.0
|
4
3
|
- 2.1.9
|
5
4
|
- 2.2.4
|
6
5
|
- 2.3.1
|
@@ -26,12 +25,6 @@ matrix:
|
|
26
25
|
- rvm: ruby-head
|
27
26
|
- gemfile: gemfiles/rails_master.gemfile
|
28
27
|
exclude:
|
29
|
-
- rvm: 2.0.0
|
30
|
-
gemfile: gemfiles/rails_5_0.gemfile
|
31
|
-
- rvm: 2.0.0
|
32
|
-
gemfile: gemfiles/rails_5_1.gemfile
|
33
|
-
- rvm: 2.0.0
|
34
|
-
gemfile: gemfiles/rails_master.gemfile
|
35
28
|
- rvm: 2.1.9
|
36
29
|
gemfile: gemfiles/rails_5_0.gemfile
|
37
30
|
- rvm: 2.1.9
|
@@ -46,6 +39,8 @@ matrix:
|
|
46
39
|
gemfile: gemfiles/rails_4_0.gemfile
|
47
40
|
- rvm: ruby-head
|
48
41
|
gemfile: gemfiles/rails_4_1.gemfile
|
42
|
+
- rvm: jruby-9.0.5.0
|
43
|
+
gemfile: gemfiles/rails_4_0.gemfile
|
49
44
|
- rvm: jruby-9.0.5.0
|
50
45
|
gemfile: gemfiles/rails_4_2.gemfile
|
51
46
|
- rvm: jruby-9.0.5.0
|
@@ -54,6 +49,8 @@ matrix:
|
|
54
49
|
gemfile: gemfiles/rails_5_1.gemfile
|
55
50
|
- rvm: jruby-9.0.5.0
|
56
51
|
gemfile: gemfiles/rails_master.gemfile
|
52
|
+
- rvm: jruby-9.1.9.0
|
53
|
+
gemfile: gemfiles/rails_4_0.gemfile
|
57
54
|
- rvm: jruby-9.1.9.0
|
58
55
|
gemfile: gemfiles/rails_5_0.gemfile
|
59
56
|
- rvm: jruby-9.1.9.0
|
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -94,12 +94,17 @@ One can optionally use the full database creation instead if they want, though t
|
|
94
94
|
To switch tenants using Apartment, use the following command:
|
95
95
|
|
96
96
|
```ruby
|
97
|
-
Apartment::Tenant.switch
|
97
|
+
Apartment::Tenant.switch('tenant_name') do
|
98
|
+
# ...
|
99
|
+
end
|
98
100
|
```
|
99
101
|
|
100
102
|
When switch is called, all requests coming to ActiveRecord will be routed to the tenant
|
101
|
-
you specify (with the exception of excluded models, see below).
|
102
|
-
|
103
|
+
you specify (with the exception of excluded models, see below). The tenant is automatically
|
104
|
+
switched back at the end of the block to what it was before.
|
105
|
+
|
106
|
+
There is also `switch!` which doesn't take a block, but it's recommended to use `switch`.
|
107
|
+
To return to the default tenant, you can call `switch` with no arguments.
|
103
108
|
|
104
109
|
### Switching Tenants per request
|
105
110
|
|
@@ -117,7 +122,7 @@ manually in your `application.rb` like so
|
|
117
122
|
|
118
123
|
```ruby
|
119
124
|
# config/application.rb
|
120
|
-
require 'apartment/elevators/subdomain' # or 'domain'
|
125
|
+
require 'apartment/elevators/subdomain' # or 'domain', 'first_subdomain', 'host'
|
121
126
|
```
|
122
127
|
|
123
128
|
#### Switch on subdomain
|
@@ -155,7 +160,7 @@ module MyApplication
|
|
155
160
|
end
|
156
161
|
```
|
157
162
|
|
158
|
-
If you want to exclude a domain, for example if you don't want your application to
|
163
|
+
If you want to exclude a domain, for example if you don't want your application to treat www like a subdomain, in an initializer in your application, you can set the following:
|
159
164
|
|
160
165
|
```ruby
|
161
166
|
# config/initializers/apartment/subdomain_exclusions.rb
|
@@ -166,7 +171,7 @@ This functions much in the same way as the Subdomain elevator. **NOTE:** in fact
|
|
166
171
|
|
167
172
|
#### Switch on domain
|
168
173
|
|
169
|
-
To switch based on full domain (excluding
|
174
|
+
To switch based on full domain (excluding the 'www' subdomains and top level domains *ie '.com'* ) use the following:
|
170
175
|
|
171
176
|
```ruby
|
172
177
|
# application.rb
|
@@ -177,6 +182,11 @@ module MyApplication
|
|
177
182
|
end
|
178
183
|
```
|
179
184
|
|
185
|
+
Note that if you have several subdomains, then it will match on the first *non-www* subdomain:
|
186
|
+
- example.com => example
|
187
|
+
- www.example.com => example
|
188
|
+
- a.example.com => a
|
189
|
+
|
180
190
|
#### Switch on full host using a hash
|
181
191
|
|
182
192
|
To switch based on full host with a hash to find corresponding tenant name use the following:
|
@@ -190,6 +200,31 @@ module MyApplication
|
|
190
200
|
end
|
191
201
|
```
|
192
202
|
|
203
|
+
#### Switch on full host, ignoring given first subdomains
|
204
|
+
|
205
|
+
To switch based on full host to find corresponding tenant name use the following:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
# application.rb
|
209
|
+
module MyApplication
|
210
|
+
class Application < Rails::Application
|
211
|
+
config.middleware.use Apartment::Elevators::Host
|
212
|
+
end
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
If you want to exclude a first-subdomain, for example if you don't want your application to include www in the matching, in an initializer in your application, you can set the following:
|
217
|
+
|
218
|
+
```ruby
|
219
|
+
Apartment::Elevators::Host.ignored_first_subdomains = ['www']
|
220
|
+
```
|
221
|
+
|
222
|
+
With the above set, these would be the results:
|
223
|
+
- example.com => example.com
|
224
|
+
- www.example.com => example.com
|
225
|
+
- a.example.com => a.example.com
|
226
|
+
- www.a.example.com => a.example.com
|
227
|
+
|
193
228
|
#### Custom Elevator
|
194
229
|
|
195
230
|
A Generic Elevator exists that allows you to pass a `Proc` (or anything that responds to `call`) to the middleware. This Object will be passed in an `ActionDispatch::Request` object when called for you to do your magic. Apartment will use the return value of this proc to switch to the appropriate tenant. Use like so:
|
@@ -244,7 +279,7 @@ This works okay for simple applications, but it's important to consider that you
|
|
244
279
|
To resolve this issue, consider adding the Apartment middleware at a location in the Rack stack that makes sense for your needs, e.g.:
|
245
280
|
|
246
281
|
```ruby
|
247
|
-
Rails.application.config.middleware.insert_before
|
282
|
+
Rails.application.config.middleware.insert_before Warden::Manager, Apartment::Elevators::Subdomain
|
248
283
|
```
|
249
284
|
|
250
285
|
Now work done in the Warden middleware is wrapped in the `Apartment::Tenant.switch` context started in the Generic elevator.
|
@@ -342,6 +377,8 @@ namespace :db do
|
|
342
377
|
ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE SCHEMA shared_extensions;'
|
343
378
|
# Enable UUID-OSSP
|
344
379
|
ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA shared_extensions;'
|
380
|
+
# Grant usage to public
|
381
|
+
ActiveRecord::Base.connection.execute 'GRANT usage ON SCHEMA shared_extensions to public;'
|
345
382
|
end
|
346
383
|
end
|
347
384
|
|
@@ -443,6 +480,17 @@ Note that you can disable the default migrating of all tenants with `db:migrate`
|
|
443
480
|
`Apartment.db_migrate_tenants = false` in your `Rakefile`. Note this must be done
|
444
481
|
*before* the rake tasks are loaded. ie. before `YourApp::Application.load_tasks` is called
|
445
482
|
|
483
|
+
#### Parallel Migrations
|
484
|
+
|
485
|
+
Apartment supports parallelizing migrations into multiple threads when
|
486
|
+
you have a large number of tenants. By default, parallel migrations is
|
487
|
+
turned off. You can enable this by setting `parallel_migration_threads` to
|
488
|
+
the number of threads you want to use in your initializer.
|
489
|
+
|
490
|
+
Keep in mind that because migrations are going to access the database,
|
491
|
+
the number of threads indicated here should be less than the pool size
|
492
|
+
that Rails will use to connect to your database.
|
493
|
+
|
446
494
|
### Handling Environments
|
447
495
|
|
448
496
|
By default, when not using postgresql schemas, Apartment will prepend the environment to the tenant name
|
@@ -481,9 +529,9 @@ config.tenant_names = lambda do
|
|
481
529
|
end
|
482
530
|
```
|
483
531
|
|
484
|
-
##
|
532
|
+
## Background workers
|
485
533
|
|
486
|
-
|
534
|
+
See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) or [apartment-activejob](https://github.com/influitive/apartment-activejob).
|
487
535
|
|
488
536
|
## Contributing
|
489
537
|
|
data/apartment.gemspec
CHANGED
@@ -21,7 +21,8 @@ Gem::Specification.new do |s|
|
|
21
21
|
# must be >= 3.1.2 due to bug in prepared_statements
|
22
22
|
s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0'
|
23
23
|
s.add_dependency 'rack', '>= 1.3.6'
|
24
|
-
s.add_dependency 'public_suffix', '
|
24
|
+
s.add_dependency 'public_suffix', '>= 2'
|
25
|
+
s.add_dependency 'parallel', '>= 0.7.1'
|
25
26
|
|
26
27
|
s.add_development_dependency 'appraisal'
|
27
28
|
s.add_development_dependency 'rake', '~> 0.9'
|
@@ -41,9 +42,4 @@ Gem::Specification.new do |s|
|
|
41
42
|
s.add_development_dependency 'pg', '>= 0.11.0'
|
42
43
|
s.add_development_dependency 'sqlite3'
|
43
44
|
end
|
44
|
-
|
45
|
-
if RUBY_VERSION < '2.1.0'
|
46
|
-
# capybara depends on xpath depends on nokogiri
|
47
|
-
s.add_development_dependency 'nokogiri', '< 1.7.0'
|
48
|
-
end
|
49
45
|
end
|
data/lib/apartment.rb
CHANGED
@@ -11,7 +11,7 @@ module Apartment
|
|
11
11
|
extend Forwardable
|
12
12
|
|
13
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]
|
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]
|
15
15
|
|
16
16
|
attr_accessor(*ACCESSOR_METHODS)
|
17
17
|
attr_writer(*WRITER_METHODS)
|
@@ -51,6 +51,10 @@ module Apartment
|
|
51
51
|
def default_schema
|
52
52
|
@default_schema || "public" # TODO 'public' is postgres specific
|
53
53
|
end
|
54
|
+
|
55
|
+
def parallel_migration_threads
|
56
|
+
@parallel_migration_threads || 0
|
57
|
+
end
|
54
58
|
alias :default_tenant :default_schema
|
55
59
|
alias :default_tenant= :default_schema=
|
56
60
|
|
@@ -106,8 +106,9 @@ module Apartment
|
|
106
106
|
class PostgresqlSchemaFromSqlAdapter < PostgresqlSchemaAdapter
|
107
107
|
|
108
108
|
PSQL_DUMP_BLACKLISTED_STATEMENTS= [
|
109
|
-
/SET search_path/i,
|
110
|
-
/SET lock_timeout/i,
|
109
|
+
/SET search_path/i, # overridden later
|
110
|
+
/SET lock_timeout/i, # new in postgresql 9.3
|
111
|
+
/SET row_security/i, # new in postgresql 9.5
|
111
112
|
/SET idle_in_transaction_session_timeout/i, # new in postgresql 9.6
|
112
113
|
]
|
113
114
|
|
@@ -4,9 +4,11 @@ module Apartment
|
|
4
4
|
module Elevators
|
5
5
|
# Provides a rack based tenant switching solution based on domain
|
6
6
|
# Assumes that tenant name should match domain
|
7
|
-
# Parses request host for second level domain
|
7
|
+
# Parses request host for second level domain, ignoring www
|
8
8
|
# eg. example.com => example
|
9
9
|
# www.example.bc.ca => example
|
10
|
+
# a.example.bc.ca => a
|
11
|
+
#
|
10
12
|
#
|
11
13
|
class Domain < Generic
|
12
14
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'apartment/elevators/generic'
|
2
|
+
|
3
|
+
module Apartment
|
4
|
+
module Elevators
|
5
|
+
# Provides a rack based tenant switching solution based on the host
|
6
|
+
# Assumes that tenant name should match host
|
7
|
+
# Strips/ignores first subdomains in ignored_first_subdomains
|
8
|
+
# eg. example.com => example.com
|
9
|
+
# www.example.bc.ca => www.example.bc.ca
|
10
|
+
# if ignored_first_subdomains = ['www']
|
11
|
+
# www.example.bc.ca => example.bc.ca
|
12
|
+
# www.a.b.c.d.com => a.b.c.d.com
|
13
|
+
#
|
14
|
+
class Host < Generic
|
15
|
+
def self.ignored_first_subdomains
|
16
|
+
@ignored_first_subdomains ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.ignored_first_subdomains=(arg)
|
20
|
+
@ignored_first_subdomains = arg
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_tenant_name(request)
|
24
|
+
return nil if request.host.blank?
|
25
|
+
parts = request.host.split('.')
|
26
|
+
self.class.ignored_first_subdomains.include?(parts[0]) ? parts.drop(1).join('.') : request.host
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,33 +3,54 @@
|
|
3
3
|
|
4
4
|
module Apartment
|
5
5
|
class RakeTaskEnhancer
|
6
|
-
|
7
|
-
TASKS
|
8
|
-
|
6
|
+
|
7
|
+
module TASKS
|
8
|
+
ENHANCE_BEFORE = %w(db:drop)
|
9
|
+
ENHANCE_AFTER = %w(db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed)
|
10
|
+
freeze
|
11
|
+
end
|
12
|
+
|
9
13
|
# This is a bit convoluted, but helps solve problems when using Apartment within an engine
|
10
14
|
# See spec/integration/use_within_an_engine.rb
|
11
|
-
|
15
|
+
|
12
16
|
class << self
|
13
17
|
def enhance!
|
14
|
-
|
18
|
+
return unless should_enhance?
|
19
|
+
|
20
|
+
# insert task before
|
21
|
+
TASKS::ENHANCE_BEFORE.each do |name|
|
15
22
|
task = Rake::Task[name]
|
16
|
-
task
|
17
|
-
if should_enhance?
|
18
|
-
enhance_task(task)
|
19
|
-
end
|
20
|
-
end
|
23
|
+
enhance_before_task(task)
|
21
24
|
end
|
25
|
+
|
26
|
+
# insert task after
|
27
|
+
TASKS::ENHANCE_AFTER.each do |name|
|
28
|
+
task = Rake::Task[name]
|
29
|
+
enhance_after_task(task)
|
30
|
+
end
|
31
|
+
|
22
32
|
end
|
23
|
-
|
33
|
+
|
24
34
|
def should_enhance?
|
25
35
|
Apartment.db_migrate_tenants
|
26
36
|
end
|
27
|
-
|
28
|
-
def
|
29
|
-
|
37
|
+
|
38
|
+
def enhance_before_task(task)
|
39
|
+
task.enhance([inserted_task_name(task)])
|
40
|
+
end
|
41
|
+
|
42
|
+
def enhance_after_task(task)
|
43
|
+
task.enhance do
|
44
|
+
Rake::Task[inserted_task_name(task)].invoke
|
45
|
+
end
|
30
46
|
end
|
47
|
+
|
48
|
+
def inserted_task_name(task)
|
49
|
+
task.name.sub(/db:/, 'apartment:')
|
50
|
+
end
|
51
|
+
|
31
52
|
end
|
32
|
-
|
53
|
+
|
33
54
|
end
|
34
55
|
end
|
35
56
|
|
data/lib/apartment/version.rb
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
# require 'apartment/elevators/domain'
|
7
7
|
require 'apartment/elevators/subdomain'
|
8
8
|
# require 'apartment/elevators/first_subdomain'
|
9
|
+
# require 'apartment/elevators/host'
|
9
10
|
|
10
11
|
#
|
11
12
|
# Apartment Configuration
|
@@ -95,3 +96,4 @@ end
|
|
95
96
|
# Rails.application.config.middleware.use Apartment::Elevators::Domain
|
96
97
|
Rails.application.config.middleware.use Apartment::Elevators::Subdomain
|
97
98
|
# Rails.application.config.middleware.use Apartment::Elevators::FirstSubdomain
|
99
|
+
# Rails.application.config.middleware.use Apartment::Elevators::Host
|
data/lib/tasks/apartment.rake
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'apartment/migrator'
|
2
|
+
require 'parallel'
|
2
3
|
|
3
4
|
apartment_namespace = namespace :apartment do
|
4
5
|
|
@@ -14,10 +15,22 @@ apartment_namespace = namespace :apartment do
|
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
18
|
+
desc "Drop all tenants"
|
19
|
+
task :drop do
|
20
|
+
tenants.each do |tenant|
|
21
|
+
begin
|
22
|
+
puts("Dropping #{tenant} tenant")
|
23
|
+
Apartment::Tenant.drop(tenant)
|
24
|
+
rescue Apartment::TenantNotFound => e
|
25
|
+
puts e.message
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
17
30
|
desc "Migrate all tenants"
|
18
31
|
task :migrate do
|
19
32
|
warn_if_tenants_empty
|
20
|
-
|
33
|
+
each_tenant do |tenant|
|
21
34
|
begin
|
22
35
|
puts("Migrating #{tenant} tenant")
|
23
36
|
Apartment::Migrator.migrate tenant
|
@@ -31,7 +44,7 @@ apartment_namespace = namespace :apartment do
|
|
31
44
|
task :seed do
|
32
45
|
warn_if_tenants_empty
|
33
46
|
|
34
|
-
|
47
|
+
each_tenant do |tenant|
|
35
48
|
begin
|
36
49
|
puts("Seeding #{tenant} tenant")
|
37
50
|
Apartment::Tenant.switch(tenant) do
|
@@ -49,7 +62,7 @@ apartment_namespace = namespace :apartment do
|
|
49
62
|
|
50
63
|
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
|
51
64
|
|
52
|
-
|
65
|
+
each_tenant do |tenant|
|
53
66
|
begin
|
54
67
|
puts("Rolling back #{tenant} tenant")
|
55
68
|
Apartment::Migrator.rollback tenant, step
|
@@ -67,7 +80,7 @@ apartment_namespace = namespace :apartment do
|
|
67
80
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
68
81
|
raise 'VERSION is required' unless version
|
69
82
|
|
70
|
-
|
83
|
+
each_tenant do |tenant|
|
71
84
|
begin
|
72
85
|
puts("Migrating #{tenant} tenant up")
|
73
86
|
Apartment::Migrator.run :up, tenant, version
|
@@ -84,7 +97,7 @@ apartment_namespace = namespace :apartment do
|
|
84
97
|
version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
|
85
98
|
raise 'VERSION is required' unless version
|
86
99
|
|
87
|
-
|
100
|
+
each_tenant do |tenant|
|
88
101
|
begin
|
89
102
|
puts("Migrating #{tenant} tenant down")
|
90
103
|
Apartment::Migrator.run :down, tenant, version
|
@@ -106,6 +119,12 @@ apartment_namespace = namespace :apartment do
|
|
106
119
|
end
|
107
120
|
end
|
108
121
|
|
122
|
+
def each_tenant(&block)
|
123
|
+
Parallel.each(tenants, in_threads: Apartment.parallel_migration_threads) do |tenant|
|
124
|
+
block.call(tenant)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
109
128
|
def tenants
|
110
129
|
ENV['DB'] ? ENV['DB'].split(',').map { |s| s.strip } : Apartment.tenant_names || []
|
111
130
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'rake'
|
3
3
|
require 'apartment/migrator'
|
4
|
+
require 'apartment/tenant'
|
4
5
|
|
5
6
|
describe "apartment rake tasks" do
|
6
7
|
|
@@ -116,5 +117,13 @@ describe "apartment rake tasks" do
|
|
116
117
|
@rake['apartment:rollback'].invoke
|
117
118
|
end
|
118
119
|
end
|
120
|
+
|
121
|
+
describe "apartment:drop" do
|
122
|
+
it "should migrate public and all multi-tenant dbs" do
|
123
|
+
expect(Apartment::Tenant).to receive(:drop).exactly(tenant_count).times
|
124
|
+
@rake['apartment:drop'].invoke
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
119
128
|
end
|
120
129
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'apartment/elevators/host'
|
3
|
+
|
4
|
+
describe Apartment::Elevators::Host do
|
5
|
+
|
6
|
+
subject(:elevator){ described_class.new(Proc.new{}) }
|
7
|
+
|
8
|
+
describe "#parse_tenant_name" do
|
9
|
+
|
10
|
+
it "should return nil when no host" do
|
11
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => '')
|
12
|
+
expect(elevator.parse_tenant_name(request)).to be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
context "assuming no ignored_first_subdomains" do
|
16
|
+
before { allow(described_class).to receive(:ignored_first_subdomains).and_return([]) }
|
17
|
+
|
18
|
+
context "with 3 parts" do
|
19
|
+
it "should return the whole host" do
|
20
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com')
|
21
|
+
expect(elevator.parse_tenant_name(request)).to eq('foo.bar.com')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with 6 parts" do
|
26
|
+
it "should return the whole host" do
|
27
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'one.two.three.foo.bar.com')
|
28
|
+
expect(elevator.parse_tenant_name(request)).to eq('one.two.three.foo.bar.com')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "assuming ignored_first_subdomains is set" do
|
34
|
+
before { allow(described_class).to receive(:ignored_first_subdomains).and_return(%w{www foo}) }
|
35
|
+
|
36
|
+
context "with 3 parts" do
|
37
|
+
it "should return host without www" do
|
38
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'www.bar.com')
|
39
|
+
expect(elevator.parse_tenant_name(request)).to eq('bar.com')
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return host without foo" do
|
43
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com')
|
44
|
+
expect(elevator.parse_tenant_name(request)).to eq('bar.com')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with 6 parts" do
|
49
|
+
it "should return host without www" do
|
50
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'www.one.two.three.foo.bar.com')
|
51
|
+
expect(elevator.parse_tenant_name(request)).to eq('one.two.three.foo.bar.com')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return host without www" do
|
55
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.one.two.three.bar.com')
|
56
|
+
expect(elevator.parse_tenant_name(request)).to eq('one.two.three.bar.com')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "assuming localhost" do
|
62
|
+
it "should return localhost" do
|
63
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => 'localhost')
|
64
|
+
expect(elevator.parse_tenant_name(request)).to eq('localhost')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "assuming ip address" do
|
69
|
+
it "should return the ip address" do
|
70
|
+
request = ActionDispatch::Request.new('HTTP_HOST' => '127.0.0.1')
|
71
|
+
expect(elevator.parse_tenant_name(request)).to eq('127.0.0.1')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#call" do
|
77
|
+
it "switches to the proper tenant" do
|
78
|
+
allow(described_class).to receive(:ignored_first_subdomains).and_return([])
|
79
|
+
expect(Apartment::Tenant).to receive(:switch).with('foo.bar.com')
|
80
|
+
elevator.call('HTTP_HOST' => 'foo.bar.com')
|
81
|
+
end
|
82
|
+
|
83
|
+
it "ignores ignored_first_subdomains" do
|
84
|
+
allow(described_class).to receive(:ignored_first_subdomains).and_return(%w{foo})
|
85
|
+
expect(Apartment::Tenant).to receive(:switch).with('bar.com')
|
86
|
+
elevator.call('HTTP_HOST' => 'foo.bar.com')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apartment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Brunner
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-12-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -49,16 +49,30 @@ dependencies:
|
|
49
49
|
name: public_suffix
|
50
50
|
requirement: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 2
|
54
|
+
version: '2'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
57
|
version_requirements: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: parallel
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.7.1
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 0.7.1
|
62
76
|
- !ruby/object:Gem::Dependency
|
63
77
|
name: appraisal
|
64
78
|
requirement: !ruby/object:Gem::Requirement
|
@@ -180,11 +194,10 @@ executables: []
|
|
180
194
|
extensions: []
|
181
195
|
extra_rdoc_files: []
|
182
196
|
files:
|
197
|
+
- ".github/ISSUE_TEMPLATE.md"
|
183
198
|
- ".gitignore"
|
184
199
|
- ".pryrc"
|
185
200
|
- ".rspec"
|
186
|
-
- ".ruby-gemset"
|
187
|
-
- ".ruby-version"
|
188
201
|
- ".travis.yml"
|
189
202
|
- Appraisals
|
190
203
|
- Gemfile
|
@@ -215,6 +228,7 @@ files:
|
|
215
228
|
- lib/apartment/elevators/domain.rb
|
216
229
|
- lib/apartment/elevators/first_subdomain.rb
|
217
230
|
- lib/apartment/elevators/generic.rb
|
231
|
+
- lib/apartment/elevators/host.rb
|
218
232
|
- lib/apartment/elevators/host_hash.rb
|
219
233
|
- lib/apartment/elevators/subdomain.rb
|
220
234
|
- lib/apartment/migrator.rb
|
@@ -322,6 +336,7 @@ files:
|
|
322
336
|
- spec/unit/elevators/first_subdomain_spec.rb
|
323
337
|
- spec/unit/elevators/generic_spec.rb
|
324
338
|
- spec/unit/elevators/host_hash_spec.rb
|
339
|
+
- spec/unit/elevators/host_spec.rb
|
325
340
|
- spec/unit/elevators/subdomain_spec.rb
|
326
341
|
- spec/unit/migrator_spec.rb
|
327
342
|
- spec/unit/reloader_spec.rb
|
@@ -445,6 +460,7 @@ test_files:
|
|
445
460
|
- spec/unit/elevators/first_subdomain_spec.rb
|
446
461
|
- spec/unit/elevators/generic_spec.rb
|
447
462
|
- spec/unit/elevators/host_hash_spec.rb
|
463
|
+
- spec/unit/elevators/host_spec.rb
|
448
464
|
- spec/unit/elevators/subdomain_spec.rb
|
449
465
|
- spec/unit/migrator_spec.rb
|
450
466
|
- spec/unit/reloader_spec.rb
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
apartment
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.3
|