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 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