penthouse 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|