ros-apartment 2.3.0.alpha1 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.pryrc +5 -3
- data/.rubocop.yml +36 -0
- data/.rubocop_todo.yml +439 -0
- data/.ruby-version +1 -0
- data/Appraisals +31 -52
- data/CHANGELOG.md +965 -0
- data/Gemfile +2 -7
- data/Guardfile +3 -16
- data/README.md +121 -44
- data/Rakefile +42 -31
- data/lib/apartment/active_record/connection_handling.rb +31 -0
- data/lib/apartment/active_record/internal_metadata.rb +9 -0
- data/lib/apartment/active_record/postgresql_adapter.rb +43 -0
- data/lib/apartment/active_record/schema_migration.rb +11 -0
- data/lib/apartment/adapters/abstract_adapter.rb +52 -46
- data/lib/apartment/adapters/abstract_jdbc_adapter.rb +5 -3
- data/lib/apartment/adapters/jdbc_mysql_adapter.rb +3 -3
- data/lib/apartment/adapters/jdbc_postgresql_adapter.rb +19 -13
- data/lib/apartment/adapters/mysql2_adapter.rb +15 -9
- data/lib/apartment/adapters/postgis_adapter.rb +3 -2
- data/lib/apartment/adapters/postgresql_adapter.rb +79 -31
- data/lib/apartment/adapters/sqlite3_adapter.rb +18 -8
- data/lib/apartment/adapters/trilogy_adapter.rb +29 -0
- data/lib/apartment/console.rb +23 -11
- data/lib/apartment/custom_console.rb +42 -0
- data/lib/apartment/deprecation.rb +2 -1
- data/lib/apartment/elevators/domain.rb +4 -3
- data/lib/apartment/elevators/first_subdomain.rb +3 -2
- data/lib/apartment/elevators/generic.rb +4 -3
- data/lib/apartment/elevators/host.rb +6 -1
- data/lib/apartment/elevators/host_hash.rb +6 -2
- data/lib/apartment/elevators/subdomain.rb +9 -5
- data/lib/apartment/log_subscriber.rb +45 -0
- data/lib/apartment/migrator.rb +7 -24
- data/lib/apartment/model.rb +29 -0
- data/lib/apartment/railtie.rb +21 -20
- data/lib/apartment/tasks/enhancements.rb +4 -6
- data/lib/apartment/tasks/task_helper.rb +52 -0
- data/lib/apartment/tenant.rb +7 -10
- data/lib/apartment/version.rb +3 -1
- data/lib/apartment.rb +46 -15
- data/lib/generators/apartment/install/install_generator.rb +4 -3
- data/lib/generators/apartment/install/templates/apartment.rb +10 -3
- data/lib/tasks/apartment.rake +48 -87
- data/ros-apartment.gemspec +65 -0
- metadata +181 -264
- data/.github/ISSUE_TEMPLATE.md +0 -21
- data/.travis.yml +0 -65
- data/HISTORY.md +0 -398
- data/TODO.md +0 -51
- data/apartment.gemspec +0 -46
- data/docker-compose.yml +0 -33
- data/gemfiles/rails_4_2.gemfile +0 -23
- data/gemfiles/rails_5_0.gemfile +0 -22
- data/gemfiles/rails_5_1.gemfile +0 -22
- data/gemfiles/rails_5_2.gemfile +0 -18
- data/gemfiles/rails_6_0.gemfile +0 -22
- data/gemfiles/rails_master.gemfile +0 -22
- data/lib/apartment/reloader.rb +0 -21
- data/spec/adapters/jdbc_mysql_adapter_spec.rb +0 -19
- data/spec/adapters/jdbc_postgresql_adapter_spec.rb +0 -41
- data/spec/adapters/mysql2_adapter_spec.rb +0 -59
- data/spec/adapters/postgresql_adapter_spec.rb +0 -61
- data/spec/adapters/sqlite3_adapter_spec.rb +0 -83
- data/spec/apartment_spec.rb +0 -11
- data/spec/config/database.yml.sample +0 -49
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/controllers/application_controller.rb +0 -6
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/models/company.rb +0 -3
- data/spec/dummy/app/models/user.rb +0 -3
- data/spec/dummy/app/views/application/index.html.erb +0 -1
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/config/application.rb +0 -49
- data/spec/dummy/config/boot.rb +0 -11
- data/spec/dummy/config/database.yml.sample +0 -44
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -28
- data/spec/dummy/config/environments/production.rb +0 -51
- data/spec/dummy/config/environments/test.rb +0 -34
- data/spec/dummy/config/initializers/apartment.rb +0 -4
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -10
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/routes.rb +0 -3
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb +0 -39
- data/spec/dummy/db/migrate/20111202022214_create_table_books.rb +0 -14
- data/spec/dummy/db/migrate/20180415260934_create_public_tokens.rb +0 -13
- data/spec/dummy/db/schema.rb +0 -55
- data/spec/dummy/db/seeds/import.rb +0 -5
- data/spec/dummy/db/seeds.rb +0 -5
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -26
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +0 -6
- data/spec/dummy_engine/.gitignore +0 -8
- data/spec/dummy_engine/Gemfile +0 -15
- data/spec/dummy_engine/Rakefile +0 -34
- data/spec/dummy_engine/bin/rails +0 -12
- data/spec/dummy_engine/config/initializers/apartment.rb +0 -51
- data/spec/dummy_engine/dummy_engine.gemspec +0 -24
- data/spec/dummy_engine/lib/dummy_engine/engine.rb +0 -4
- data/spec/dummy_engine/lib/dummy_engine/version.rb +0 -3
- data/spec/dummy_engine/lib/dummy_engine.rb +0 -4
- data/spec/dummy_engine/test/dummy/Rakefile +0 -6
- data/spec/dummy_engine/test/dummy/config/application.rb +0 -22
- data/spec/dummy_engine/test/dummy/config/boot.rb +0 -5
- data/spec/dummy_engine/test/dummy/config/database.yml +0 -25
- data/spec/dummy_engine/test/dummy/config/environment.rb +0 -5
- data/spec/dummy_engine/test/dummy/config/environments/development.rb +0 -37
- data/spec/dummy_engine/test/dummy/config/environments/production.rb +0 -78
- data/spec/dummy_engine/test/dummy/config/environments/test.rb +0 -39
- data/spec/dummy_engine/test/dummy/config/initializers/assets.rb +0 -8
- data/spec/dummy_engine/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy_engine/test/dummy/config/initializers/cookies_serializer.rb +0 -3
- data/spec/dummy_engine/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
- data/spec/dummy_engine/test/dummy/config/initializers/inflections.rb +0 -16
- data/spec/dummy_engine/test/dummy/config/initializers/mime_types.rb +0 -4
- data/spec/dummy_engine/test/dummy/config/initializers/session_store.rb +0 -3
- data/spec/dummy_engine/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy_engine/test/dummy/config/locales/en.yml +0 -23
- data/spec/dummy_engine/test/dummy/config/routes.rb +0 -56
- data/spec/dummy_engine/test/dummy/config/secrets.yml +0 -22
- data/spec/dummy_engine/test/dummy/config.ru +0 -4
- data/spec/examples/connection_adapter_examples.rb +0 -42
- data/spec/examples/generic_adapter_custom_configuration_example.rb +0 -95
- data/spec/examples/generic_adapter_examples.rb +0 -163
- data/spec/examples/schema_adapter_examples.rb +0 -234
- data/spec/integration/apartment_rake_integration_spec.rb +0 -107
- data/spec/integration/query_caching_spec.rb +0 -81
- data/spec/integration/use_within_an_engine_spec.rb +0 -28
- data/spec/schemas/v1.rb +0 -16
- data/spec/schemas/v2.rb +0 -43
- data/spec/schemas/v3.rb +0 -49
- data/spec/spec_helper.rb +0 -61
- data/spec/support/apartment_helpers.rb +0 -43
- data/spec/support/capybara_sessions.rb +0 -15
- data/spec/support/config.rb +0 -10
- data/spec/support/contexts.rb +0 -52
- data/spec/support/requirements.rb +0 -35
- data/spec/support/setup.rb +0 -46
- data/spec/tasks/apartment_rake_spec.rb +0 -129
- data/spec/tenant_spec.rb +0 -190
- data/spec/unit/config_spec.rb +0 -112
- data/spec/unit/elevators/domain_spec.rb +0 -32
- data/spec/unit/elevators/first_subdomain_spec.rb +0 -24
- data/spec/unit/elevators/generic_spec.rb +0 -54
- data/spec/unit/elevators/host_hash_spec.rb +0 -32
- data/spec/unit/elevators/host_spec.rb +0 -89
- data/spec/unit/elevators/subdomain_spec.rb +0 -76
- data/spec/unit/migrator_spec.rb +0 -77
- data/spec/unit/reloader_spec.rb +0 -24
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# A sample Guardfile
|
2
4
|
# More info at https://github.com/guard/guard#readme
|
3
5
|
|
@@ -5,20 +7,5 @@ guard :rspec do
|
|
5
7
|
watch(%r{^spec/.+_spec\.rb$})
|
6
8
|
watch(%r{^lib/apartment/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
|
7
9
|
watch(%r{^lib/apartment/(.+)\.rb$}) { |m| "spec/integration/#{m[1]}_spec.rb" }
|
8
|
-
watch('spec/spec_helper.rb')
|
9
|
-
|
10
|
-
# # Rails example
|
11
|
-
# watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
12
|
-
# watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
13
|
-
# watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
14
|
-
# watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
15
|
-
# watch('config/routes.rb') { "spec/routing" }
|
16
|
-
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
17
|
-
|
18
|
-
# # Capybara features specs
|
19
|
-
# watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
20
|
-
|
21
|
-
# # Turnip features and steps
|
22
|
-
# watch(%r{^spec/acceptance/(.+)\.feature$})
|
23
|
-
# watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
10
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
24
11
|
end
|
data/README.md
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
# Apartment
|
2
2
|
|
3
|
-
[![Gem Version](https://badge.fury.io/rb/apartment.svg)](https://badge.fury.io/rb/apartment)
|
4
|
-
[![Code Climate](https://codeclimate.com/
|
5
|
-
[![Build Status](https://travis-ci.org/influitive/apartment.svg?branch=development)](https://travis-ci.org/influitive/apartment)
|
6
|
-
|
7
|
-
# NOT ACTIVELY MAINTAINED YET. SOLELY FOR TESTING PURPOSES ATM
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/ros-apartment.svg)](https://badge.fury.io/rb/ros-apartment)
|
4
|
+
[![Code Climate](https://api.codeclimate.com/v1/badges/b0dc327380bb8438f991/maintainability)](https://codeclimate.com/github/rails-on-services/apartment/maintainability)
|
8
5
|
|
9
6
|
*Multitenancy for Rails and ActiveRecord*
|
10
7
|
|
@@ -12,26 +9,21 @@ Apartment provides tools to help you deal with multiple tenants in your Rails
|
|
12
9
|
application. If you need to have certain data sequestered based on account or company,
|
13
10
|
but still allow some data to exist in a common tenant, Apartment can help.
|
14
11
|
|
15
|
-
##
|
16
|
-
|
17
|
-
In order to help drive the direction of development and clean up the codebase, we'd like to take a poll
|
18
|
-
on how people are currently using Apartment. If you can take 5 seconds (1 question) to answer
|
19
|
-
this poll, we'd greatly appreciated it.
|
20
|
-
|
21
|
-
[View Poll](http://www.poll-maker.com/poll391552x4Bfb41a9-15)
|
12
|
+
## Apartment drop in replacement gem
|
22
13
|
|
23
|
-
|
14
|
+
After having reached out via github issues and email directly, no replies ever
|
15
|
+
came. Since we wanted to upgrade our application to Rails 6 we decided to fork
|
16
|
+
and start some development to support Rails 6. Because we don't have access
|
17
|
+
to the apartment gem itself, the solution was to release it under a different
|
18
|
+
name but providing the exact same API as it was before.
|
24
19
|
|
25
|
-
|
26
|
-
> when using Apartment, that's because there's [an issue](https://github.com/rails/rails/issues/19578)
|
27
|
-
> with how ActiveRecord maps Postgresql data types into AR data types.
|
28
|
-
> This has been patched and will be released for AR 4.2.2. It's apparently hard
|
29
|
-
> to backport to 4.1 unfortunately.
|
30
|
-
> If you're noticing high memory usage from ActiveRecord with Apartment please upgrade.
|
20
|
+
## Help wanted
|
31
21
|
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
We were never involved with the development of Apartment gem in the first place
|
23
|
+
and this project started out of our own needs. We will be more than happy
|
24
|
+
to collaborate to maintain the gem alive and supporting the latest versions
|
25
|
+
of ruby and rails, but your help is appreciated. Either by reporting bugs you
|
26
|
+
may find or proposing improvements to the gem itself. Feel free to reach out.
|
35
27
|
|
36
28
|
## Installation
|
37
29
|
|
@@ -40,7 +32,7 @@ gem 'rails', '4.2.1', github: 'influitive/rails', tag: 'v4.2.1.memfix'
|
|
40
32
|
Add the following to your Gemfile:
|
41
33
|
|
42
34
|
```ruby
|
43
|
-
gem 'apartment'
|
35
|
+
gem 'ros-apartment', require: 'apartment'
|
44
36
|
```
|
45
37
|
|
46
38
|
Then generate your `Apartment` config file using
|
@@ -75,7 +67,7 @@ you need to create a new tenant, you can run the following command:
|
|
75
67
|
Apartment::Tenant.create('tenant_name')
|
76
68
|
```
|
77
69
|
|
78
|
-
If you're using the [prepend environment](https://github.com/
|
70
|
+
If you're using the [prepend environment](https://github.com/rails-on-services/apartment#handling-environments) config option or you AREN'T using Postgresql Schemas, this will create a tenant in the following format: "#{environment}\_tenant_name".
|
79
71
|
In the case of a sqlite database, this will be created in your 'db/' folder. With
|
80
72
|
other databases, the tenant will be created as a new DB within the system.
|
81
73
|
|
@@ -108,6 +100,16 @@ switched back at the end of the block to what it was before.
|
|
108
100
|
There is also `switch!` which doesn't take a block, but it's recommended to use `switch`.
|
109
101
|
To return to the default tenant, you can call `switch` with no arguments.
|
110
102
|
|
103
|
+
#### Multiple Tenants
|
104
|
+
|
105
|
+
When using schemas, you can also pass in a list of schemas if desired. Any tables defined in a schema earlier in the chain will be referenced first, so this is only useful if you have a schema with only some of the tables defined:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
Apartment::Tenant.switch(['tenant_1', 'tenant_2']) do
|
109
|
+
# ...
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
111
113
|
### Switching Tenants per request
|
112
114
|
|
113
115
|
You can have Apartment route to the appropriate tenant by adding some Rack middleware.
|
@@ -236,7 +238,7 @@ A Generic Elevator exists that allows you to pass a `Proc` (or anything that res
|
|
236
238
|
module MyApplication
|
237
239
|
class Application < Rails::Application
|
238
240
|
# Obviously not a contrived example
|
239
|
-
config.middleware.use Apartment::Elevators::Generic,
|
241
|
+
config.middleware.use Apartment::Elevators::Generic, proc { |request| request.host.reverse }
|
240
242
|
end
|
241
243
|
end
|
242
244
|
```
|
@@ -272,7 +274,7 @@ In the examples above, we show the Apartment middleware being appended to the Ra
|
|
272
274
|
Rails.application.config.middleware.use Apartment::Elevators::Subdomain
|
273
275
|
```
|
274
276
|
|
275
|
-
By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/
|
277
|
+
By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/rails-on-services/apartment/blob/development/lib/apartment/elevators/generic.rb#L22) elevator, so all elevators that inherit from this elevator will operate as such.
|
276
278
|
|
277
279
|
It's also good to note that Apartment switches back to the "public" tenant any time an error is raised in your application.
|
278
280
|
|
@@ -296,6 +298,27 @@ Apartment::Tenant.drop('tenant_name')
|
|
296
298
|
|
297
299
|
When method is called, the schema is dropped and all data from itself will be lost. Be careful with this method.
|
298
300
|
|
301
|
+
### Custom Prompt
|
302
|
+
|
303
|
+
#### Console methods
|
304
|
+
|
305
|
+
`ros-apartment` console configures two helper methods:
|
306
|
+
1. `tenant_list` - list available tenants while using the console
|
307
|
+
2. `st(tenant_name:String)` - Switches the context to the tenant name passed, if
|
308
|
+
it exists.
|
309
|
+
|
310
|
+
#### Custom printed prompt
|
311
|
+
|
312
|
+
`ros-apartment` also has a custom prompt that gives a bit more information about
|
313
|
+
the context in which you're running. It shows the environment as well as the tenant
|
314
|
+
that is currently switched to. In order for you to enable this, you need to require
|
315
|
+
the custom console in your application.
|
316
|
+
|
317
|
+
In `application.rb` add `require 'apartment/custom_console'`.
|
318
|
+
Please note that we rely on `pry-rails` to edit the prompt, thus your project needs
|
319
|
+
to install it as well. In order to do so, you need to add `gem 'pry-rails'` to your
|
320
|
+
project's gemfile.
|
321
|
+
|
299
322
|
## Config
|
300
323
|
|
301
324
|
The following config options should be set up in a Rails initializer such as:
|
@@ -310,6 +333,37 @@ Apartment.configure do |config|
|
|
310
333
|
end
|
311
334
|
```
|
312
335
|
|
336
|
+
### Skip tenant schema check
|
337
|
+
|
338
|
+
This is configurable by setting: `tenant_presence_check`. It defaults to true
|
339
|
+
in order to maintain the original gem behavior. This is only checked when using one of the PostgreSQL adapters.
|
340
|
+
The original gem behavior, when running `switch` would look for the existence of the schema before switching. This adds an extra query on every context switch. While in the default simple scenarios this is a valid check, in high volume platforms this adds some unnecessary overhead which can be detected in some other ways on the application level.
|
341
|
+
|
342
|
+
Setting this configuration value to `false` will disable the schema presence check before trying to switch the context.
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
Apartment.configure do |config|
|
346
|
+
config.tenant_presence_check = false
|
347
|
+
end
|
348
|
+
```
|
349
|
+
|
350
|
+
### Additional logging information
|
351
|
+
|
352
|
+
Enabling this configuration will output the database that the process is currently connected to as well as which
|
353
|
+
schemas are in the search path. This can be enabled by setting to true the `active_record_log` configuration.
|
354
|
+
|
355
|
+
Please note that our custom logger inherits from `ActiveRecord::LogSubscriber` so this will be required for the configuration to work.
|
356
|
+
|
357
|
+
**Example log output:**
|
358
|
+
|
359
|
+
<img src="documentation/images/log_example.png">
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
Apartment.configure do |config|
|
363
|
+
config.active_record_log = true
|
364
|
+
end
|
365
|
+
```
|
366
|
+
|
313
367
|
### Excluding models
|
314
368
|
|
315
369
|
If you have some models that should always access the 'public' tenant, you can specify this by configuring Apartment using `Apartment.configure`. This will yield a config object for you. You can set excluded models like so:
|
@@ -327,17 +381,30 @@ Rails will always access the 'public' tenant when accessing these models, but no
|
|
327
381
|
|
328
382
|
### Postgresql Schemas
|
329
383
|
|
330
|
-
|
384
|
+
#### Alternative: Creating new schemas by using raw SQL dumps
|
385
|
+
|
386
|
+
Apartment can be forced to use raw SQL dumps insted of `schema.rb` for creating new schemas. Use this when you are using some extra features in postgres that can't be represented in `schema.rb`, like materialized views etc.
|
387
|
+
|
388
|
+
This only applies while using postgres adapter and `config.use_schemas` is set to `true`.
|
389
|
+
(Note: this option doesn't use `db/structure.sql`, it creates SQL dump by executing `pg_dump`)
|
390
|
+
|
391
|
+
Enable this option with:
|
392
|
+
|
393
|
+
```ruby
|
394
|
+
config.use_sql = true
|
395
|
+
```
|
396
|
+
|
397
|
+
### Providing a Different default_tenant
|
331
398
|
|
332
399
|
By default, ActiveRecord will use `"$user", public` as the default `schema_search_path`. This can be modified if you wish to use a different default schema be setting:
|
333
400
|
|
334
401
|
```ruby
|
335
|
-
config.
|
402
|
+
config.default_tenant = "some_other_schema"
|
336
403
|
```
|
337
404
|
|
338
405
|
With that set, all excluded models will use this schema as the table name prefix instead of `public` and `reset` on `Apartment::Tenant` will return to this schema as well.
|
339
406
|
|
340
|
-
|
407
|
+
### Persistent Schemas
|
341
408
|
|
342
409
|
Apartment will normally just switch the `schema_search_path` whole hog to the one passed in. This can lead to problems if you want other schemas to always be searched as well. Enter `persistent_schemas`. You can configure a list of other schemas that will always remain in the search path, while the default gets swapped out:
|
343
410
|
|
@@ -405,7 +472,7 @@ schema_search_path: "public,shared_extensions"
|
|
405
472
|
...
|
406
473
|
```
|
407
474
|
|
408
|
-
This would be for a config with `
|
475
|
+
This would be for a config with `default_tenant` set to `public` and `persistent_schemas` set to `['shared_extensions']`. **Note**: This only works on Heroku with [Rails 4.1+](https://devcenter.heroku.com/changelog-items/426). For apps that use older Rails versions hosted on Heroku, the only way to properly setup is to start with a fresh PostgreSQL instance:
|
409
476
|
|
410
477
|
1. Append `?schema_search_path=public,hstore` to your `DATABASE_URL` environment variable, by this you don't have to revise the `database.yml` file (which is impossible since Heroku regenerates a completely different and immutable `database.yml` of its own on each deploy)
|
411
478
|
2. Run `heroku pg:psql` from your command line
|
@@ -443,18 +510,6 @@ schema in the `search_path` at all times. We won't be able to do this though unt
|
|
443
510
|
also contain the tenanted tables, which is an open issue with no real milestone to be completed.
|
444
511
|
Happy to accept PR's on the matter.
|
445
512
|
|
446
|
-
#### Alternative: Creating new schemas by using raw SQL dumps
|
447
|
-
|
448
|
-
Apartment can be forced to use raw SQL dumps insted of `schema.rb` for creating new schemas. Use this when you are using some extra features in postgres that can't be represented in `schema.rb`, like materialized views etc.
|
449
|
-
|
450
|
-
This only applies while using postgres adapter and `config.use_schemas` is set to `true`.
|
451
|
-
(Note: this option doesn't use `db/structure.sql`, it creates SQL dump by executing `pg_dump`)
|
452
|
-
|
453
|
-
Enable this option with:
|
454
|
-
```ruby
|
455
|
-
config.use_sql = true
|
456
|
-
```
|
457
|
-
|
458
513
|
### Managing Migrations
|
459
514
|
|
460
515
|
In order to migrate all of your tenants (or postgresql schemas) you need to provide a list
|
@@ -475,7 +530,7 @@ You can then migrate your tenants using the normal rake task:
|
|
475
530
|
rake db:migrate
|
476
531
|
```
|
477
532
|
|
478
|
-
This just invokes `Apartment::
|
533
|
+
This just invokes `Apartment::Migrator.migrate(#{tenant_name})` for each tenant name supplied
|
479
534
|
from `Apartment.tenant_names`
|
480
535
|
|
481
536
|
Note that you can disable the default migrating of all tenants with `db:migrate` by setting
|
@@ -533,7 +588,12 @@ end
|
|
533
588
|
|
534
589
|
## Background workers
|
535
590
|
|
536
|
-
|
591
|
+
Both these gems have been forked as a side consequence of having a new gem name.
|
592
|
+
You can use them exactly as you were using before. They are, just like this one
|
593
|
+
a drop-in replacement.
|
594
|
+
|
595
|
+
See [apartment-sidekiq](https://github.com/rails-on-services/apartment-sidekiq)
|
596
|
+
or [apartment-activejob](https://github.com/rails-on-services/apartment-activejob).
|
537
597
|
|
538
598
|
## Callbacks
|
539
599
|
|
@@ -560,6 +620,16 @@ module Apartment
|
|
560
620
|
end
|
561
621
|
```
|
562
622
|
|
623
|
+
## Running rails console without a connection to the database
|
624
|
+
|
625
|
+
By default, once apartment starts, it establishes a connection to the database. It is possible to
|
626
|
+
disable this initial connection, by running with `APARTMENT_DISABLE_INIT` set to something:
|
627
|
+
|
628
|
+
```shell
|
629
|
+
$ APARTMENT_DISABLE_INIT=true DATABASE_URL=postgresql://localhost:1234/buk_development bin/rails runner 'puts 1'
|
630
|
+
# 1
|
631
|
+
```
|
632
|
+
|
563
633
|
## Contributing
|
564
634
|
|
565
635
|
* In both `spec/dummy/config` and `spec/config`, you will see `database.yml.sample` files
|
@@ -571,6 +641,13 @@ end
|
|
571
641
|
|
572
642
|
* If you're looking to help, check out the TODO file for some upcoming changes I'd like to implement in Apartment.
|
573
643
|
|
644
|
+
### Running bundle install
|
645
|
+
|
646
|
+
mysql2 gem in some cases fails to install.
|
647
|
+
If you face problems running bundle install in OSX, try installing the gem running:
|
648
|
+
|
649
|
+
`gem install mysql2 -v '0.5.3' -- --with-ldflags=-L/usr/local/opt/openssl/lib --with-cppflags=-I/usr/local/opt/openssl/include`
|
650
|
+
|
574
651
|
## License
|
575
652
|
|
576
653
|
Apartment is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler'
|
5
|
+
rescue StandardError
|
6
|
+
'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
2
8
|
Bundler.setup
|
3
9
|
Bundler::GemHelper.install_tasks
|
4
10
|
|
5
11
|
require 'appraisal'
|
6
12
|
|
7
|
-
require
|
8
|
-
require
|
13
|
+
require 'rspec'
|
14
|
+
require 'rspec/core/rake_task'
|
9
15
|
|
10
|
-
RSpec::Core::RakeTask.new(:
|
11
|
-
spec.pattern =
|
16
|
+
RSpec::Core::RakeTask.new(spec: %w[db:copy_credentials db:test:prepare]) do |spec|
|
17
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
12
18
|
# spec.rspec_opts = '--order rand:47078'
|
13
19
|
end
|
14
20
|
|
15
21
|
namespace :spec do
|
16
|
-
[
|
22
|
+
%i[tasks unit adapters integration].each do |type|
|
17
23
|
RSpec::Core::RakeTask.new(type => :spec) do |spec|
|
18
24
|
spec.pattern = "spec/#{type}/**/*_spec.rb"
|
19
25
|
end
|
@@ -27,11 +33,11 @@ task :console do
|
|
27
33
|
Pry.start
|
28
34
|
end
|
29
35
|
|
30
|
-
task :
|
36
|
+
task default: :spec
|
31
37
|
|
32
38
|
namespace :db do
|
33
39
|
namespace :test do
|
34
|
-
task :
|
40
|
+
task prepare: %w[postgres:drop_db postgres:build_db mysql:drop_db mysql:build_db]
|
35
41
|
end
|
36
42
|
|
37
43
|
desc "copy sample database credential files over if real files don't exist"
|
@@ -40,29 +46,36 @@ namespace :db do
|
|
40
46
|
apartment_db_file = 'spec/config/database.yml'
|
41
47
|
rails_db_file = 'spec/dummy/config/database.yml'
|
42
48
|
|
43
|
-
|
44
|
-
|
49
|
+
unless File.exist?(apartment_db_file)
|
50
|
+
FileUtils.copy("#{apartment_db_file}.sample", apartment_db_file, verbose: true)
|
51
|
+
end
|
52
|
+
FileUtils.copy("#{rails_db_file}.sample", rails_db_file, verbose: true) unless File.exist?(rails_db_file)
|
45
53
|
end
|
46
54
|
end
|
47
55
|
|
48
56
|
namespace :postgres do
|
49
57
|
require 'active_record'
|
50
|
-
require
|
58
|
+
require File.join(File.dirname(__FILE__), 'spec', 'support', 'config').to_s
|
51
59
|
|
52
60
|
desc 'Build the PostgreSQL test databases'
|
53
61
|
task :build_db do
|
54
62
|
params = []
|
55
|
-
params <<
|
63
|
+
params << '-E UTF8'
|
56
64
|
params << pg_config['database']
|
57
65
|
params << "-U#{pg_config['username']}"
|
58
66
|
params << "-h#{pg_config['host']}" if pg_config['host']
|
59
67
|
params << "-p#{pg_config['port']}" if pg_config['port']
|
60
|
-
|
68
|
+
|
69
|
+
begin
|
70
|
+
`createdb #{params.join(' ')}`
|
71
|
+
rescue StandardError
|
72
|
+
'test db already exists'
|
73
|
+
end
|
61
74
|
ActiveRecord::Base.establish_connection pg_config
|
62
75
|
migrate
|
63
76
|
end
|
64
77
|
|
65
|
-
desc
|
78
|
+
desc 'drop the PostgreSQL test database'
|
66
79
|
task :drop_db do
|
67
80
|
puts "dropping database #{pg_config['database']}"
|
68
81
|
params = []
|
@@ -70,14 +83,13 @@ namespace :postgres do
|
|
70
83
|
params << "-U#{pg_config['username']}"
|
71
84
|
params << "-h#{pg_config['host']}" if pg_config['host']
|
72
85
|
params << "-p#{pg_config['port']}" if pg_config['port']
|
73
|
-
|
86
|
+
`dropdb #{params.join(' ')}`
|
74
87
|
end
|
75
|
-
|
76
88
|
end
|
77
89
|
|
78
90
|
namespace :mysql do
|
79
91
|
require 'active_record'
|
80
|
-
require
|
92
|
+
require File.join(File.dirname(__FILE__), 'spec', 'support', 'config').to_s
|
81
93
|
|
82
94
|
desc 'Build the MySQL test databases'
|
83
95
|
task :build_db do
|
@@ -85,24 +97,29 @@ namespace :mysql do
|
|
85
97
|
params << "-h #{my_config['host']}" if my_config['host']
|
86
98
|
params << "-u #{my_config['username']}" if my_config['username']
|
87
99
|
params << "-p#{my_config['password']}" if my_config['password']
|
88
|
-
|
100
|
+
params << "--port #{my_config['port']}" if my_config['port']
|
101
|
+
begin
|
102
|
+
`mysqladmin #{params.join(' ')} create #{my_config['database']}`
|
103
|
+
rescue StandardError
|
104
|
+
'test db already exists'
|
105
|
+
end
|
89
106
|
ActiveRecord::Base.establish_connection my_config
|
90
107
|
migrate
|
91
108
|
end
|
92
109
|
|
93
|
-
desc
|
110
|
+
desc 'drop the MySQL test database'
|
94
111
|
task :drop_db do
|
95
112
|
puts "dropping database #{my_config['database']}"
|
96
113
|
params = []
|
97
114
|
params << "-h #{my_config['host']}" if my_config['host']
|
98
115
|
params << "-u #{my_config['username']}" if my_config['username']
|
99
116
|
params << "-p#{my_config['password']}" if my_config['password']
|
100
|
-
|
117
|
+
params << "--port #{my_config['port']}" if my_config['port']
|
118
|
+
`mysqladmin #{params.join(' ')} drop #{my_config['database']} --force`
|
101
119
|
end
|
102
|
-
|
103
120
|
end
|
104
121
|
|
105
|
-
# TODO clean this up
|
122
|
+
# TODO: clean this up
|
106
123
|
def config
|
107
124
|
Apartment::Test.config['connections']
|
108
125
|
end
|
@@ -115,14 +132,8 @@ def my_config
|
|
115
132
|
config['mysql']
|
116
133
|
end
|
117
134
|
|
118
|
-
def activerecord_below_5_2?
|
119
|
-
ActiveRecord.version.release < Gem::Version.new('5.2.0')
|
120
|
-
end
|
121
|
-
|
122
135
|
def migrate
|
123
|
-
if
|
124
|
-
|
125
|
-
|
126
|
-
ActiveRecord::MigrationContext.new('spec/dummy/db/migrate').migrate
|
127
|
-
end
|
136
|
+
# TODO: Figure out if there is any other possibility that can/should be
|
137
|
+
# passed here as the second argument for the migration context
|
138
|
+
ActiveRecord::MigrationContext.new('spec/dummy/db/migrate', ActiveRecord::SchemaMigration).migrate
|
128
139
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
# This is monkeypatching Active Record to ensure that whenever a new connection is established it
|
5
|
+
# switches to the same tenant as before the connection switching. This problem is more evident when
|
6
|
+
# using read replica in Rails 6
|
7
|
+
module ConnectionHandling
|
8
|
+
if ActiveRecord.version.release <= Gem::Version.new('6.2')
|
9
|
+
def connected_to_with_tenant(database: nil, role: nil, prevent_writes: false, &blk)
|
10
|
+
current_tenant = Apartment::Tenant.current
|
11
|
+
|
12
|
+
connected_to_without_tenant(database: database, role: role, prevent_writes: prevent_writes) do
|
13
|
+
Apartment::Tenant.switch!(current_tenant)
|
14
|
+
yield(blk)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
def connected_to_with_tenant(role: nil, prevent_writes: false, &blk)
|
19
|
+
current_tenant = Apartment::Tenant.current
|
20
|
+
|
21
|
+
connected_to_without_tenant(role: role, prevent_writes: prevent_writes) do
|
22
|
+
Apartment::Tenant.switch!(current_tenant)
|
23
|
+
yield(blk)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
alias connected_to_without_tenant connected_to
|
29
|
+
alias connected_to connected_to_with_tenant
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/ClassAndModuleChildren
|
4
|
+
|
5
|
+
# NOTE: This patch is meant to remove any schema_prefix appart from the ones for
|
6
|
+
# excluded models. The schema_prefix would be resolved by apartment's setting
|
7
|
+
# of search path
|
8
|
+
module Apartment::PostgreSqlAdapterPatch
|
9
|
+
def default_sequence_name(table, _column)
|
10
|
+
res = super
|
11
|
+
|
12
|
+
# for JDBC driver, if rescued in super_method, trim leading and trailing quotes
|
13
|
+
res.delete!('"') if defined?(JRUBY_VERSION)
|
14
|
+
|
15
|
+
schema_prefix = "#{Apartment::Tenant.current}."
|
16
|
+
default_tenant_prefix = "#{Apartment::Tenant.default_tenant}."
|
17
|
+
|
18
|
+
# NOTE: Excluded models should always access the sequence from the default
|
19
|
+
# tenant schema
|
20
|
+
if excluded_model?(table)
|
21
|
+
res.sub!(schema_prefix, default_tenant_prefix) if schema_prefix != default_tenant_prefix
|
22
|
+
return res
|
23
|
+
end
|
24
|
+
|
25
|
+
res.delete_prefix!(schema_prefix) if res&.starts_with?(schema_prefix)
|
26
|
+
|
27
|
+
res
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def excluded_model?(table)
|
33
|
+
Apartment.excluded_models.any? { |m| m.constantize.table_name == table }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
38
|
+
|
39
|
+
# NOTE: inject this into postgresql adapters
|
40
|
+
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
41
|
+
include Apartment::PostgreSqlAdapterPatch
|
42
|
+
end
|
43
|
+
# rubocop:enable Style/ClassAndModuleChildren
|