mongoid-multitenancy 0.1.1 → 0.2

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjY1NTgwNWVlNWJhZWQ5NGI2MjVkZmZlMTY0ZWIyMjUyZDg3ZTdlNw==
5
+ data.tar.gz: !binary |-
6
+ YjFhODhkYjZiNTUwZDczYjA3NGZhMTViNDhkY2I3MWU1MTE1ODJhZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ M2U5YjM5MWE0YWZjMWJiOTc0YzE3YmQ4ZDljNzgwYjc5YzE0YTRhMzczZmUy
10
+ YzBjMDJmYTNjY2FjMjBiNDljYTRlNDRiYTg1YTM0NjUzOGVmNjJmMjkyYzdh
11
+ N2NhMDcwMjI4MDc2MjJkOGJhMTEwNTg4ZGMzYTNkMjE2ZmQ4N2Q=
12
+ data.tar.gz: !binary |-
13
+ ODMwMDVjOTc4YTFiN2E2ZWQ5ODhlNmM1NTgyYWQ1MmEyMjI0ZTQwZmRlNjhj
14
+ NTMyY2U2YzE1N2U1MDNhM2I2MjdhMjhmOWRlZGJjM2U1ZWQyOGE0MTM4NmY3
15
+ NWY2MTdkMjY0M2EyZmY1YmFjZmYzMTk4MDA3MzlkNmEyM2FjYzQ=
data/Gemfile.lock CHANGED
@@ -1,52 +1,52 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mongoid-multitenancy (0.1)
4
+ mongoid-multitenancy (0.2.0)
5
5
  mongoid (~> 3)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activemodel (3.2.9)
11
- activesupport (= 3.2.9)
10
+ activemodel (3.2.13)
11
+ activesupport (= 3.2.13)
12
12
  builder (~> 3.0.0)
13
- activesupport (3.2.9)
14
- i18n (~> 0.6)
13
+ activesupport (3.2.13)
14
+ i18n (= 0.6.1)
15
15
  multi_json (~> 1.0)
16
16
  builder (3.0.4)
17
- database_cleaner (0.9.1)
18
- diff-lcs (1.1.3)
17
+ database_cleaner (1.0.1)
18
+ diff-lcs (1.2.4)
19
19
  i18n (0.6.1)
20
- mongoid (3.0.14)
21
- activemodel (~> 3.1)
22
- moped (~> 1.1)
20
+ mongoid (3.1.3)
21
+ activemodel (~> 3.2)
22
+ moped (~> 1.4.2)
23
23
  origin (~> 1.0)
24
24
  tzinfo (~> 0.3.22)
25
- mongoid-rspec (1.5.5)
25
+ mongoid-rspec (1.8.1)
26
26
  mongoid (>= 3.0.1)
27
27
  rake
28
28
  rspec (>= 2.9)
29
- moped (1.3.1)
30
- multi_json (1.5.0)
31
- origin (1.0.11)
32
- rake (10.0.3)
29
+ moped (1.4.5)
30
+ multi_json (1.7.3)
31
+ origin (1.1.0)
32
+ rake (10.0.4)
33
33
  redcarpet (2.2.2)
34
- rspec (2.12.0)
35
- rspec-core (~> 2.12.0)
36
- rspec-expectations (~> 2.12.0)
37
- rspec-mocks (~> 2.12.0)
38
- rspec-core (2.12.1)
39
- rspec-expectations (2.12.0)
40
- diff-lcs (~> 1.1.3)
41
- rspec-mocks (2.12.0)
42
- tzinfo (0.3.35)
43
- yard (0.8.3)
34
+ rspec (2.13.0)
35
+ rspec-core (~> 2.13.0)
36
+ rspec-expectations (~> 2.13.0)
37
+ rspec-mocks (~> 2.13.0)
38
+ rspec-core (2.13.1)
39
+ rspec-expectations (2.13.0)
40
+ diff-lcs (>= 1.1.3, < 2.0)
41
+ rspec-mocks (2.13.1)
42
+ tzinfo (0.3.37)
43
+ yard (0.8.6.1)
44
44
 
