penthouse 0.11.0 → 0.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1b6d19ee617c554c080a828dbce840a3431879a
4
- data.tar.gz: b4c6b6ae7398e7b0bb78e5b17539865a3479e1ee
3
+ metadata.gz: 9901a76e3311f855d6ee6ca8d654c71f85e83ed8
4
+ data.tar.gz: 48832ed750877f9368e9d27541bf4b93cabb21c5
5
5
  SHA512:
6
- metadata.gz: 51ef4839e0ffcfcc637b82732e167f49e90e96e077d319d55ed483247ae2176e284770c6a05a4a214704adea35f41d004929cecff799e9431869fc3a4a6fa5fb
7
- data.tar.gz: 8cef1801324324d687794256e80f5622505f0f8ce74715a5508c923b546519fc80895f1f746b69e7a71be1384a769d37d969f595871787cdc3a731157c39c9a3
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
@@ -30,7 +30,7 @@ module Penthouse
30
30
  # @return [void]
31
31
  def call(env)
32
32
  request = Rack::Request.new(env)
33
- runner.call(router.call(request)) do
33
+ runner.call(tenant_identifier: router.call(request)) do
34
34
  app.call(env)
35
35
  end
36
36
  end
@@ -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 self.call(tenant_identifier, &block)
19
- load_tenant(tenant_identifier).call do |tenant|
20
- Penthouse.with_tenant(tenant.identifier) do
21
- block.yield(tenant)
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 self.load_tenant(tenant_identifier)
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 self.load_tenant(tenant_identifier)
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
@@ -3,7 +3,7 @@ module Penthouse
3
3
  module Middleware
4
4
  class Server
5
5
  def call(worker_class, item, queue)
6
- Penthouse.switch(item['tenant']) do
6
+ Penthouse.switch(tenant_identifier: item['tenant']) do
7
7
  yield
8
8
  end
9
9
  end
@@ -14,7 +14,7 @@ module Penthouse
14
14
  private :identifier=
15
15
 
16
16
  # @param identifier [String, Symbol] An identifier for the tenant
17
- def initialize(identifier)
17
+ def initialize(identifier:, **args)
18
18
  self.identifier = identifier
19
19
  end
20
20
 
@@ -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, shard:, tenant_schema: "public", **options)
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
- def initialize(identifier, tenant_schema:, persistent_schemas: ["shared_extensions"], default_schema: "public")
25
- super(identifier)
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(default_schema).join(", ")
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?
@@ -1,3 +1,3 @@
1
1
  module Penthouse
2
- VERSION = "0.11.0"
2
+ VERSION = "0.12.0"
3
3
  end
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, default_tenant: self.tenant, &block)
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, runner: self.configuration.runner, &block)
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, runner: self.configuration.runner, **options)
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, runner: self.configuration.runner, **options)
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.11.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-03-18 00:00:00.000000000 Z
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