penthouse 0.2.0 → 0.3.0
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.
- checksums.yaml +4 -4
- data/lib/penthouse/configuration.rb +10 -2
- data/lib/penthouse/routers/base_router.rb +1 -1
- data/lib/penthouse/runners/base_runner.rb +1 -0
- data/lib/penthouse/tenants/base_tenant.rb +11 -1
- data/lib/penthouse/tenants/migratable.rb +31 -0
- data/lib/penthouse/tenants/octopus_schema_tenant.rb +18 -0
- data/lib/penthouse/tenants/octopus_shard_tenant.rb +3 -0
- data/lib/penthouse/tenants/schema_tenant.rb +37 -8
- data/lib/penthouse/version.rb +1 -1
- data/lib/penthouse.rb +19 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fdce337d0e5a5c6662f7d2fbfd2a3f14877fc7a
|
4
|
+
data.tar.gz: ca32b3019f2bdf8b105f962c8f1f40767a86cf95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbc0c2e3951d8acd63ffe18c6a2dd2d3ea4a32b508001767ce64cfbff1c30e8d63e913f0401688c157bf88b95f0c69a977a735c4cc6f1c8c3d2433283bf644f5
|
7
|
+
data.tar.gz: 2eab6ab62ea17238ab22999b6c7a0f6f8bc7228583ee06f29bef91e9e3a2676a42fc25b681d649f5b737c4b141826ca75562e07a40b2575331151be4e76d97b1
|
@@ -12,17 +12,25 @@
|
|
12
12
|
|
13
13
|
module Penthouse
|
14
14
|
class Configuration
|
15
|
-
attr_accessor :router, :runner, :migrate_tenants, :tenant_identifiers
|
15
|
+
attr_accessor :router, :runner, :migrate_tenants, :db_schema_file, :tenant_identifiers
|
16
16
|
|
17
17
|
# @param router [Penthouse::Routers::BaseRouter] the default router for your application to use
|
18
18
|
# @param runner [Penthouse::Runners::BaseRunner] the default runner for your application to use
|
19
19
|
# @param migrate_tenants [Boolean] whether you want Penthouse to automatically migrate all tenants
|
20
|
+
# @param db_schema_file [String] a path to your schema file (typically `db/schema.rb` or `db/structure.sql` in Rails)
|
20
21
|
# @param tenant_identifiers [Proc] some code which must return an array of tenant identifiers (strings/symbols)
|
21
|
-
def initialize(router: nil, runner: nil, migrate_tenants: false, tenant_identifiers: -> { raise NotImplementedError })
|
22
|
+
def initialize(router: nil, runner: nil, migrate_tenants: false, db_schema_file: nil, tenant_identifiers: -> { raise NotImplementedError })
|
22
23
|
self.router = router
|
23
24
|
self.runner = runner
|
24
25
|
self.migrate_tenants = migrate_tenants
|
26
|
+
self.db_schema_file = db_schema_file
|
25
27
|
self.tenant_identifiers = tenant_identifiers
|
28
|
+
|
29
|
+
if migrate_tenants? && !db_schema_file
|
30
|
+
raise ArgumentError, "If you want to migrate tenants, we need a path to a DB schema file"
|
31
|
+
elsif migrate_tenants? && !File.exist?(db_schema_file)
|
32
|
+
raise ArgumentError, "#{db_schema_file} is not readable"
|
33
|
+
end
|
26
34
|
end
|
27
35
|
|
28
36
|
# @return [Boolean] whether or not Penthouse should automatically migrate all tenants
|
@@ -11,7 +11,7 @@ module Penthouse
|
|
11
11
|
module Routers
|
12
12
|
class BaseRouter
|
13
13
|
|
14
|
-
#
|
14
|
+
# @abstract typically used by the App to receive a request and return a tenant that can be switched to
|
15
15
|
# @param request [Rack::Request] The request from the Rack app, used to determine the tenant
|
16
16
|
# @return [String, Symbol] A tenant identifier
|
17
17
|
# @raise [Penthouse::TenantNotFound] if the tenant cannot be found/switched to
|
@@ -22,6 +22,7 @@ module Penthouse
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
# @abstract returns the tenant object
|
25
26
|
# @param tenant_identifier [String, Symbol] The identifier for the tenant
|
26
27
|
# @return [Penthouse::Tenants::BaseTenant] An instance of a tenant
|
27
28
|
# @raise [Penthouse::TenantNotFound] if the tenant cannot be switched to
|
@@ -18,12 +18,22 @@ module Penthouse
|
|
18
18
|
self.identifier = identifier
|
19
19
|
end
|
20
20
|
|
21
|
-
# placeholder for the relevant tenant-switching code
|
21
|
+
# @abstract placeholder for the relevant tenant-switching code
|
22
22
|
# @param block [Block] The code to execute within the tenant
|
23
23
|
# @yield [BaseTenant] The current tenant instance
|
24
24
|
def call(&block)
|
25
25
|
raise NotImplementedError
|
26
26
|
end
|
27
|
+
|
28
|
+
# @abstract creates the tenant data store
|
29
|
+
def create(*)
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
33
|
+
# @abstract deletes the tenant data store
|
34
|
+
def delete(*)
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#
|
2
|
+
# This class provides an abstract for the tenant interface. Whilst any Proc
|
3
|
+
# could be used, it's safest for people to sub-class to ensure that any future
|
4
|
+
# interface changes are catered for.
|
5
|
+
#
|
6
|
+
# A tenant class's responsibility is to receive a block, around which it should
|
7
|
+
# handle switching to the given tenant's configuration, ensuring that if an
|
8
|
+
# exception occurs, the configuration is reset back to the global configuration.
|
9
|
+
#
|
10
|
+
module Penthouse
|
11
|
+
module Tenants
|
12
|
+
module Migratable
|
13
|
+
|
14
|
+
# @param db_schema_file [String] a path to the DB schema file to load, defaults to Penthouse.configuration.db_schema_file
|
15
|
+
def migrate(db_schema_file: Penthouse.configuration.db_schema_file)
|
16
|
+
if File.exist?(db_schema_file)
|
17
|
+
# turn off logging schemas
|
18
|
+
ActiveRecord::Schema.verbose = false
|
19
|
+
# run the migrations within this schema
|
20
|
+
call do
|
21
|
+
puts ActiveRecord::Base.connection.schema_search_path
|
22
|
+
load(db_schema_file)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
raise ArgumentError, "#{db_schema_file} does not exist"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -23,6 +23,24 @@ module Penthouse
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
# creates the tenant schema within the master shard
|
28
|
+
# @see Penthouse::Tenants::SchemaTenant#create
|
29
|
+
def create(*)
|
30
|
+
call { super }
|
31
|
+
end
|
32
|
+
|
33
|
+
# drops the tenant schema within the master shard
|
34
|
+
# @see Penthouse::Tenants::SchemaTenant#delete
|
35
|
+
def delete(*)
|
36
|
+
call { super }
|
37
|
+
end
|
38
|
+
|
39
|
+
# returns whether or not the schema exists
|
40
|
+
# @see Penthouse::Tenants::SchemaTenant#exists?
|
41
|
+
def exists?(*)
|
42
|
+
call { super }
|
43
|
+
end
|
26
44
|
end
|
27
45
|
end
|
28
46
|
end
|
@@ -6,40 +6,69 @@
|
|
6
6
|
#
|
7
7
|
|
8
8
|
require_relative './base_tenant'
|
9
|
+
require_relative './migratable'
|
9
10
|
|
10
11
|
module Penthouse
|
11
12
|
module Tenants
|
12
13
|
class SchemaTenant < BaseTenant
|
14
|
+
include Migratable
|
15
|
+
|
13
16
|
attr_accessor :tenant_schema, :persistent_schemas, :default_schema
|
14
17
|
private :tenant_schema=, :persistent_schemas=, :default_schema=
|
15
18
|
|
16
19
|
# @param identifier [String, Symbol] An identifier for the tenant
|
17
20
|
# @param tenant_schema [String] your tenant's schema name in Postgres
|
18
|
-
# @param tenant_schema [String] your tenant's schema name in Postgres
|
19
21
|
# @param persistent_schemas [Array<String>] The schemas you always want in the search path
|
20
22
|
# @param default_schema [String] The global schema name, usually 'public'
|
21
23
|
def initialize(identifier, tenant_schema:, persistent_schemas: ["shared_extensions"], default_schema: "public")
|
22
24
|
super(identifier)
|
23
|
-
self.tenant_schema = tenant_schema
|
24
|
-
self.persistent_schemas = Array(persistent_schemas).flatten
|
25
|
-
self.default_schema = default_schema
|
25
|
+
self.tenant_schema = tenant_schema.freeze
|
26
|
+
self.persistent_schemas = Array(persistent_schemas).flatten.freeze
|
27
|
+
self.default_schema = default_schema.freeze
|
26
28
|
freeze
|
27
29
|
end
|
28
30
|
|
29
|
-
#
|
30
|
-
#
|
31
|
+
# switches to the tenant schema to run the block, ensuring we switch back
|
32
|
+
# afterwards, regardless of whether an exception occurs
|
31
33
|
# @param block [Block] The code to execute within the schema
|
32
34
|
# @yield [SchemaTenant] The current tenant instance
|
33
35
|
def call(&block)
|
34
36
|
begin
|
35
37
|
# set the search path to include the tenant
|
36
|
-
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.unshift(tenant_schema).join(", ")
|
38
|
+
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.dup.unshift(tenant_schema).join(", ")
|
37
39
|
block.yield(self)
|
38
40
|
ensure
|
39
41
|
# reset the search path back to the default
|
40
|
-
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.unshift(default_schema).join(", ")
|
42
|
+
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.dup.unshift(default_schema).join(", ")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# creates the tenant schema
|
47
|
+
# @param run_migrations [Boolean] whether or not to run migrations, defaults to Penthouse.configuration.migrate_tenants?
|
48
|
+
# @param db_schema_file [String] a path to the DB schema file to load, defaults to Penthouse.configuration.db_schema_file
|
49
|
+
def create(run_migrations: Penthouse.configuration.migrate_tenants?, db_schema_file: Penthouse.configuration.db_schema_file)
|
50
|
+
sql = ActiveRecord::Base.send(:sanitize_sql_array, ["create schema if not exists %s", tenant_schema])
|
51
|
+
ActiveRecord::Base.connection.exec_query(sql, 'Create Schema')
|
52
|
+
if !!run_migrations
|
53
|
+
migrate(db_schema_file: db_schema_file)
|
41
54
|
end
|
42
55
|
end
|
56
|
+
|
57
|
+
# drops the tenant schema
|
58
|
+
# @param force [Boolean] whether or not to drop the schema if not empty, defaults to true
|
59
|
+
def delete(force: true)
|
60
|
+
sql = ActiveRecord::Base.send(:sanitize_sql_array, ["drop schema if exists %s %s", tenant_schema, force ? 'cascade' : 'restrict'])
|
61
|
+
ActiveRecord::Base.connection.exec_query(sql, 'Delete Schema')
|
62
|
+
end
|
63
|
+
|
64
|
+
# returns whether or not this tenant's schema exists
|
65
|
+
# @return [Boolean] whether or not the tenant exists
|
66
|
+
def exists?
|
67
|
+
sql = ActiveRecord::Base.send(:sanitize_sql_array, ["select 1 from pg_namespace where nspname = '%s'", tenant_schema])
|
68
|
+
result = ActiveRecord::Base.connection.exec_query(sql, "Schema Exists")
|
69
|
+
!result.rows.empty?
|
70
|
+
end
|
71
|
+
|
43
72
|
end
|
44
73
|
end
|
45
74
|
end
|
data/lib/penthouse/version.rb
CHANGED
data/lib/penthouse.rb
CHANGED
@@ -47,10 +47,29 @@ module Penthouse
|
|
47
47
|
# @param tenant_identifier [String, Symbol] the identifier for the tenant
|
48
48
|
# @param runner [Penthouse::Runners::BaseRunner] an optional runner to use, defaults to the one configured
|
49
49
|
# @param block [Block] the code to execute
|
50
|
+
# @yield [Penthouse::Tenants::BaseTenant] the tenant instance
|
50
51
|
def switch(tenant_identifier, runner: configuration.runner, &block)
|
51
52
|
runner.call(tenant_identifier, &block)
|
52
53
|
end
|
53
54
|
|
55
|
+
# Loads the tenant and creates their data store
|
56
|
+
# @param tenant_identifier [String, Symbol] the identifier for the tenant
|
57
|
+
# @see Penthouse::Tenants::BaseTenant#delete
|
58
|
+
def create(tenant_identifier, runner: configuration.runner, **options)
|
59
|
+
switch(tenant_identifier, runner: runner) do |tenant|
|
60
|
+
tenant.create(**options)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Loads the tenant and deletes their data store
|
65
|
+
# @param tenant_identifier [String, Symbol] the identifier for the tenant
|
66
|
+
# @see Penthouse::Tenants::BaseTenant#delete
|
67
|
+
def delete(tenant_identifier, runner: configuration.runner, **options)
|
68
|
+
switch(tenant_identifier, runner: runner) do |tenant|
|
69
|
+
tenant.delete(**options)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
54
73
|
# Allows you to configure the router of Penthouse
|
55
74
|
# @yield [Penthouse::Configuration]
|
56
75
|
def configure(&block)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: penthouse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Townsend
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/penthouse/sidekiq/railtie.rb
|
181
181
|
- lib/penthouse/tasks/enhancements.rb
|
182
182
|
- lib/penthouse/tenants/base_tenant.rb
|
183
|
+
- lib/penthouse/tenants/migratable.rb
|
183
184
|
- lib/penthouse/tenants/octopus_schema_tenant.rb
|
184
185
|
- lib/penthouse/tenants/octopus_shard_tenant.rb
|
185
186
|
- lib/penthouse/tenants/schema_tenant.rb
|