45
45
  PLATFORMS
46
46
  ruby
47
47
 
48
48
  DEPENDENCIES
49
- database_cleaner (~> 0.9)
49
+ database_cleaner (~> 1.0)
50
50
  mongoid-multitenancy!
51
51
  mongoid-rspec (~> 1.5)
52
52
  rake (~> 10.0)
data/README.md CHANGED
@@ -76,6 +76,11 @@ Scoping your models
76
76
  Adding `tenant` to your model declaration will scope that model to the current tenant **BUT ONLY if a current tenant has been set**.
77
77
  The association passed to the `tenant` function must be valid.
78
78
 
79
+ `tenant` accepts several options:
80
+
81
+ * :optional : set to true when the tenant can be optional
82
+ * :class_name, etc. : all the other options will be passed to the mongoid relation
83
+
79
84
  Some examples to illustrate this behavior:
80
85
 
81
86
  # This manually sets the current tenant for testing purposes. In your app this is handled by the gem.
@@ -92,6 +97,12 @@ Some examples to illustrate this behavior:
92
97
  article.client = another_client
93
98
  article.valid? # => false
94
99
 
100
+ **Optional tenant**
101
+
102
+ When setting an optional tenant, for example to allow shared instances between all the tenants, the default scope will return both the tenant and the free-tenant items. That means that using `Article.delete_all` or `Article.destroy_all` will remove the shared items too.
103
+
104
+ Article.all # => all articles where client_id.in [50ca04b86c82bfc125000025, nil]
105
+
95
106
  Rails
96
107
  -------------------
97
108
 
@@ -18,29 +18,44 @@ module Mongoid
18
18
 
19
19
  module ClassMethods
20
20
  # Access to the tenant field
21
- attr_reader :tenant_field
21
+ attr_reader :tenant_field, :tenant_options
22
22
 
23
- def tenant(association = :account)
23
+ def tenant(association = :account, options={})
24
+ @tenant_options = { optional: options.delete(:optional) }
24
25
  # Setup the association between the class and the tenant class
25
26
  # TODO: should index this association if no other indexes are defined => , index: true
26
- belongs_to association
27
+ belongs_to association, options
27
28
 
28
29
  # Get the tenant model and its foreign key
29
30
  fkey = reflect_on_association(association).foreign_key
30
31
  @tenant_field = fkey
31
32
 
32
33
  # Validates the presence of the association key
33
- validates_presence_of fkey
34
+ validates_presence_of fkey unless @tenant_options[:optional]
34
35
 
35
36
  # Set the current_tenant on newly created objects
36
- after_initialize lambda { |m| m.send "#{association}=".to_sym, Multitenancy.current_tenant if Multitenancy.current_tenant ; true }
37
+ after_initialize lambda { |m|
38
+ if Multitenancy.current_tenant #and !self.class.tenant_options[:optional]
39
+ m.send "#{association}=".to_sym, Multitenancy.current_tenant
40
+ end
41
+ true
42
+ }
37
43
 
38
44
  # Rewrite accessors to make tenant foreign_key/association immutable
39
45
  validate :check_tenant_immutability, :on => :update
40
46
 
41
47
  # Set the default_scope to scope to current tenant
42
48
  default_scope lambda {
43
- where(Multitenancy.current_tenant ? { self.tenant_field => Multitenancy.current_tenant.id } : nil)
49
+ criteria = if Multitenancy.current_tenant
50
+ if self.tenant_options[:optional]
51
+ #any_of({ self.tenant_field => Multitenancy.current_tenant.id }, { self.tenant_field => nil })
52
+ where({ self.tenant_field.to_sym.in => [Multitenancy.current_tenant.id, nil] })
53
+ else
54
+ where({ self.tenant_field => Multitenancy.current_tenant.id })
55
+ end
56
+ else
57
+ where(nil)
58
+ end
44
59
  }
45
60
  end
46
61
 
