penthouse 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +70 -0
- data/README.md +20 -4
- data/lib/penthouse/app.rb +1 -1
- data/lib/penthouse/runners/base_runner.rb +24 -5
- data/lib/penthouse/runners/schema_runner.rb +2 -2
- data/lib/penthouse/sidekiq/middleware/server.rb +1 -1
- data/lib/penthouse/tenants/base_tenant.rb +1 -1
- data/lib/penthouse/tenants/octopus_schema_tenant.rb +3 -3
- data/lib/penthouse/tenants/octopus_shard_tenant.rb +2 -2
- data/lib/penthouse/tenants/schema_tenant.rb +7 -5
- data/lib/penthouse/version.rb +1 -1
- data/lib/penthouse.rb +8 -8
- data/penthouse.gemspec +3 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9901a76e3311f855d6ee6ca8d654c71f85e83ed8
|
4
|
+
data.tar.gz: 48832ed750877f9368e9d27541bf4b93cabb21c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eeabfea803c0467bc39ec9a629c650b2a2d25061e8effa341a7efb1e90ec8632c0da07c8788ec6fff5a726a35adf2ff06c4c9935c8588514987a818fcc0e519a
|
7
|
+
data.tar.gz: a35faf59909bd4e05ecf4e772ccddaa31fe10519968e38d2508d1ed7e62893e6fd6556dc0d71d33a215c8079eb4760c688aba2864dc519a9197f41536715e2a5
|
data/Guardfile
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features) \
|
6
|
+
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
7
|
+
|
8
|
+
## Note: if you are using the `directories` clause above and you are not
|
9
|
+
## watching the project directory ('.'), then you will want to move
|
10
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
11
|
+
#
|
12
|
+
# $ mkdir config
|
13
|
+
# $ mv Guardfile config/
|
14
|
+
# $ ln -s config/Guardfile .
|
15
|
+
#
|
16
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
17
|
+
|
18
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
19
|
+
# rspec may be run, below are examples of the most common uses.
|
20
|
+
# * bundler: 'bundle exec rspec'
|
21
|
+
# * bundler binstubs: 'bin/rspec'
|
22
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
23
|
+
# installed the spring binstubs per the docs)
|
24
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
|
+
# * 'just' rspec: 'rspec'
|
26
|
+
|
27
|
+
guard :rspec, cmd: "bundle exec rspec", failed_mode: :focus do
|
28
|
+
require "guard/rspec/dsl"
|
29
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
30
|
+
|
31
|
+
# Feel free to open issues for suggestions and improvements
|
32
|
+
|
33
|
+
# RSpec files
|
34
|
+
rspec = dsl.rspec
|
35
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
36
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
37
|
+
watch(rspec.spec_files)
|
38
|
+
|
39
|
+
# Ruby files
|
40
|
+
ruby = dsl.ruby
|
41
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
42
|
+
|
43
|
+
# Rails files
|
44
|
+
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
45
|
+
dsl.watch_spec_files_for(rails.app_files)
|
46
|
+
dsl.watch_spec_files_for(rails.views)
|
47
|
+
|
48
|
+
watch(rails.controllers) do |m|
|
49
|
+
[
|
50
|
+
rspec.spec.("routing/#{m[1]}_routing"),
|
51
|
+
rspec.spec.("controllers/#{m[1]}_controller"),
|
52
|
+
rspec.spec.("acceptance/#{m[1]}")
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
# Rails config changes
|
57
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
58
|
+
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
59
|
+
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
60
|
+
|
61
|
+
# Capybara features specs
|
62
|
+
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
63
|
+
watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
|
64
|
+
|
65
|
+
# Turnip features and steps
|
66
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
67
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
68
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
69
|
+
end
|
70
|
+
end
|
data/README.md
CHANGED
@@ -18,10 +18,6 @@ If you're using Rails, you just need to configure an initializer at `config/init
|
|
18
18
|
require 'penthouse'
|
19
19
|
# include the standard Rack app
|
20
20
|
require 'penthouse/app'
|
21
|
-
# include the automated Sidekiq integration, should you need it
|
22
|
-
require 'penthouse/sidekiq' if defined?(Sidekiq)
|
23
|
-
# include the automated ActiveJob integration, should you need it
|
24
|
-
require 'penthouse/active_job' if defined?(ActiveJob)
|
25
21
|
# require the relevant router/runner you wish to use
|
26
22
|
require 'penthouse/routers/subdomain_router'
|
27
23
|
require 'penthouse/runners/schema_runner'
|
@@ -56,6 +52,26 @@ Octopus.setup do |config|
|
|
56
52
|
end
|
57
53
|
```
|
58
54
|
|
55
|
+
## ActiveJob
|
56
|
+
|
57
|
+
If you are using ActiveJob, you'll need to create an `active_job.rb` initializer:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
require 'penthouse/active_job'
|
61
|
+
|
62
|
+
class ActiveJob::Base
|
63
|
+
include Penthouse::ActiveJob
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
## Sidekiq
|
68
|
+
|
69
|
+
If you are using Sidekiq, you simply need to include the Penthouse module, this can be done in the `penthouse.rb` initializer:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'penthouse/sidekiq'
|
73
|
+
```
|
74
|
+
|
59
75
|
## Dictionary
|
60
76
|
|
61
77
|
* **Router** – this class receives a Rack request object and returns an identifier (just a string or symbol) for the tenant.
|
data/lib/penthouse/app.rb
CHANGED
@@ -11,25 +11,44 @@ module Penthouse
|
|
11
11
|
module Runners
|
12
12
|
class BaseRunner
|
13
13
|
|
14
|
+
PENTHOUSE_RUNNER_CALL_STACK = :current_penthouse_runner_call_stack
|
15
|
+
|
14
16
|
# @param tenant_identifier [String, Symbol] The identifier for the tenant
|
15
17
|
# @param block [Block] The code to execute within the tenant
|
16
18
|
# @return [void]
|
17
19
|
# @raise [Penthouse::TenantNotFound] if the tenant cannot be switched to
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def call(tenant_identifier:, &block)
|
21
|
+
previous_tenant_identifier = call_stack.last || 'public'
|
22
|
+
call_stack.push(tenant_identifier)
|
23
|
+
|
24
|
+
result = nil
|
25
|
+
|
26
|
+
begin
|
27
|
+
load_tenant(tenant_identifier: tenant_identifier, previous_tenant_identifier: previous_tenant_identifier).call do |tenant|
|
28
|
+
Penthouse.with_tenant(tenant_identifier: tenant.identifier) do
|
29
|
+
result = block.yield(tenant)
|
30
|
+
end
|
22
31
|
end
|
32
|
+
ensure
|
33
|
+
call_stack.pop
|
23
34
|
end
|
35
|
+
result
|
24
36
|
end
|
25
37
|
|
26
38
|
# @abstract returns the tenant object
|
27
39
|
# @param tenant_identifier [String, Symbol] The identifier for the tenant
|
28
40
|
# @return [Penthouse::Tenants::BaseTenant] An instance of a tenant
|
29
41
|
# @raise [Penthouse::TenantNotFound] if the tenant cannot be switched to
|
30
|
-
def
|
42
|
+
def load_tenant(tenant_identifier:, previous_tenant_identifier: 'public')
|
31
43
|
raise NotImplementedError
|
32
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def call_stack
|
49
|
+
Thread.current[PENTHOUSE_RUNNER_CALL_STACK] ||= []
|
50
|
+
end
|
51
|
+
|
33
52
|
end
|
34
53
|
end
|
35
54
|
end
|
@@ -11,8 +11,8 @@ module Penthouse
|
|
11
11
|
|
12
12
|
# @param tenant_identifier [String, Symbol] The identifier for the tenant
|
13
13
|
# @return [Penthouse::Tenants::BaseTenant] An instance of a tenant
|
14
|
-
def
|
15
|
-
Tenants::SchemaTenant.new(tenant_identifier, tenant_schema: tenant_identifier)
|
14
|
+
def load_tenant(tenant_identifier:, previous_tenant_identifier: 'public')
|
15
|
+
Tenants::SchemaTenant.new(identifier: tenant_identifier, tenant_schema: tenant_identifier, previous_schema: previous_tenant_identifier)
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
@@ -29,21 +29,21 @@ module Penthouse
|
|
29
29
|
# creates the tenant schema within the master shard
|
30
30
|
# @see Penthouse::Tenants::SchemaTenant#create
|
31
31
|
# @return [void]
|
32
|
-
def create(
|
32
|
+
def create(**)
|
33
33
|
call { super }
|
34
34
|
end
|
35
35
|
|
36
36
|
# drops the tenant schema within the master shard
|
37
37
|
# @see Penthouse::Tenants::SchemaTenant#delete
|
38
38
|
# @return [void]
|
39
|
-
def delete(
|
39
|
+
def delete(**)
|
40
40
|
call { super }
|
41
41
|
end
|
42
42
|
|
43
43
|
# returns whether or not the schema exists
|
44
44
|
# @see Penthouse::Tenants::SchemaTenant#exists?
|
45
45
|
# @return [Boolean] whether or not the schema exists in the master shard
|
46
|
-
def exists?(
|
46
|
+
def exists?(**)
|
47
47
|
call { super }
|
48
48
|
end
|
49
49
|
end
|
@@ -18,9 +18,9 @@ module Penthouse
|
|
18
18
|
# @param identifier [String, Symbol] An identifier for the tenant
|
19
19
|
# @param shard [String, Symbol] the configured Octopus shard to use for this tenant
|
20
20
|
# @param tenant_schema [String] your tenant's schema name within the Postgres shard, typically just 'public' as the shard should be dedicated
|
21
|
-
def initialize(identifier
|
21
|
+
def initialize(identifier:, shard:, tenant_schema: "public", **options)
|
22
22
|
self.shard = shard
|
23
|
-
super(identifier, tenant_schema: tenant_schema, **options)
|
23
|
+
super(identifier: identifier, tenant_schema: tenant_schema, **options)
|
24
24
|
end
|
25
25
|
|
26
26
|
# switches to the relevant Octopus shard, and processes the block
|
@@ -14,18 +14,20 @@ module Penthouse
|
|
14
14
|
class SchemaTenant < BaseTenant
|
15
15
|
include Migratable
|
16
16
|
|
17
|
-
attr_accessor :tenant_schema, :persistent_schemas, :default_schema
|
17
|
+
attr_accessor :tenant_schema, :persistent_schemas, :default_schema, :previous_schema
|
18
18
|
private :tenant_schema=, :persistent_schemas=, :default_schema=
|
19
19
|
|
20
20
|
# @param identifier [String, Symbol] An identifier for the tenant
|
21
21
|
# @param tenant_schema [String] your tenant's schema name in Postgres
|
22
22
|
# @param persistent_schemas [Array<String>] The schemas you always want in the search path
|
23
23
|
# @param default_schema [String] The global schema name, usually 'public'
|
24
|
-
|
25
|
-
|
24
|
+
# @param previous_schema [String] The previous schema name, usually 'public' unless dealing with nested calls.
|
25
|
+
def initialize(identifier:, tenant_schema:, persistent_schemas: ["shared_extensions"], default_schema: "public", previous_schema: default_schema)
|
26
|
+
super
|
26
27
|
self.tenant_schema = tenant_schema.freeze
|
27
28
|
self.persistent_schemas = Array(persistent_schemas).flatten.freeze
|
28
29
|
self.default_schema = default_schema.freeze
|
30
|
+
self.previous_schema = previous_schema.freeze
|
29
31
|
freeze
|
30
32
|
end
|
31
33
|
|
@@ -41,7 +43,7 @@ module Penthouse
|
|
41
43
|
block.yield(self)
|
42
44
|
ensure
|
43
45
|
# reset the search path back to the default
|
44
|
-
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.dup.unshift(
|
46
|
+
ActiveRecord::Base.connection.schema_search_path = persistent_schemas.dup.unshift(previous_schema).join(", ")
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
@@ -67,7 +69,7 @@ module Penthouse
|
|
67
69
|
|
68
70
|
# returns whether or not this tenant's schema exists
|
69
71
|
# @return [Boolean] whether or not the tenant exists
|
70
|
-
def exists?
|
72
|
+
def exists?(**)
|
71
73
|
sql = ActiveRecord::Base.send(:sanitize_sql_array, ["select 1 from pg_namespace where nspname = '%s'", tenant_schema])
|
72
74
|
result = ActiveRecord::Base.connection.exec_query(sql, "Schema Exists")
|
73
75
|
!result.rows.empty?
|
data/lib/penthouse/version.rb
CHANGED
data/lib/penthouse.rb
CHANGED
@@ -29,7 +29,7 @@ module Penthouse
|
|
29
29
|
# @param block [Block] the code to execute
|
30
30
|
# @yield [String, Symbol] the identifier for the tenant
|
31
31
|
# @return [void]
|
32
|
-
def with_tenant(tenant_identifier
|
32
|
+
def with_tenant(tenant_identifier:, default_tenant: self.tenant, &block)
|
33
33
|
self.tenant = tenant_identifier
|
34
34
|
block.yield(tenant_identifier)
|
35
35
|
ensure
|
@@ -45,7 +45,7 @@ module Penthouse
|
|
45
45
|
# @return [void]
|
46
46
|
def each_tenant(tenant_identifiers: self.tenant_identifiers, runner: self.configuration.runner, &block)
|
47
47
|
tenant_identifiers.each do |tenant_identifier|
|
48
|
-
switch(tenant_identifier, runner: runner, &block)
|
48
|
+
switch(tenant_identifier: tenant_identifier, runner: runner, &block)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,16 +55,16 @@ module Penthouse
|
|
55
55
|
# @param block [Block] the code to execute
|
56
56
|
# @yield [Penthouse::Tenants::BaseTenant] the tenant instance
|
57
57
|
# @return [void]
|
58
|
-
def switch(tenant_identifier
|
59
|
-
runner.call(tenant_identifier, &block)
|
58
|
+
def switch(tenant_identifier:, runner: self.configuration.runner, &block)
|
59
|
+
runner.call(tenant_identifier: tenant_identifier, &block)
|
60
60
|
end
|
61
61
|
|
62
62
|
# Loads the tenant and creates their data store
|
63
63
|
# @param tenant_identifier [String, Symbol] the identifier for the tenant
|
64
64
|
# @see Penthouse::Tenants::BaseTenant#delete
|
65
65
|
# @return [void]
|
66
|
-
def create(tenant_identifier
|
67
|
-
switch(tenant_identifier, runner: runner) do |tenant|
|
66
|
+
def create(tenant_identifier:, runner: self.configuration.runner, **options)
|
67
|
+
switch(tenant_identifier: tenant_identifier, runner: runner) do |tenant|
|
68
68
|
tenant.create(**options)
|
69
69
|
end
|
70
70
|
end
|
@@ -73,8 +73,8 @@ module Penthouse
|
|
73
73
|
# @param tenant_identifier [String, Symbol] the identifier for the tenant
|
74
74
|
# @see Penthouse::Tenants::BaseTenant#delete
|
75
75
|
# @return [void]
|
76
|
-
def delete(tenant_identifier
|
77
|
-
switch(tenant_identifier, runner: runner) do |tenant|
|
76
|
+
def delete(tenant_identifier:, runner: self.configuration.runner, **options)
|
77
|
+
switch(tenant_identifier: tenant_identifier, runner: runner) do |tenant|
|
78
78
|
tenant.delete(**options)
|
79
79
|
end
|
80
80
|
end
|
data/penthouse.gemspec
CHANGED
@@ -33,4 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency 'activesupport', '~> 4.2.6'
|
34
34
|
spec.add_development_dependency 'activerecord', '~> 4.2.6'
|
35
35
|
spec.add_development_dependency 'pg'
|
36
|
+
|
37
|
+
spec.add_development_dependency 'guard-rspec'
|
38
|
+
spec.add_development_dependency 'pry'
|
36
39
|
end
|
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.12.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-
|
11
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -178,6 +178,34 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: guard-rspec
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: pry
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
181
209
|
description:
|
182
210
|
email:
|
183
211
|
- ryan@ryantownsend.co.uk
|
@@ -191,6 +219,7 @@ files:
|
|
191
219
|
- ".ruby-gemset"
|
192
220
|
- ".ruby-version"
|
193
221
|
- Gemfile
|
222
|
+
- Guardfile
|
194
223
|
- README.md
|
195
224
|
- Rakefile
|
196
225
|
- bin/console
|