leases 0.0.1
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.
- data/.gitignore +19 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +100 -0
- data/Rakefile +5 -0
- data/leases.gemspec +29 -0
- data/lib/leases.rb +34 -0
- data/lib/leases/controller.rb +32 -0
- data/lib/leases/model.rb +32 -0
- data/lib/leases/model/base.rb +75 -0
- data/lib/leases/model/callbacks.rb +59 -0
- data/lib/leases/railtie.rb +12 -0
- data/lib/leases/version.rb +3 -0
- data/spec/leases/controller_spec.rb +38 -0
- data/spec/leases/model/base_spec.rb +93 -0
- data/spec/leases/model/callbacks_spec.rb +169 -0
- data/spec/leases/model_spec.rb +43 -0
- data/spec/leases_spec.rb +34 -0
- data/spec/spec_helper.rb +31 -0
- metadata +205 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Arjen Oosterkamp
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# Leases
|
2
|
+
|
3
|
+
Database multi-tenancy for Rails.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your Gemfile and run `bundle`.
|
8
|
+
|
9
|
+
```
|
10
|
+
gem 'leases'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# app/models/account.rb
|
17
|
+
class Account < ActiveRecord::Base
|
18
|
+
leases
|
19
|
+
end
|
20
|
+
|
21
|
+
# app/controllers/application_controller.rb
|
22
|
+
class ApplicationController < ActionController::Base
|
23
|
+
visit_as :current_account
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def current_account
|
28
|
+
@account
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
That's it. Now you have a fully multi-tenant app.
|
34
|
+
|
35
|
+
## More
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# app/models/account.rb
|
39
|
+
class Account < ActiveRecord::Base
|
40
|
+
|
41
|
+
# Customize the leaser name (used for the database name)
|
42
|
+
leases :name => :slug
|
43
|
+
leases :name => Proc.new { |c| "acount_#{c.id}" }
|
44
|
+
|
45
|
+
# Callbacks
|
46
|
+
on_enter :set_something # => Called on :enter (part of visit)
|
47
|
+
on_leave :clear_something # => Called on :leave (part of visit)
|
48
|
+
|
49
|
+
on_lease :create_something # => Called on new lease
|
50
|
+
on_break :delete_something # => Called on break of lease
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
account = Account.create :name => 'John Doe' # => Calls account.lease! and sets up database 'account-1'
|
55
|
+
|
56
|
+
account.enter # => Switches to database 'account-1'
|
57
|
+
account.leave # => Switches back to regular database
|
58
|
+
|
59
|
+
account.visit do
|
60
|
+
# You are now in account database context
|
61
|
+
# Context is automatically cleared after visit
|
62
|
+
end
|
63
|
+
|
64
|
+
account.destroy # => Calls account.break! and deletes leaser database
|
65
|
+
```
|
66
|
+
|
67
|
+
## Shared models
|
68
|
+
|
69
|
+
By default only the leaser model is a shared model. The rest is stored in a separate database. There may be situations in which you want to have more shared models, such as a `User` model. You can configure this as such:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
# app/models/user.rb
|
73
|
+
class User < ActiveRecord::Base
|
74
|
+
shared_by_leasers
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
## Under the hood
|
79
|
+
|
80
|
+
We're using `apartment` for managing and switching databases. This is a fantastic gem that you should check out: https://github.com/influitive/apartment. Apartment supports MySQL and PostgreSQL.
|
81
|
+
|
82
|
+
## Migrating
|
83
|
+
|
84
|
+
```
|
85
|
+
rake apartment:migrate
|
86
|
+
```
|
87
|
+
|
88
|
+
This will migrate all leaser databases.
|
89
|
+
|
90
|
+
## Other solutions
|
91
|
+
|
92
|
+
If you're looking for multi-tenancy without having multiple databases then you should check out acts_as_tenant: https://github.com/ErwinM/acts_as_tenant.
|
93
|
+
|
94
|
+
## Contributing
|
95
|
+
|
96
|
+
1. Fork it
|
97
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
98
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
99
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
100
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/leases.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'leases/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'leases'
|
8
|
+
gem.version = Leases::VERSION
|
9
|
+
gem.authors = ['Arjen Oosterkamp']
|
10
|
+
gem.email = ['mail@arjen.me']
|
11
|
+
gem.description = %q{Database multi-tenancy for Rails.}
|
12
|
+
gem.summary = %q{Database multi-tenancy for Rails.}
|
13
|
+
gem.homepage = 'https://github.com/Arjeno/leases'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
|
20
|
+
gem.add_development_dependency 'bundler', '>= 1.0.0'
|
21
|
+
gem.add_development_dependency 'rake'
|
22
|
+
gem.add_development_dependency 'rspec', '>= 2.3'
|
23
|
+
gem.add_development_dependency 'rspec-rails', '~> 2.0'
|
24
|
+
gem.add_development_dependency 'sqlite3'
|
25
|
+
gem.add_development_dependency 'with_model'
|
26
|
+
|
27
|
+
gem.add_dependency 'rails', '>= 3.0'
|
28
|
+
gem.add_dependency 'apartment', '>= 0.20'
|
29
|
+
end
|
data/lib/leases.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'leases/railtie'
|
2
|
+
require 'leases/version'
|
3
|
+
require 'leases/model'
|
4
|
+
require 'leases/controller'
|
5
|
+
|
6
|
+
module Leases
|
7
|
+
|
8
|
+
extend self
|
9
|
+
|
10
|
+
attr_accessor :leasers
|
11
|
+
self.leasers = []
|
12
|
+
|
13
|
+
# Add leaser to array of leasers.
|
14
|
+
#
|
15
|
+
# => leasing(Account)
|
16
|
+
def leasing(object)
|
17
|
+
self.leasers << object.name
|
18
|
+
self.leasers.uniq!
|
19
|
+
|
20
|
+
Apartment.database_names = Proc.new { Leases.leaser_names }
|
21
|
+
|
22
|
+
object
|
23
|
+
end
|
24
|
+
|
25
|
+
def leaser_names(preload=true)
|
26
|
+
Rails.application.eager_load! if preload
|
27
|
+
|
28
|
+
leasers.map do |l|
|
29
|
+
model = l.constantize
|
30
|
+
model.all.collect(&:leaser_name)
|
31
|
+
end.flatten
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Leases
|
2
|
+
module Controller
|
3
|
+
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
|
8
|
+
helper_method :current_leaser
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# Uses a leaser as the context in a controller.
|
15
|
+
#
|
16
|
+
# => visit_as :current_account
|
17
|
+
def visit_as(leaser)
|
18
|
+
around_filter do |c, block|
|
19
|
+
c.send(leaser).visit(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_leaser
|
26
|
+
Thread.current[:leaser]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ActionController::Base.send(:include, Leases::Controller)
|
data/lib/leases/model.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'leases/model/base'
|
2
|
+
require 'leases/model/callbacks'
|
3
|
+
|
4
|
+
module Leases
|
5
|
+
module Model
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def leases(options={})
|
12
|
+
include Base
|
13
|
+
include Callbacks
|
14
|
+
|
15
|
+
shared_by_leasers
|
16
|
+
|
17
|
+
Leases.leasing(self)
|
18
|
+
self.leases_options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def shared_by_leasers
|
22
|
+
Apartment.excluded_models ||= []
|
23
|
+
Apartment.excluded_models += [self.name]
|
24
|
+
Apartment.excluded_models.uniq!
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ActiveRecord::Base.send(:include, Leases::Model)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Leases
|
2
|
+
module Model
|
3
|
+
module Base
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
cattr_accessor :leases_options
|
10
|
+
self.leases_options = {}
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# Name of the leaser, used for naming the database.
|
15
|
+
# This can be set in the leaser_options. Must be unique.
|
16
|
+
def leaser_name
|
17
|
+
name = self.class.leases_options[:name]
|
18
|
+
|
19
|
+
if name.nil?
|
20
|
+
[self.class.name.parameterize, id].join('-')
|
21
|
+
elsif name.is_a?(Symbol)
|
22
|
+
send(name)
|
23
|
+
elsif name.is_a?(Proc)
|
24
|
+
name.call(self)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Enter the leaser-context.
|
29
|
+
#
|
30
|
+
# => account.enter
|
31
|
+
def enter
|
32
|
+
Apartment::Database.switch(leaser_name)
|
33
|
+
Thread.current[:leaser] = self
|
34
|
+
end
|
35
|
+
|
36
|
+
# Leave the leaser-context.
|
37
|
+
#
|
38
|
+
# => account.leave
|
39
|
+
def leave
|
40
|
+
Thread.current[:leaser] = nil
|
41
|
+
Apartment::Database.reset
|
42
|
+
end
|
43
|
+
|
44
|
+
# Visit a leaser by entering and leaving.
|
45
|
+
# Very useful for executing code in a leaser-context
|
46
|
+
#
|
47
|
+
# => account.visit { User.find(1) }
|
48
|
+
def visit(&block)
|
49
|
+
enter
|
50
|
+
begin
|
51
|
+
yield
|
52
|
+
ensure
|
53
|
+
leave
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a new lease.
|
58
|
+
# This is usually called when a model is created.
|
59
|
+
#
|
60
|
+
# => account.lease!
|
61
|
+
def lease!
|
62
|
+
Apartment::Database.create(leaser_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Break a lease.
|
66
|
+
# This is usually called when a model is destroyed.
|
67
|
+
#
|
68
|
+
# => account.break!
|
69
|
+
def break!
|
70
|
+
Apartment::Database.drop(leaser_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Leases
|
2
|
+
module Model
|
3
|
+
module Callbacks
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
|
9
|
+
## Callbacks
|
10
|
+
|
11
|
+
after_create :lease!
|
12
|
+
after_destroy :break!
|
13
|
+
|
14
|
+
## Custom callbacks
|
15
|
+
|
16
|
+
define_model_callbacks :enter, :leave, :lease, :break
|
17
|
+
|
18
|
+
# Define callback aliases
|
19
|
+
#
|
20
|
+
# => on_enter :method
|
21
|
+
# => on_leave :method
|
22
|
+
# => on_lease :method
|
23
|
+
# => on_break :method
|
24
|
+
class << self
|
25
|
+
alias :on_enter :after_enter
|
26
|
+
alias :on_leave :after_leave
|
27
|
+
alias :on_lease :after_lease
|
28
|
+
alias :on_break :after_break
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def enter
|
34
|
+
run_callbacks :enter do
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def leave
|
40
|
+
run_callbacks :leave do
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def lease!
|
46
|
+
run_callbacks :lease do
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def break!
|
52
|
+
run_callbacks :break do
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class AnonymousController < ActionController::Base
|
4
|
+
include Rails.application.routes.url_helpers
|
5
|
+
def index
|
6
|
+
@account = Thread.current[:leaser]
|
7
|
+
@current_leaser = current_leaser
|
8
|
+
render :text => :none
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe Leases::Controller, :type => :controller do
|
13
|
+
|
14
|
+
with_model :Account do
|
15
|
+
table do |t|
|
16
|
+
t.string :name
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
model do
|
20
|
+
leases
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
controller(AnonymousController) do
|
25
|
+
visit_as :current_account
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:account) { Account.create(:name => 'John Doe') }
|
29
|
+
|
30
|
+
before(:each) do
|
31
|
+
controller.stub(:current_account).and_return(account)
|
32
|
+
get :index
|
33
|
+
end
|
34
|
+
|
35
|
+
it { assigns(:account).should == account }
|
36
|
+
it { assigns(:current_leaser).should == account }
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Leases::Model::Base do
|
4
|
+
|
5
|
+
with_model :Account do
|
6
|
+
table do |t|
|
7
|
+
t.string :name
|
8
|
+
t.string :slug
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) { Account.leases options }
|
14
|
+
|
15
|
+
let(:options) { {} }
|
16
|
+
let(:account) { Account.create(:name => 'John Doe', :slug => 'john-doe') }
|
17
|
+
|
18
|
+
subject { account }
|
19
|
+
|
20
|
+
describe :leaser_name do
|
21
|
+
|
22
|
+
context 'default' do
|
23
|
+
|
24
|
+
let(:options) { {} }
|
25
|
+
|
26
|
+
its(:leaser_name) { should == "account-#{account.id}" }
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with symbol' do
|
31
|
+
|
32
|
+
let(:options) { { :name => :slug } }
|
33
|
+
|
34
|
+
its(:leaser_name) { should == account.slug }
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'with proc' do
|
39
|
+
|
40
|
+
let(:options) { { :name => Proc.new { |a| [a.id, a.name].join('-') } } }
|
41
|
+
|
42
|
+
its(:leaser_name) { should == "#{account.id}-#{account.name}" }
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe :enter do
|
49
|
+
|
50
|
+
after(:each) { account.enter }
|
51
|
+
|
52
|
+
it { Apartment::Database.should_receive(:switch).with(account.leaser_name) }
|
53
|
+
it { Thread.current[:leaser].id.should == account.id }
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe :leave do
|
58
|
+
|
59
|
+
after(:each) { account.leave }
|
60
|
+
|
61
|
+
it { Apartment::Database.should_receive(:reset) }
|
62
|
+
it { Thread.current[:leaser].should == nil }
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe :visit do
|
67
|
+
|
68
|
+
it 'should enter and leave' do
|
69
|
+
account.should_receive(:enter)
|
70
|
+
account.should_receive(:leave)
|
71
|
+
|
72
|
+
account.visit {}
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe :lease! do
|
78
|
+
|
79
|
+
after(:each) { account.lease! }
|
80
|
+
|
81
|
+
it { Apartment::Database.should_receive(:create).with(account.leaser_name) }
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
describe :break! do
|
86
|
+
|
87
|
+
after(:each) { account.break! }
|
88
|
+
|
89
|
+
it { Apartment::Database.should_receive(:drop).with(account.leaser_name) }
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Leases::Model::Callbacks do
|
4
|
+
|
5
|
+
with_model :Account do
|
6
|
+
table do |t|
|
7
|
+
t.string :name
|
8
|
+
t.string :slug
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) { Account.leases }
|
14
|
+
|
15
|
+
let(:account) { Account.create }
|
16
|
+
|
17
|
+
subject { account }
|
18
|
+
|
19
|
+
describe :enter do
|
20
|
+
|
21
|
+
before(:each) do
|
22
|
+
account.stub(:_test_callback_before_enter)
|
23
|
+
account.stub(:_test_callback_after_enter)
|
24
|
+
Account.before_enter :_test_callback_before_enter
|
25
|
+
Account.after_enter :_test_callback_after_enter
|
26
|
+
end
|
27
|
+
|
28
|
+
after(:each) { account.enter }
|
29
|
+
|
30
|
+
it { account.should_receive(:_test_callback_before_enter) }
|
31
|
+
it { account.should_receive(:_test_callback_after_enter) }
|
32
|
+
|
33
|
+
describe :aliases do
|
34
|
+
|
35
|
+
describe :on_enter do
|
36
|
+
|
37
|
+
before(:each) do
|
38
|
+
account.stub(:_test_callback_on_enter)
|
39
|
+
Account.on_enter :_test_callback_on_enter
|
40
|
+
end
|
41
|
+
|
42
|
+
it { account.should_receive(:_test_callback_on_enter) }
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
describe :leave do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
account.stub(:_test_callback_before_leave)
|
54
|
+
account.stub(:_test_callback_after_leave)
|
55
|
+
Account.before_leave :_test_callback_before_leave
|
56
|
+
Account.after_leave :_test_callback_after_leave
|
57
|
+
end
|
58
|
+
|
59
|
+
after(:each) { account.leave }
|
60
|
+
|
61
|
+
it { account.should_receive(:_test_callback_before_leave) }
|
62
|
+
it { account.should_receive(:_test_callback_after_leave) }
|
63
|
+
|
64
|
+
describe :aliases do
|
65
|
+
|
66
|
+
describe :on_leave do
|
67
|
+
|
68
|
+
before(:each) do
|
69
|
+
account.stub(:_test_callback_on_leave)
|
70
|
+
Account.on_leave :_test_callback_on_leave
|
71
|
+
end
|
72
|
+
|
73
|
+
it { account.should_receive(:_test_callback_on_leave) }
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe :lease! do
|
82
|
+
|
83
|
+
describe :after_create do
|
84
|
+
|
85
|
+
let(:account) { Account.new }
|
86
|
+
|
87
|
+
after(:each) { account.save }
|
88
|
+
|
89
|
+
it { account.should_receive(:lease!) }
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe :custom do
|
94
|
+
|
95
|
+
before(:each) do
|
96
|
+
account.stub(:_test_callback_before_lease)
|
97
|
+
account.stub(:_test_callback_after_lease)
|
98
|
+
Account.before_lease :_test_callback_before_lease
|
99
|
+
Account.after_lease :_test_callback_after_lease
|
100
|
+
end
|
101
|
+
|
102
|
+
after(:each) { account.lease! }
|
103
|
+
|
104
|
+
it { account.should_receive(:_test_callback_before_lease) }
|
105
|
+
it { account.should_receive(:_test_callback_after_lease) }
|
106
|
+
|
107
|
+
describe :aliases do
|
108
|
+
|
109
|
+
describe :on_lease do
|
110
|
+
|
111
|
+
before(:each) do
|
112
|
+
account.stub(:_test_callback_on_lease)
|
113
|
+
Account.on_lease :_test_callback_on_lease
|
114
|
+
end
|
115
|
+
|
116
|
+
it { account.should_receive(:_test_callback_on_lease) }
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
describe :break! do
|
127
|
+
|
128
|
+
describe :after_destroy do
|
129
|
+
|
130
|
+
after(:each) { account.destroy }
|
131
|
+
|
132
|
+
it { account.should_receive(:break!) }
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
describe :custom do
|
137
|
+
|
138
|
+
before(:each) do
|
139
|
+
account.stub(:_test_callback_before_break)
|
140
|
+
account.stub(:_test_callback_after_break)
|
141
|
+
Account.before_break :_test_callback_before_break
|
142
|
+
Account.after_break :_test_callback_after_break
|
143
|
+
end
|
144
|
+
|
145
|
+
after(:each) { account.break! }
|
146
|
+
|
147
|
+
it { account.should_receive(:_test_callback_before_break) }
|
148
|
+
it { account.should_receive(:_test_callback_after_break) }
|
149
|
+
|
150
|
+
describe :aliases do
|
151
|
+
|
152
|
+
describe :on_break do
|
153
|
+
|
154
|
+
before(:each) do
|
155
|
+
account.stub(:_test_callback_on_break)
|
156
|
+
Account.on_break :_test_callback_on_break
|
157
|
+
end
|
158
|
+
|
159
|
+
it { account.should_receive(:_test_callback_on_break) }
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Leases::Model do
|
4
|
+
|
5
|
+
with_model :Account do
|
6
|
+
table do |t|
|
7
|
+
t.string :name
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe :leases do
|
13
|
+
|
14
|
+
it { Account.respond_to?(:leases).should be_true }
|
15
|
+
|
16
|
+
it 'should set leases_options' do
|
17
|
+
Account.leases :name => :slug
|
18
|
+
Account.leases_options.should == { :name => :slug }
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should add the class to a list of leasers' do
|
22
|
+
Account.leases :name => :slug
|
23
|
+
Leases.leasers.should include 'Account'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe :shared_by_leasers do
|
29
|
+
|
30
|
+
with_model :User do
|
31
|
+
table do |t|
|
32
|
+
t.string :name
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
before(:each) { User.shared_by_leasers }
|
38
|
+
|
39
|
+
it { Apartment.excluded_models.should include 'User' }
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
data/spec/leases_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Leases do
|
4
|
+
|
5
|
+
with_model :Account do
|
6
|
+
table do |t|
|
7
|
+
t.string :name
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
model do
|
11
|
+
leases
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe :leasing do
|
16
|
+
|
17
|
+
before(:each) { Leases.leasing(Account) }
|
18
|
+
|
19
|
+
it { Leases.leasers.should == ['Account'] }
|
20
|
+
it { Apartment.excluded_models.should include 'Account' }
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
describe :leaser_names do
|
25
|
+
|
26
|
+
let!(:account_1) { Account.create }
|
27
|
+
let!(:account_2) { Account.create }
|
28
|
+
|
29
|
+
it { Leases.leaser_names.should == [account_1.leaser_name, account_2.leaser_name] }
|
30
|
+
it { Apartment.database_names.should == Leases.leaser_names }
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'action_controller'
|
3
|
+
|
4
|
+
require 'leases'
|
5
|
+
require 'with_model'
|
6
|
+
require 'rspec/rails'
|
7
|
+
|
8
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3',
|
9
|
+
:database => File.dirname(__FILE__) + '/leaser.sqlite3')
|
10
|
+
|
11
|
+
Dir['spec/support/**/*.rb'].each { |f| load f }
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
|
15
|
+
config.mock_with :rspec
|
16
|
+
config.extend WithModel
|
17
|
+
|
18
|
+
config.before(:each) do
|
19
|
+
# Stub Apartment database interactions
|
20
|
+
Apartment::Database.stub(:switch)
|
21
|
+
Apartment::Database.stub(:reset)
|
22
|
+
Apartment::Database.stub(:create)
|
23
|
+
Apartment::Database.stub(:drop)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module Dummy
|
29
|
+
class Application < Rails::Application
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: leases
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Arjen Oosterkamp
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-18 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.3'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec-rails
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: sqlite3
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: with_model
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rails
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: apartment
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0.20'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0.20'
|
142
|
+
description: Database multi-tenancy for Rails.
|
143
|
+
email:
|
144
|
+
- mail@arjen.me
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- .travis.yml
|
151
|
+
- Gemfile
|
152
|
+
- LICENSE.txt
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- leases.gemspec
|
156
|
+
- lib/leases.rb
|
157
|
+
- lib/leases/controller.rb
|
158
|
+
- lib/leases/model.rb
|
159
|
+
- lib/leases/model/base.rb
|
160
|
+
- lib/leases/model/callbacks.rb
|
161
|
+
- lib/leases/railtie.rb
|
162
|
+
- lib/leases/version.rb
|
163
|
+
- spec/leases/controller_spec.rb
|
164
|
+
- spec/leases/model/base_spec.rb
|
165
|
+
- spec/leases/model/callbacks_spec.rb
|
166
|
+
- spec/leases/model_spec.rb
|
167
|
+
- spec/leases_spec.rb
|
168
|
+
- spec/spec_helper.rb
|
169
|
+
homepage: https://github.com/Arjeno/leases
|
170
|
+
licenses: []
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ! '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
segments:
|
182
|
+
- 0
|
183
|
+
hash: -3059890359605888421
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
segments:
|
191
|
+
- 0
|
192
|
+
hash: -3059890359605888421
|
193
|
+
requirements: []
|
194
|
+
rubyforge_project:
|
195
|
+
rubygems_version: 1.8.24
|
196
|
+
signing_key:
|
197
|
+
specification_version: 3
|
198
|
+
summary: Database multi-tenancy for Rails.
|
199
|
+
test_files:
|
200
|
+
- spec/leases/controller_spec.rb
|
201
|
+
- spec/leases/model/base_spec.rb
|
202
|
+
- spec/leases/model/callbacks_spec.rb
|
203
|
+
- spec/leases/model_spec.rb
|
204
|
+
- spec/leases_spec.rb
|
205
|
+
- spec/spec_helper.rb
|