@@ -60,7 +75,7 @@ module Mongoid
60
75
 
61
76
  # Redefine 'delete_all' to take in account the default scope
62
77
  def delete_all(conditions = nil)
63
- self.where(conditions).delete
78
+ scoped.where(conditions).delete
64
79
  end
65
80
  end
66
81
  end
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Multitenancy
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2"
4
4
  end
5
5
  end
@@ -21,6 +21,6 @@ Gem::Specification.new do |gem|
21
21
  gem.add_development_dependency('rspec', '~> 2.12')
22
22
  gem.add_development_dependency('yard', '~> 0.8')
23
23
  gem.add_development_dependency('mongoid-rspec', '~> 1.5')
24
- gem.add_development_dependency('database_cleaner', '~> 0.9')
24
+ gem.add_development_dependency('database_cleaner', '~> 1.0')
25
25
  gem.add_development_dependency('redcarpet', '~> 2.2')
26
26
  end
@@ -1,4 +1,4 @@
1
- class Client
1
+ class Account
2
2
  include Mongoid::Document
3
3
 
4
4
  field :name, :type => String
@@ -2,7 +2,7 @@ class Article
2
2
  include Mongoid::Document
3
3
  include Mongoid::Multitenancy::Document
4
4
 
5
- tenant(:client)
5
+ tenant(:client, :class_name => 'Account')
6
6
 
7
7
  field :slug, :type => String
8
8
  field :title, :type => String
@@ -0,0 +1,15 @@
1
+ class Page
2
+ include Mongoid::Document
3
+ include Mongoid::Multitenancy::Document
4
+
5
+ tenant(:client, :class_name => 'Account', :optional => true)
6
+
7
+ field :slug, :type => String
8
+ field :title, :type => String
9
+
10
+ validates_uniqueness_of :slug
11
+ validates_presence_of :slug
12
+ validates_presence_of :title
13
+
14
+ index({ :title => 1 })
15
+ end
@@ -1,8 +1,8 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Mongoid::Multitenancy do
4
- let(:client) { Client.create!(:name => "client") }
5
- let(:another_client) { Client.create!(:name => "another client") }
4
+ let(:client) { Account.create!(:name => "client") }
5
+ let(:another_client) { Account.create!(:name => "another client") }
6
6
 
7
7
  before { Mongoid::Multitenancy.current_tenant = client }
8
8
  after { Mongoid::Multitenancy.current_tenant = nil }
@@ -21,28 +21,97 @@ describe Mongoid::Multitenancy do
21
21
  end
22
22
  end
23
23
 
24
- describe Article do
24
+ shared_examples_for "a tenantable model" do
25
+
25
26
  it { should belong_to(:client) }
26
- it { should validate_presence_of(:client_id) }
27
27
  it { should validate_uniqueness_of(:slug).scoped_to(:client_id) }
28
28
  it { should have_index_for(:client_id => 1, :title => 1) }
29
29
 
30
- let(:client) { Client.create!(:name => "client") }
31
- let(:another_client) { Client.create!(:name => "another client") }
30
+ let(:client) { Account.create!(:name => "client") }
31
+ let(:another_client) { Account.create!(:name => "another client") }
32
32
 
33
33
  describe ".initialize" do
34
34
  before { Mongoid::Multitenancy.current_tenant = client }
35
35
  after { Mongoid::Multitenancy.current_tenant = nil }
36
36
 
37
37
  it "should set the client field" do
