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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 190533a0da2c8d16b425d2ffa4bde288e19a97da
4
- data.tar.gz: 69a6e1b680e9525c513e386d427de26f5b6667fd
3
+ metadata.gz: 5fdce337d0e5a5c6662f7d2fbfd2a3f14877fc7a
4
+ data.tar.gz: ca32b3019f2bdf8b105f962c8f1f40767a86cf95
5
5
  SHA512:
6
- metadata.gz: 46a2dce89a0a2577808f9bc325766a010b19f58f0fa566c414a9aaa5ba16352e08ae84f25ad0b5d8b55f689b2119ed15156654fa22203527ab5c0f58e2da130f
7
- data.tar.gz: 0a502681b521e29b05fbdb96b78b5b86821ea35924a05693557566b95ae127ede976bd866efa8e2c7df977b47ff99823aca7720a9de0eaf915c256eb92d6fb6e
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
- # Typically used by the App to return a tenant that can be switched to
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
@@ -8,10 +8,13 @@
8
8
 
9
9
  require_relative './base_tenant'
10
10
  require 'octopus'
11
+ require_relative './migratable'
11
12
 
12
13
  module Penthouse
13
14
  module Tenants
14
15
  class OctopusShardTenant < BaseTenant
16
+ include Migratable
17
+
15
18
  attr_accessor :shard
16
19
  private :shard=
17
20
 
@@ -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
- # ensures we're on the master Octopus shard, just updates the schema name
30
- # with the tenant name
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
@@ -1,3 +1,3 @@
1
1
  module Penthouse
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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-14 00:00:00.000000000 Z
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