mongoid-multitenancy 0.2 → 0.3
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 +8 -8
- data/Gemfile.lock +1 -1
- data/README.md +67 -49
- data/lib/mongoid/multitenancy/document.rb +2 -2
- data/lib/mongoid/multitenancy/version.rb +1 -1
- data/spec/models/article.rb +1 -1
- data/spec/models/page.rb +1 -1
- data/spec/mongoid-multitenancy_spec.rb +31 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
Njk4YWRlMTZhYzljNjc2ZjBiNjQwYWY1YmQ4NjAzMWM2OTIzZGExYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NjcwYzEwZDNiYTlhODBiMWExNjY5MGVjOWI3ZTQ5MmQwM2Q2OWNiMQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NzFmNjU1ODg0MzE0YzE4ODE0YTViODVhMGEzZWMyOGViZjBkNTg5MDg4ZWU4
|
10
|
+
M2ViY2MxMzE0YmU1MDY5NTAxNGQxN2NiOTJkNjg2NzM2ODA0ODMyYjJjYWM4
|
11
|
+
NmMyY2NjMzljMGJlY2VkNzMyYzA3OTJmYzk2NTY1ODhjOTllZWM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
M2RiMmYxZGU5NWE5MGZjNzkxZTljNjU1ZjVjM2U1ZGM2Yzg3ZmRkYjhkYTBh
|
14
|
+
YzZkZTYyMjJjYjRmZWUwOTdmNGU2M2U2NTVmMjY5MTJjODE4Mzg1NmIzMDA4
|
15
|
+
YTQzZWZjMTAyYzAwYWExZWQ1NGNhODFlOGQ1NTExOTBiYzQzZmE=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,8 @@ In addition, mongoid-multitenancy:
|
|
8
8
|
|
9
9
|
* allows you to set the current tenant
|
10
10
|
* redefines some mongoid functions like `index`, `validates_with` and `delete_all` to take in account the multitenancy
|
11
|
-
*
|
11
|
+
* allows shared items between the tenants
|
12
|
+
* allows you to define an immutable tenant field once it is persisted
|
12
13
|
* is thread safe.
|
13
14
|
|
14
15
|
Installation
|
@@ -40,15 +41,19 @@ There are two ways to set the current tenant: (1) by setting the current tenant
|
|
40
41
|
|
41
42
|
**Setting the current tenant in a controller, manually**
|
42
43
|
|
43
|
-
|
44
|
+
```ruby
|
45
|
+
Mongoid::Multitenancy.current_tenant = client_instance
|
46
|
+
```
|
44
47
|
|
45
48
|
Setting the current_tenant yourself requires you to use a before_filter to set the Mongoid::Multitenancy.current_tenant variable.
|
46
49
|
|
47
50
|
**Setting the current tenant for a block**
|
48
51
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
```ruby
|
53
|
+
Mongoid::Multitenancy.with_tenant(client_instance) do
|
54
|
+
# Current tenant is set for all code in this block
|
55
|
+
end
|
56
|
+
```
|
52
57
|
|
53
58
|
This approach is useful when running background processes for a specified tenant. For example, by putting this in your worker's run method,
|
54
59
|
any code in this block will be scoped to the current tenant. All methods that set the current tenant are thread safe.
|
@@ -57,51 +62,58 @@ any code in this block will be scoped to the current tenant. All methods that se
|
|
57
62
|
|
58
63
|
Scoping your models
|
59
64
|
-------------------
|
60
|
-
|
61
|
-
|
65
|
+
```ruby
|
66
|
+
class Client
|
67
|
+
include Mongoid::Document
|
62
68
|
|
63
|
-
|
64
|
-
|
65
|
-
|
69
|
+
field :name, :type => String
|
70
|
+
validates_uniqueness_of :name
|
71
|
+
end
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
73
|
+
class Article
|
74
|
+
include Mongoid::Document
|
75
|
+
include Mongoid::Multitenancy::Document
|
70
76
|
|
71
|
-
|
77
|
+
tenant(:client)
|
72
78
|
|
73
|
-
|
74
|
-
|
79
|
+
field :title, :type => String
|
80
|
+
end
|
81
|
+
```
|
75
82
|
|
76
83
|
Adding `tenant` to your model declaration will scope that model to the current tenant **BUT ONLY if a current tenant has been set**.
|
77
84
|
The association passed to the `tenant` function must be valid.
|
78
85
|
|
79
86
|
`tenant` accepts several options:
|
80
87
|
|
81
|
-
* :optional : set to true when the tenant
|
88
|
+
* :optional : set to true when the tenant is optional (default value is `false`)
|
89
|
+
* :immutable : set to true when the tenant field is immutable (default value is `true`)
|
82
90
|
* :class_name, etc. : all the other options will be passed to the mongoid relation
|
83
91
|
|
84
92
|
Some examples to illustrate this behavior:
|
85
93
|
|
86
|
-
|
87
|
-
|
94
|
+
```ruby
|
95
|
+
# This manually sets the current tenant for testing purposes. In your app this is handled by the gem.
|
96
|
+
Mongoid::Multitenancy.current_tenant = Client.find_by(:name => 'Perfect Memory') # => <#Client _id:50ca04b86c82bfc125000025, :name: "Perfect Memory">
|
88
97
|
|
89
|
-
|
90
|
-
|
98
|
+
# All searches are scoped by the tenant, the following searches will only return objects belonging to the current client.
|
99
|
+
Article.all # => all articles where client_id => 50ca04b86c82bfc125000025
|
91
100
|
|
92
|
-
|
93
|
-
|
101
|
+
# New objects are scoped to the current tenant
|
102
|
+
Article.new(:title => 'New blog') # => <#Article _id: nil, title: 'New blog', :client_id: 50ca04b86c82bfc125000025>
|
94
103
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
104
|
+
# It can make the tenant field immutable once it is persisted to avoid inconsistency
|
105
|
+
article.persisted? # => true
|
106
|
+
article.client = another_client
|
107
|
+
article.valid? # => false
|
108
|
+
```
|
99
109
|
|
100
110
|
**Optional tenant**
|
101
111
|
|
102
112
|
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
113
|
|
104
|
-
|
114
|
+
```ruby
|
115
|
+
Article.all # => all articles where client_id.in [50ca04b86c82bfc125000025, nil]
|
116
|
+
```
|
105
117
|
|
106
118
|
Rails
|
107
119
|
-------------------
|
@@ -110,14 +122,16 @@ If you are using Rails, you may want to set the current tenant at each request.
|
|
110
122
|
|
111
123
|
**Manually set the current tenant in ApplicationController using the host request**
|
112
124
|
|
113
|
-
|
114
|
-
|
125
|
+
```ruby
|
126
|
+
class ApplicationController < ActionController::Base
|
127
|
+
before_filter :set_current_client
|
115
128
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
129
|
+
def set_current_client
|
130
|
+
current_client = Client.find_by_host(request.host)
|
131
|
+
Mongoid::Multitenancy.current_tenant = current_client
|
132
|
+
end
|
133
|
+
end
|
134
|
+
```
|
121
135
|
|
122
136
|
Setting the current_tenant yourself requires you to use a before_filter to set the Mongoid::Multitenancy.current_tenant variable.
|
123
137
|
|
@@ -127,32 +141,36 @@ Mongoid Uniqueness validators
|
|
127
141
|
mongoid-multitenancy will automatically add the tenant foreign key in the scope list for each of uniqueness validators in order
|
128
142
|
to avoid to redefine all your validators.
|
129
143
|
|
130
|
-
|
131
|
-
|
132
|
-
|
144
|
+
```ruby
|
145
|
+
class Article
|
146
|
+
include Mongoid::Document
|
147
|
+
include Mongoid::Multitenancy::Document
|
133
148
|
|
134
|
-
|
149
|
+
tenant(:client)
|
135
150
|
|
136
|
-
|
151
|
+
field :slug
|
137
152
|
|
138
|
-
|
139
|
-
|
153
|
+
validates_uniqueness_of :slug # => :scope => client_id is added automatically
|
154
|
+
end
|
155
|
+
```
|
140
156
|
|
141
157
|
Mongoid indexes
|
142
158
|
-------------------
|
143
159
|
|
144
160
|
mongoid-multitenancy will automatically add the tenant foreign key in all your mongoid indexes to avoid to redefine all your validators.
|
145
161
|
|
146
|
-
|
147
|
-
|
148
|
-
|
162
|
+
```ruby
|
163
|
+
class Article
|
164
|
+
include Mongoid::Document
|
165
|
+
include Mongoid::Multitenancy::Document
|
149
166
|
|
150
|
-
|
167
|
+
tenant(:client)
|
151
168
|
|
152
|
-
|
169
|
+
field :title
|
153
170
|
|
154
|
-
|
155
|
-
|
171
|
+
index({ :title => 1 }) # => create an index with { :client_id => 1, :title => 1 }
|
172
|
+
end
|
173
|
+
```
|
156
174
|
|
157
175
|
Author & Credits
|
158
176
|
----------------
|
@@ -21,7 +21,7 @@ module Mongoid
|
|
21
21
|
attr_reader :tenant_field, :tenant_options
|
22
22
|
|
23
23
|
def tenant(association = :account, options={})
|
24
|
-
@tenant_options = { optional: options.delete(:optional) }
|
24
|
+
@tenant_options = { optional: options.delete(:optional), immutable: options.delete(:immutable) { true } }
|
25
25
|
# Setup the association between the class and the tenant class
|
26
26
|
# TODO: should index this association if no other indexes are defined => , index: true
|
27
27
|
belongs_to association, options
|
@@ -42,7 +42,7 @@ module Mongoid
|
|
42
42
|
}
|
43
43
|
|
44
44
|
# Rewrite accessors to make tenant foreign_key/association immutable
|
45
|
-
validate :check_tenant_immutability, :on => :update
|
45
|
+
validate :check_tenant_immutability, :on => :update if @tenant_options[:immutable]
|
46
46
|
|
47
47
|
# Set the default_scope to scope to current tenant
|
48
48
|
default_scope lambda {
|
data/spec/models/article.rb
CHANGED
data/spec/models/page.rb
CHANGED
@@ -2,7 +2,7 @@ class Page
|
|
2
2
|
include Mongoid::Document
|
3
3
|
include Mongoid::Multitenancy::Document
|
4
4
|
|
5
|
-
tenant(:client, :class_name => 'Account', :optional => true)
|
5
|
+
tenant(:client, :class_name => 'Account', :optional => true )
|
6
6
|
|
7
7
|
field :slug, :type => String
|
8
8
|
field :title, :type => String
|
@@ -136,20 +136,41 @@ describe Article do
|
|
136
136
|
before { Mongoid::Multitenancy.current_tenant = client }
|
137
137
|
after { Mongoid::Multitenancy.current_tenant = nil }
|
138
138
|
|
139
|
-
|
139
|
+
context "with :immutable" do
|
140
|
+
let(:item) { Page.create!(:title => "title X", :slug => "article-x") }
|
141
|
+
|
142
|
+
context "when the tenant has not changed" do
|
143
|
+
it 'should be valid' do
|
144
|
+
item.title = "title X (2)"
|
145
|
+
item.should be_valid
|
146
|
+
end
|
147
|
+
end
|
140
148
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
149
|
+
context "when the tenant has changed" do
|
150
|
+
it 'should be invalid' do
|
151
|
+
item.title = "title X (2)"
|
152
|
+
item.client = another_client
|
153
|
+
item.should_not be_valid
|
154
|
+
end
|
145
155
|
end
|
146
156
|
end
|
147
157
|
|
148
|
-
context "
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
158
|
+
context "without :immutable" do
|
159
|
+
let(:item) { Article.create!(:title => "title X", :slug => "article-x") }
|
160
|
+
|
161
|
+
context "when the tenant has not changed" do
|
162
|
+
it 'should be valid' do
|
163
|
+
item.title = "title X (2)"
|
164
|
+
item.should be_valid
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "when the tenant has changed" do
|
169
|
+
it 'should be valid' do
|
170
|
+
item.title = "title X (2)"
|
171
|
+
item.client = another_client
|
172
|
+
item.should be_valid
|
173
|
+
end
|
153
174
|
end
|
154
175
|
end
|
155
176
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-multitenancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aymeric Brisse
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongoid
|