38
- Article.new.client.should eq client
38
+ described_class.new.client.should eq client
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ describe Page do
45
+
46
+ it_behaves_like "a tenantable model"
47
+
48
+ it { should_not validate_presence_of(:client_id) }
49
+
50
+ let(:client) { Account.create!(:name => "client") }
51
+ let(:another_client) { Account.create!(:name => "another client") }
52
+
53
+ describe ".default_scope" do
54
+ before {
55
+ @itemC = described_class.create!(:title => "title C", :slug => "article-c")
56
+ Mongoid::Multitenancy.with_tenant(client) { @itemX = described_class.create!(:title => "title X", :slug => "article-x") }
57
+ Mongoid::Multitenancy.with_tenant(another_client) { @itemY = described_class.create!(:title => "title Y", :slug => "article-y") }
58
+ }
59
+
60
+ context "with a current tenant" do
61
+ before { Mongoid::Multitenancy.current_tenant = another_client }
62
+ after { Mongoid::Multitenancy.current_tenant = nil }
63
+
64
+ it "should filter on the current tenant / free-tenant items" do
65
+ described_class.all.to_a.should =~ [@itemY, @itemC]
66
+ end
67
+ end
68
+
69
+ context "without a current tenant" do
70
+ before { Mongoid::Multitenancy.current_tenant = nil }
71
+
72
+ it "should not filter on any tenant" do
73
+ described_class.all.to_a.should =~ [@itemC, @itemX, @itemY]
74
+ end
39
75
  end
40
76
  end
41
77
 
78
+ describe "#delete_all" do
79
+ before {
80
+ @itemC = described_class.create!(:title => "title C", :slug => "article-c")
81
+ Mongoid::Multitenancy.with_tenant(client) { @itemX = described_class.create!(:title => "title X", :slug => "article-x") }
82
+ Mongoid::Multitenancy.with_tenant(another_client) { @itemY = described_class.create!(:title => "title Y", :slug => "article-y") }
83
+ }
84
+
85
+ context "with a current tenant" do
86
+ it "should only delete the current tenant / free-tenant items" do
87
+ Mongoid::Multitenancy.with_tenant(another_client) { described_class.delete_all }
88
+ described_class.all.to_a.should =~ [@itemX]
89
+ end
90
+ end
91
+
92
+ context "without a current tenant" do
93
+ it "should delete all the pages" do
94
+ described_class.delete_all
95
+ described_class.all.to_a.should be_empty
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+
102
+ describe Article do
103
+
104
+ it_behaves_like "a tenantable model"
105
+
106
+ it { should validate_presence_of(:client_id) }
107
+
108
+ let(:client) { Account.create!(:name => "client") }
109
+ let(:another_client) { Account.create!(:name => "another client") }
110
+
42
111
  describe ".default_scope" do
43
112
  before {
44
- Mongoid::Multitenancy.with_tenant(client) { @articleX = Article.create!(:title => "title X", :slug => "article-x") }
45
- Mongoid::Multitenancy.with_tenant(another_client) { @articleY = Article.create!(:title => "title Y", :slug => "article-y") }
113
+ Mongoid::Multitenancy.with_tenant(client) { @itemX = described_class.create!(:title => "title X", :slug => "article-x") }
114
+ Mongoid::Multitenancy.with_tenant(another_client) { @itemY = described_class.create!(:title => "title Y", :slug => "article-y") }
46
115
  }
47
116
 
48
117
  context "with a current tenant" do
@@ -50,7 +119,7 @@ describe Article do
50
119
  after { Mongoid::Multitenancy.current_tenant = nil }
51
120
 
52
121
  it "should filter on the current tenant" do
53
- Article.all.to_a.should == [@articleY]
122
+ described_class.all.to_a.should =~ [@itemY]
54
123
  end
55
124
  end
56
125
 
@@ -58,7 +127,7 @@ describe Article do
58
127
  before { Mongoid::Multitenancy.current_tenant = nil }
59
128
 
60
129
  it "should not filter on any tenant" do
61
- Article.all.to_a.should == [@articleX, @articleY]
130
+ described_class.all.to_a.should =~ [@itemX, @itemY]
62
131
  end
63
132
  end
64
133
  end
@@ -88,22 +157,23 @@ describe Article do
88
157
 
89
158
  describe "#delete_all" do
90
159
  before {
91
- Mongoid::Multitenancy.with_tenant(client) { @articleX = Article.create!(:title => "title X", :slug => "article-x") }
92
- Mongoid::Multitenancy.with_tenant(another_client) { @articleY = Article.create!(:title => "title Y", :slug => "article-y") }
160
+ Mongoid::Multitenancy.with_tenant(client) { @itemX = described_class.create!(:title => "title X", :slug => "article-x") }
161
+ Mongoid::Multitenancy.with_tenant(another_client) { @itemY = described_class.create!(:title => "title Y", :slug => "article-y") }
93
162
  }
94
163
 
95
164
  context "with a current tenant" do
96
165
  it "should only delete the current tenant articles" do
97
- Mongoid::Multitenancy.with_tenant(another_client) { Article.delete_all }
98
- Article.all.to_a == [@articleX]
166
+ Mongoid::Multitenancy.with_tenant(another_client) { described_class.delete_all }
167
+ described_class.all.to_a.should =~ [@itemX]
99
168
  end
100
169
  end
101
170
 
102
171
  context "without a current tenant" do
103
172
  it "should delete all the articles" do
104
- Article.delete_all
105
- Article.all.to_a.should be_empty
173
+ described_class.delete_all
174
+ described_class.all.to_a.should be_empty
106
175
  end
107
176
  end
108
177
  end
178
+
109
179
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-multitenancy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: '0.2'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Aymeric Brisse
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-12-14 00:00:00.000000000 Z
11
+ date: 2013-05-16 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: mongoid
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: yard
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ~>
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ~>
76
67
  - !ruby/object:Gem::Version
@@ -78,7 +69,6 @@ dependencies:
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: mongoid-rspec
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
73
  - - ~>
84
74
  - !ruby/object:Gem::Version
@@ -86,7 +76,6 @@ dependencies:
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
80
  - - ~>
92
81
  - !ruby/object:Gem::Version
@@ -94,23 +83,20 @@ dependencies:
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: database_cleaner
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
87
  - - ~>
100
88
  - !ruby/object:Gem::Version
101
- version: '0.9'
89
+ version: '1.0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
94
  - - ~>
108
95
  - !ruby/object:Gem::Version
109
- version: '0.9'
96
+ version: '1.0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: redcarpet
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
101
  - - ~>
116
102
  - !ruby/object:Gem::Version
@@ -118,7 +104,6 @@ dependencies:
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
108
  - - ~>
124
109
  - !ruby/object:Gem::Version
@@ -142,37 +127,38 @@ files:
142
127
  - lib/mongoid/multitenancy/document.rb
143
128
  - lib/mongoid/multitenancy/version.rb
144
129
  - mongoid-multitenancy.gemspec
130
+ - spec/models/account.rb
145
131
  - spec/models/article.rb
146
- - spec/models/client.rb
132
+ - spec/models/page.rb
147
133
  - spec/mongoid-multitenancy_spec.rb
148
134
  - spec/spec_helper.rb
149
135
  homepage: https://github.com/PerfectMemory/mongoid-multitenancy
150
136
  licenses: []
137
+ metadata: {}
151
138
  post_install_message:
152
139
  rdoc_options: []
153
140
  require_paths:
154
141
  - lib
155
142
  required_ruby_version: !ruby/object:Gem::Requirement
156
- none: false
157
143
  requirements:
158
144
  - - ! '>='
159
145
  - !ruby/object:Gem::Version
160
146
  version: '0'
161
147
  required_rubygems_version: !ruby/object:Gem::Requirement
162
- none: false
163
148
  requirements:
164
149
  - - ! '>='
165
150
  - !ruby/object:Gem::Version
166
151
  version: '0'
167
152
  requirements: []
168
153
  rubyforge_project:
169
- rubygems_version: 1.8.24
154
+ rubygems_version: 2.0.1
170
155
  signing_key:
171
- specification_version: 3
156
+ specification_version: 4
172
157
  summary: Support of a multi-tenant database with Mongoid
173
158
  test_files:
159
+ - spec/models/account.rb
174
160
  - spec/models/article.rb
175
- - spec/models/client.rb
161
+ - spec/models/page.rb
176
162
  - spec/mongoid-multitenancy_spec.rb
177
163
  - spec/spec_helper.rb
178
164
  has_rdoc: