mongoid-multitenancy 1.1.3 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -6
- data/CHANGELOG.md +34 -1
- data/Gemfile +4 -6
- data/README.md +51 -26
- data/gemfiles/Gemfile.mongoid-6 +14 -0
- data/gemfiles/Gemfile.mongoid-7 +14 -0
- data/lib/mongoid/multitenancy.rb +7 -6
- data/lib/mongoid/multitenancy/document.rb +11 -7
- data/lib/mongoid/multitenancy/validators/tenancy.rb +4 -4
- data/lib/mongoid/multitenancy/validators/tenant_uniqueness.rb +4 -10
- data/lib/mongoid/multitenancy/version.rb +1 -1
- data/mongoid-multitenancy.gemspec +3 -3
- data/spec/immutable_spec.rb +1 -1
- data/spec/indexable_spec.rb +4 -6
- data/spec/mandatory_spec.rb +12 -1
- data/spec/models/account.rb +1 -1
- data/spec/models/immutable.rb +4 -4
- data/spec/models/indexable.rb +2 -2
- data/spec/models/mandatory.rb +3 -3
- data/spec/models/mutable.rb +4 -4
- data/spec/models/mutable_child.rb +3 -3
- data/spec/models/no_scopable.rb +11 -0
- data/spec/models/optional.rb +2 -2
- data/spec/models/optional_exclude.rb +15 -0
- data/spec/mongoid-multitenancy_spec.rb +10 -1
- data/spec/mutable_child_spec.rb +1 -2
- data/spec/mutable_spec.rb +1 -1
- data/spec/optional_exclude_spec.rb +71 -0
- data/spec/optional_spec.rb +2 -3
- data/spec/scopable_spec.rb +29 -0
- data/spec/spec_helper.rb +2 -7
- data/spec/support/shared_examples.rb +8 -9
- metadata +21 -8
- data/gemfiles/Gemfile.mongoid-4.0 +0 -16
- data/gemfiles/Gemfile.mongoid-5.0 +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57b6a36dd90e26ab59298efdd32bfeb5b92a1bfd
|
4
|
+
data.tar.gz: da726e93d69c677784b0bb5213d21bda3a77af3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b11cda4238d5ef849b59ce910c70136692e04680c764159c128cf38eea5fd89851e8d9d728a3773edbf27ed0013d9fbc2e2777babe2b977c4bcab160198ecca2
|
7
|
+
data.tar.gz: bf624d27f91d4ac108f811445eaa9fbbb4837e88427cc4a5eb7d810c9adbb19f6310f7a94ac84592560f371ad9ed666999498a7395cc562f495d845be724880e
|
data/.travis.yml
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.1.0
|
5
|
-
- 2.2.0
|
3
|
+
- 2.2.2
|
6
4
|
- 2.3.0
|
7
|
-
-
|
5
|
+
- 2.4.1
|
8
6
|
gemfile:
|
9
|
-
- gemfiles/Gemfile.mongoid-
|
10
|
-
- gemfiles/Gemfile.mongoid-
|
7
|
+
- gemfiles/Gemfile.mongoid-6
|
8
|
+
- gemfiles/Gemfile.mongoid-7
|
11
9
|
- Gemfile
|
12
10
|
services:
|
13
11
|
- mongodb
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [2.0.3] - 2020-06-25
|
8
|
+
### Fixed
|
9
|
+
|
10
|
+
* Full Support of mongoid 7
|
11
|
+
|
12
|
+
## [2.0.2] - 2018-08-23
|
13
|
+
### Added
|
14
|
+
|
15
|
+
* Support of mongoid 7
|
16
|
+
|
17
|
+
## [2.0.1] - 2017-12-14
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
* Add ensure block in method with_tenant
|
21
|
+
|
22
|
+
## [2.0] - 2017-07-21
|
23
|
+
### New Features
|
24
|
+
|
25
|
+
* Add support for mongoid 6
|
26
|
+
* Remove support for mongoid 4 & 5
|
27
|
+
|
28
|
+
## 1.2
|
29
|
+
|
30
|
+
### New Features
|
31
|
+
|
32
|
+
* Add *exclude_shared* option for the TenantUniquenessValidator
|
33
|
+
|
1
34
|
## 1.1
|
2
35
|
|
3
36
|
### New Features
|
@@ -12,7 +45,7 @@
|
|
12
45
|
|
13
46
|
### New Features
|
14
47
|
|
15
|
-
*
|
48
|
+
* Add support for mongoid 5
|
16
49
|
|
17
50
|
### Major Changes (Backwards Incompatible)
|
18
51
|
|
data/Gemfile
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem '
|
4
|
-
|
5
|
-
gem 'rake', '~> 10.0'
|
3
|
+
gem 'rake'
|
6
4
|
|
7
5
|
group :test do
|
8
|
-
gem 'database_cleaner'
|
6
|
+
gem 'database_cleaner'
|
9
7
|
gem 'coveralls', require: false
|
10
8
|
gem 'rspec', '~> 3.1'
|
11
|
-
gem 'yard'
|
12
|
-
gem 'mongoid-rspec', '
|
9
|
+
gem 'yard'
|
10
|
+
gem 'mongoid-rspec', git: 'https://github.com/mongoid-rspec/mongoid-rspec.git'
|
13
11
|
gem 'rubocop', require: false
|
14
12
|
end
|
15
13
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# mongoid-multitenancy [![Build Status](https://travis-ci.org/PerfectMemory/mongoid-multitenancy.png?branch=master)](https://travis-ci.org/PerfectMemory/mongoid-multitenancy) [![Coverage Status](https://coveralls.io/repos/PerfectMemory/mongoid-multitenancy/badge.svg?branch=master
|
1
|
+
# mongoid-multitenancy [![Build Status](https://api.travis-ci.org/PerfectMemory/mongoid-multitenancy.png?branch=master)](https://travis-ci.org/PerfectMemory/mongoid-multitenancy) [![Coverage Status](https://coveralls.io/repos/github/PerfectMemory/mongoid-multitenancy/badge.svg?branch=master)](https://coveralls.io/github/PerfectMemory/mongoid-multitenancy?branch=master) [![Code Climate](https://codeclimate.com/github/PerfectMemory/mongoid-multitenancy.png)](https://codeclimate.com/github/PerfectMemory/mongoid-multitenancy)
|
2
2
|
|
3
3
|
mongoid-multitenancy adds the ability to scope [Mongoid](https://github.com/mongoid/mongoid) models to a tenant in a **shared database strategy**. Tenants are represented by a tenant model, such as `Client`. mongoid-multitenancy will help you set the current tenant on each request and ensures that all 'tenant models' are always properly scoped to the current tenant: when viewing, searching and creating.
|
4
4
|
|
@@ -12,12 +12,17 @@ In addition, mongoid-multitenancy:
|
|
12
12
|
* is thread safe
|
13
13
|
* redefines some mongoid functions like `index`, `validates_with` and `delete_all` to take in account the multitenancy.
|
14
14
|
|
15
|
+
Compatibility
|
16
|
+
===============
|
17
|
+
|
18
|
+
mongoid-multitenancy 2.0 is compatible with mongoid 6/7. For mongoid 4/5 compatiblity, use mongoid-multitenancy 1.2.
|
19
|
+
|
15
20
|
Installation
|
16
21
|
===============
|
17
22
|
|
18
23
|
Add this line to your application's Gemfile:
|
19
24
|
|
20
|
-
gem 'mongoid-multitenancy'
|
25
|
+
gem 'mongoid-multitenancy', '~> 2.0'
|
21
26
|
|
22
27
|
And then execute:
|
23
28
|
|
@@ -74,7 +79,7 @@ class Article
|
|
74
79
|
include Mongoid::Document
|
75
80
|
include Mongoid::Multitenancy::Document
|
76
81
|
|
77
|
-
tenant(:
|
82
|
+
tenant(:tenant)
|
78
83
|
|
79
84
|
field :title, :type => String
|
80
85
|
end
|
@@ -85,10 +90,14 @@ The association passed to the `tenant` function must be valid.
|
|
85
90
|
|
86
91
|
`tenant` accepts several options:
|
87
92
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
93
|
+
| Option | Default | Description |
|
94
|
+
| ------------- | ------------- | ------------- |
|
95
|
+
| :optional | false | set to true when the tenant is optional |
|
96
|
+
| :immutable | true | set to true when the tenant field is immutable |
|
97
|
+
| :full_indexes | true | set to true to add the tenant field automatically to all the indexes |
|
98
|
+
| :index | false | set to true to define an index for the tenant field |
|
99
|
+
| :scopes | true | set to true to define scopes :shared and :unshared |
|
100
|
+
| :class_name, etc. | | all the other options will be passed to the mongoid relation (belongs_to) |
|
92
101
|
|
93
102
|
Some examples to illustrate this behavior:
|
94
103
|
|
@@ -97,15 +106,15 @@ Some examples to illustrate this behavior:
|
|
97
106
|
Mongoid::Multitenancy.current_tenant = Client.find_by(:name => 'Perfect Memory') # => <#Client _id:50ca04b86c82bfc125000025, :name: "Perfect Memory">
|
98
107
|
|
99
108
|
# All searches are scoped by the tenant, the following searches will only return objects belonging to the current client.
|
100
|
-
Article.all # => all articles where
|
109
|
+
Article.all # => all articles where tenant_id => 50ca04b86c82bfc125000025
|
101
110
|
|
102
111
|
# New objects are scoped to the current tenant
|
103
112
|
article = Article.new(:title => 'New blog')
|
104
|
-
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog',
|
113
|
+
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025>
|
105
114
|
|
106
115
|
# It can make the tenant field immutable once it is persisted to avoid inconsistency
|
107
116
|
article.persisted? # => true
|
108
|
-
article.
|
117
|
+
article.tenant = another_client
|
109
118
|
article.valid? # => false
|
110
119
|
```
|
111
120
|
|
@@ -120,21 +129,21 @@ class Article
|
|
120
129
|
include Mongoid::Document
|
121
130
|
include Mongoid::Multitenancy::Document
|
122
131
|
|
123
|
-
tenant(:
|
132
|
+
tenant(:tenant, optional: true)
|
124
133
|
|
125
134
|
field :title, :type => String
|
126
135
|
end
|
127
136
|
|
128
137
|
Mongoid::Multitenancy.with_tenant(client_instance) do
|
129
|
-
Article.all # => all articles where
|
138
|
+
Article.all # => all articles where tenant_id.in [50ca04b86c82bfc125000025, nil]
|
130
139
|
article = Article.new(:title => 'New article')
|
131
|
-
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog',
|
140
|
+
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025>
|
132
141
|
|
133
142
|
# tenant needs to be set manually to nil
|
134
|
-
article = Article.new(:title => 'New article', :
|
135
|
-
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog',
|
143
|
+
article = Article.new(:title => 'New article', :tenant => nil)
|
144
|
+
article.save # => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: 50ca04b86c82bfc125000025>
|
136
145
|
article.tenant = nil
|
137
|
-
article.save => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog',
|
146
|
+
article.save => <#Article _id: 50ca04b86c82bfc125000044, title: 'New blog', tenant_id: nil>
|
138
147
|
end
|
139
148
|
```
|
140
149
|
|
@@ -173,7 +182,7 @@ class Article
|
|
173
182
|
include Mongoid::Document
|
174
183
|
include Mongoid::Multitenancy::Document
|
175
184
|
|
176
|
-
tenant :
|
185
|
+
tenant :tenant
|
177
186
|
|
178
187
|
field :slug
|
179
188
|
|
@@ -181,19 +190,19 @@ class Article
|
|
181
190
|
end
|
182
191
|
```
|
183
192
|
|
184
|
-
* When used with an *optional* tenant, the uniqueness constraint is not scoped if the item is shared, but is
|
185
|
-
scoped to the client new item otherwise. Note that a private item cannot have
|
186
|
-
already uses it.
|
193
|
+
* When used with an *optional* tenant, the uniqueness constraint by default is not scoped if the item is shared, but is
|
194
|
+
scoped to the client new item otherwise. Note that by default in that case a private item cannot have a value if a shared item
|
195
|
+
already uses it. You can change that behaviour by setting the option `exclude_shared` to `true`.
|
187
196
|
|
188
197
|
In the following case, 2 private articles can have the same slug if they belongs to 2 different clients. But if a shared
|
189
|
-
article has the slug "slugA", no client will be able to use that slug again, like a standard validates_uniqueness_of does.
|
198
|
+
article has the slug "slugA", no client will be able to use that slug again, like a standard `validates_uniqueness_of` does.
|
190
199
|
|
191
200
|
```ruby
|
192
201
|
class Article
|
193
202
|
include Mongoid::Document
|
194
203
|
include Mongoid::Multitenancy::Document
|
195
204
|
|
196
|
-
tenant :
|
205
|
+
tenant :tenant, optional: true
|
197
206
|
|
198
207
|
field :slug
|
199
208
|
|
@@ -201,6 +210,22 @@ class Article
|
|
201
210
|
end
|
202
211
|
```
|
203
212
|
|
213
|
+
In the following case, 2 private articles can have the same slug if they belongs to 2 different clients even if a shared
|
214
|
+
article already uses that same slug, like a `validates_uniqueness_of scope: :tenant` does.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
class Article
|
218
|
+
include Mongoid::Document
|
219
|
+
include Mongoid::Multitenancy::Document
|
220
|
+
|
221
|
+
tenant :tenant, optional: true
|
222
|
+
|
223
|
+
field :slug
|
224
|
+
|
225
|
+
validates_tenant_uniqueness_of :slug, exclude_shared: true
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
204
229
|
Mongoid indexes
|
205
230
|
-------------------
|
206
231
|
|
@@ -210,14 +235,14 @@ To create a single index on the tenant field, you can use the option `index: tru
|
|
210
235
|
|
211
236
|
On the example below, only one indexe will be created:
|
212
237
|
|
213
|
-
* { 'title_id' => 1, '
|
238
|
+
* { 'title_id' => 1, 'tenant_id' => 1 }
|
214
239
|
|
215
240
|
```ruby
|
216
241
|
class Article
|
217
242
|
include Mongoid::Document
|
218
243
|
include Mongoid::Multitenancy::Document
|
219
244
|
|
220
|
-
tenant :
|
245
|
+
tenant :tenant, full_indexes: true
|
221
246
|
|
222
247
|
field :title
|
223
248
|
|
@@ -227,7 +252,7 @@ end
|
|
227
252
|
|
228
253
|
On the example below, 2 indexes will be created:
|
229
254
|
|
230
|
-
* { '
|
255
|
+
* { 'tenant_id' => 1 }
|
231
256
|
* { 'title_id' => 1 }
|
232
257
|
|
233
258
|
```ruby
|
@@ -235,7 +260,7 @@ class Article
|
|
235
260
|
include Mongoid::Document
|
236
261
|
include Mongoid::Multitenancy::Document
|
237
262
|
|
238
|
-
tenant :
|
263
|
+
tenant :tenant, index: true
|
239
264
|
|
240
265
|
field :title
|
241
266
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'mongoid', '~> 6.0'
|
4
|
+
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
group :test do
|
8
|
+
gem 'database_cleaner'
|
9
|
+
gem 'coveralls', require: false
|
10
|
+
gem 'rspec', '~> 3.1'
|
11
|
+
gem 'yard'
|
12
|
+
gem 'mongoid-rspec', git: 'https://github.com/mongoid-rspec/mongoid-rspec.git'
|
13
|
+
gem 'rubocop', require: false
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'mongoid', '~> 7.0'
|
4
|
+
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
group :test do
|
8
|
+
gem 'database_cleaner'
|
9
|
+
gem 'coveralls', require: false
|
10
|
+
gem 'rspec', '~> 3.1'
|
11
|
+
gem 'yard'
|
12
|
+
gem 'mongoid-rspec', git: 'https://github.com/mongoid-rspec/mongoid-rspec.git'
|
13
|
+
gem 'rubocop', require: false
|
14
|
+
end
|
data/lib/mongoid/multitenancy.rb
CHANGED
@@ -7,7 +7,6 @@ require 'mongoid/multitenancy/validators/tenant_uniqueness'
|
|
7
7
|
module Mongoid
|
8
8
|
module Multitenancy
|
9
9
|
class << self
|
10
|
-
|
11
10
|
# Set the current tenant. Make it Thread aware
|
12
11
|
def current_tenant=(tenant)
|
13
12
|
Thread.current[:current_tenant] = tenant
|
@@ -22,11 +21,13 @@ module Mongoid
|
|
22
21
|
def with_tenant(tenant, &block)
|
23
22
|
raise ArgumentError, 'block required' if block.nil?
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
begin
|
25
|
+
old_tenant = current_tenant
|
26
|
+
self.current_tenant = tenant
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
self.current_tenant = old_tenant
|
30
|
+
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
@@ -7,7 +7,7 @@ module Mongoid
|
|
7
7
|
attr_accessor :tenant_field, :tenant_options
|
8
8
|
|
9
9
|
# List of authorized options
|
10
|
-
MULTITENANCY_OPTIONS = [:optional, :immutable, :full_indexes, :index]
|
10
|
+
MULTITENANCY_OPTIONS = [:optional, :immutable, :full_indexes, :index, :scopes].freeze
|
11
11
|
|
12
12
|
# Defines the tenant field for the document.
|
13
13
|
#
|
@@ -24,10 +24,13 @@ module Mongoid
|
|
24
24
|
# wil raise an Exception.
|
25
25
|
# @option options [ Boolean ] :optional If true allow the document
|
26
26
|
# to be shared among all the tenants.
|
27
|
-
#
|
27
|
+
# @option options [ Boolean ] :index If true build an index for
|
28
|
+
# the tenant field itself.
|
29
|
+
# @option options [ Boolean ] :scopes If true create scopes :shared
|
30
|
+
# and :unshared.
|
28
31
|
# @return [ Field ] The generated field
|
29
32
|
def tenant(association = :account, options = {})
|
30
|
-
options = { full_indexes: true, immutable: true }.merge!(options)
|
33
|
+
options = { full_indexes: true, immutable: true, scopes: true }.merge!(options)
|
31
34
|
assoc_options, multitenant_options = build_options(options)
|
32
35
|
|
33
36
|
# Setup the association between the class and the tenant class
|
@@ -40,7 +43,7 @@ module Mongoid
|
|
40
43
|
# Validates the tenant field
|
41
44
|
validates_tenancy_of tenant_field, multitenant_options
|
42
45
|
|
43
|
-
define_scopes
|
46
|
+
define_scopes if multitenant_options[:scopes]
|
44
47
|
define_initializer association
|
45
48
|
define_inherited association, options
|
46
49
|
define_index if multitenant_options[:index]
|
@@ -92,7 +95,7 @@ module Mongoid
|
|
92
95
|
end
|
93
96
|
|
94
97
|
# Redefine 'delete_all' to take in account the default scope
|
95
|
-
def delete_all(conditions =
|
98
|
+
def delete_all(conditions = {})
|
96
99
|
scoped.where(conditions).delete
|
97
100
|
end
|
98
101
|
|
@@ -106,6 +109,7 @@ module Mongoid
|
|
106
109
|
options.each do |k, v|
|
107
110
|
if MULTITENANCY_OPTIONS.include?(k)
|
108
111
|
multitenant_options[k] = v
|
112
|
+
assoc_options[k] = v if k == :optional
|
109
113
|
else
|
110
114
|
assoc_options[k] = v
|
111
115
|
end
|
@@ -131,7 +135,7 @@ module Mongoid
|
|
131
135
|
# Define the inherited method
|
132
136
|
def define_inherited(association, options)
|
133
137
|
define_singleton_method(:inherited) do |child|
|
134
|
-
child.tenant association, options
|
138
|
+
child.tenant association, options.merge(scopes: false)
|
135
139
|
super(child)
|
136
140
|
end
|
137
141
|
end
|
@@ -150,7 +154,7 @@ module Mongoid
|
|
150
154
|
where(tenant_field => tenant_id)
|
151
155
|
end
|
152
156
|
else
|
153
|
-
|
157
|
+
all
|
154
158
|
end
|
155
159
|
}
|
156
160
|
|
@@ -16,18 +16,18 @@ module Mongoid
|
|
16
16
|
def validate_each(object, attribute, value)
|
17
17
|
# Immutable Check
|
18
18
|
if options[:immutable]
|
19
|
-
if object.send(:attribute_changed?, attribute)
|
19
|
+
if object.send(:attribute_changed?, attribute) && object.send(:attribute_was, attribute)
|
20
20
|
object.errors.add(attribute, 'is immutable and cannot be updated')
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
# Ownership check
|
25
|
-
if value
|
26
|
-
object.errors.add(attribute,
|
25
|
+
if value && Mongoid::Multitenancy.current_tenant && value != Mongoid::Multitenancy.current_tenant.id
|
26
|
+
object.errors.add(attribute, 'not authorized')
|
27
27
|
end
|
28
28
|
|
29
29
|
# Optional Check
|
30
|
-
if !options[:optional]
|
30
|
+
if !options[:optional] && value.nil?
|
31
31
|
object.errors.add(attribute, 'is mandatory')
|
32
32
|
end
|
33
33
|
end
|
@@ -39,14 +39,8 @@ module Mongoid
|
|
39
39
|
criteria = with_tenant_criterion(criteria, klass, document)
|
40
40
|
criteria = criteria.merge(options[:conditions].call) if options[:conditions]
|
41
41
|
|
42
|
-
if
|
43
|
-
|
44
|
-
add_error(document, attribute, value)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
if criteria.with(criteria.persistence_options).read(mode: :primary).exists?
|
48
|
-
add_error(document, attribute, value)
|
49
|
-
end
|
42
|
+
if criteria.read(mode: :primary).exists?
|
43
|
+
add_error(document, attribute, value)
|
50
44
|
end
|
51
45
|
end
|
52
46
|
|
@@ -58,8 +52,8 @@ module Mongoid
|
|
58
52
|
name = document.database_field_name(item)
|
59
53
|
tenant_value = document.attributes[name]
|
60
54
|
|
61
|
-
if document.class.tenant_options[:optional]
|
62
|
-
if
|
55
|
+
if document.class.tenant_options[:optional] && !options[:exclude_shared]
|
56
|
+
if tenant_value
|
63
57
|
criteria = criteria.where(:"#{item}".in => [tenant_value, nil])
|
64
58
|
end
|
65
59
|
else
|
@@ -8,12 +8,12 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.summary = 'Support of a multi-tenant database with Mongoid'
|
9
9
|
gem.homepage = 'https://github.com/PerfectMemory/mongoid-multitenancy'
|
10
10
|
gem.license = 'MIT'
|
11
|
-
gem.files = `git ls-files`.split(
|
12
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
11
|
+
gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
13
13
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
14
|
gem.name = 'mongoid-multitenancy'
|
15
15
|
gem.require_paths = ['lib']
|
16
16
|
gem.version = Mongoid::Multitenancy::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency('mongoid', '>=
|
18
|
+
gem.add_dependency('mongoid', '>= 6', '< 8')
|
19
19
|
end
|
data/spec/immutable_spec.rb
CHANGED
data/spec/indexable_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe 'tenant' do
|
4
|
-
|
5
4
|
let(:client) do
|
6
5
|
Account.create!(name: 'client')
|
7
6
|
end
|
@@ -12,26 +11,25 @@ describe 'tenant' do
|
|
12
11
|
|
13
12
|
context 'without index: true' do
|
14
13
|
it 'does not create an index' do
|
15
|
-
expect(Immutable).not_to have_index_for(:
|
14
|
+
expect(Immutable).not_to have_index_for(tenant_id: 1)
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
18
|
context 'with index: true' do
|
20
19
|
it 'creates an index' do
|
21
|
-
expect(Indexable).to have_index_for(:
|
20
|
+
expect(Indexable).to have_index_for(tenant_id: 1)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
25
24
|
context 'with full_indexes: true' do
|
26
25
|
it 'add the tenant field on each index' do
|
27
|
-
expect(Immutable).to have_index_for(:
|
26
|
+
expect(Immutable).to have_index_for(tenant_id: 1, title: 1)
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
31
30
|
context 'with full_indexes: false' do
|
32
31
|
it 'does not add the tenant field on each index' do
|
33
|
-
expect(Indexable).not_to have_index_for(:
|
32
|
+
expect(Indexable).not_to have_index_for(tenant_id: 1, title: 1)
|
34
33
|
end
|
35
34
|
end
|
36
|
-
|
37
35
|
end
|
data/spec/mandatory_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Mandatory do
|
4
|
-
|
5
4
|
let(:client) do
|
6
5
|
Account.create!(name: 'client')
|
7
6
|
end
|
@@ -17,6 +16,18 @@ describe Mandatory do
|
|
17
16
|
it_behaves_like 'a tenantable model'
|
18
17
|
it { is_expected.to validate_tenant_uniqueness_of(:slug) }
|
19
18
|
|
19
|
+
describe '.shared' do
|
20
|
+
it 'is defined' do
|
21
|
+
expect(Mandatory).to respond_to(:shared)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.unshared' do
|
26
|
+
it 'is defined' do
|
27
|
+
expect(Mandatory).to respond_to(:unshared)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
20
31
|
describe '.default_scope' do
|
21
32
|
before do
|
22
33
|
Mongoid::Multitenancy.with_tenant(client) { @itemX = Mandatory.create!(title: 'title X', slug: 'article-x') }
|
data/spec/models/account.rb
CHANGED
data/spec/models/immutable.rb
CHANGED
@@ -2,14 +2,14 @@ class Immutable
|
|
2
2
|
include Mongoid::Document
|
3
3
|
include Mongoid::Multitenancy::Document
|
4
4
|
|
5
|
-
tenant(:
|
5
|
+
tenant(:tenant, class_name: 'Account', immutable: true)
|
6
6
|
|
7
|
-
field :slug, :
|
8
|
-
field :title, :
|
7
|
+
field :slug, type: String
|
8
|
+
field :title, type: String
|
9
9
|
|
10
10
|
validates_tenant_uniqueness_of :slug
|
11
11
|
validates_presence_of :slug
|
12
12
|
validates_presence_of :title
|
13
13
|
|
14
|
-
index(
|
14
|
+
index(title: 1)
|
15
15
|
end
|
data/spec/models/indexable.rb
CHANGED
data/spec/models/mandatory.rb
CHANGED
@@ -2,14 +2,14 @@ class Mandatory
|
|
2
2
|
include Mongoid::Document
|
3
3
|
include Mongoid::Multitenancy::Document
|
4
4
|
|
5
|
-
tenant(:
|
5
|
+
tenant(:tenant, class_name: 'Account')
|
6
6
|
|
7
7
|
field :slug, type: String
|
8
|
-
field :title, type:String
|
8
|
+
field :title, type: String
|
9
9
|
|
10
10
|
validates_tenant_uniqueness_of :slug
|
11
11
|
validates_presence_of :slug
|
12
12
|
validates_presence_of :title
|
13
13
|
|
14
|
-
index(
|
14
|
+
index(title: 1)
|
15
15
|
end
|
data/spec/models/mutable.rb
CHANGED
@@ -2,14 +2,14 @@ class Mutable
|
|
2
2
|
include Mongoid::Document
|
3
3
|
include Mongoid::Multitenancy::Document
|
4
4
|
|
5
|
-
tenant :
|
5
|
+
tenant :tenant, class_name: 'Account', immutable: false, optional: false
|
6
6
|
|
7
|
-
field :slug, :
|
8
|
-
field :title, :
|
7
|
+
field :slug, type: String
|
8
|
+
field :title, type: String
|
9
9
|
|
10
10
|
validates_tenant_uniqueness_of :slug
|
11
11
|
validates_presence_of :slug
|
12
12
|
validates_presence_of :title
|
13
13
|
|
14
|
-
index(
|
14
|
+
index(title: 1)
|
15
15
|
end
|
data/spec/models/optional.rb
CHANGED
@@ -2,7 +2,7 @@ class Optional
|
|
2
2
|
include Mongoid::Document
|
3
3
|
include Mongoid::Multitenancy::Document
|
4
4
|
|
5
|
-
tenant(:
|
5
|
+
tenant(:tenant, class_name: 'Account', optional: true)
|
6
6
|
|
7
7
|
field :slug, type: String
|
8
8
|
field :title, type: String
|
@@ -11,5 +11,5 @@ class Optional
|
|
11
11
|
validates_presence_of :slug
|
12
12
|
validates_presence_of :title
|
13
13
|
|
14
|
-
index(
|
14
|
+
index(title: 1)
|
15
15
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class OptionalExclude
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Multitenancy::Document
|
4
|
+
|
5
|
+
tenant(:tenant, class_name: 'Account', optional: true)
|
6
|
+
|
7
|
+
field :slug, type: String
|
8
|
+
field :title, type: String
|
9
|
+
|
10
|
+
validates_tenant_uniqueness_of :slug, exclude_shared: true
|
11
|
+
validates_presence_of :slug
|
12
|
+
validates_presence_of :title
|
13
|
+
|
14
|
+
index(title: 1)
|
15
|
+
end
|
@@ -21,8 +21,17 @@ describe Mongoid::Multitenancy do
|
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'restores the current tenant after the block' do
|
24
|
-
Mongoid::Multitenancy.with_tenant(another_client)
|
24
|
+
Mongoid::Multitenancy.with_tenant(another_client) { ; }
|
25
25
|
expect(Mongoid::Multitenancy.current_tenant).to eq client
|
26
26
|
end
|
27
|
+
|
28
|
+
context 'when the block fails' do
|
29
|
+
it 'restores the current tenant' do
|
30
|
+
begin
|
31
|
+
Mongoid::Multitenancy.with_tenant(another_client) { raise StandardError }
|
32
|
+
rescue StandardError; end
|
33
|
+
expect(Mongoid::Multitenancy.current_tenant).to eq client
|
34
|
+
end
|
35
|
+
end
|
27
36
|
end
|
28
37
|
end
|
data/spec/mutable_child_spec.rb
CHANGED
data/spec/mutable_spec.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OptionalExclude do
|
4
|
+
let(:client) do
|
5
|
+
Account.create!(name: 'client')
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:another_client) do
|
9
|
+
Account.create!(name: 'another client')
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:item) do
|
13
|
+
OptionalExclude.new(title: 'title X', slug: 'page-x')
|
14
|
+
end
|
15
|
+
|
16
|
+
it_behaves_like 'a tenantable model'
|
17
|
+
|
18
|
+
describe '#valid?' do
|
19
|
+
context 'with a tenant' do
|
20
|
+
before do
|
21
|
+
Mongoid::Multitenancy.current_tenant = client
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is valid' do
|
25
|
+
expect(item).to be_valid
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with a uniqueness constraint' do
|
29
|
+
let(:duplicate) do
|
30
|
+
OptionalExclude.new(title: 'title Y', slug: 'page-x')
|
31
|
+
end
|
32
|
+
|
33
|
+
before do
|
34
|
+
item.save!
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'does not allow duplicates on the same tenant' do
|
38
|
+
expect(duplicate).not_to be_valid
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'allow duplicates on a different same tenant' do
|
42
|
+
Mongoid::Multitenancy.with_tenant(another_client) do
|
43
|
+
expect(duplicate).to be_valid
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'without a tenant' do
|
50
|
+
it 'is valid' do
|
51
|
+
expect(item).to be_valid
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'with a uniqueness constraint' do
|
55
|
+
let(:duplicate) do
|
56
|
+
OptionalExclude.new(title: 'title Y', slug: 'page-x')
|
57
|
+
end
|
58
|
+
|
59
|
+
before do
|
60
|
+
item.save!
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'allow duplicates on any client' do
|
64
|
+
Mongoid::Multitenancy.with_tenant(client) do
|
65
|
+
expect(duplicate).to be_valid
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/spec/optional_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Optional do
|
4
|
-
|
5
4
|
let(:client) do
|
6
5
|
Account.create!(name: 'client')
|
7
6
|
end
|
@@ -25,13 +24,13 @@ describe Optional do
|
|
25
24
|
|
26
25
|
context 'when persisted' do
|
27
26
|
before do
|
28
|
-
item.
|
27
|
+
item.tenant = nil
|
29
28
|
item.save!
|
30
29
|
end
|
31
30
|
|
32
31
|
it 'does not override the client' do
|
33
32
|
item.reload
|
34
|
-
expect(Optional.last.
|
33
|
+
expect(Optional.last.tenant).to be_nil
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NoScopable do
|
4
|
+
it_behaves_like 'a tenantable model' do
|
5
|
+
let(:client) do
|
6
|
+
Account.create!(name: 'client')
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:another_client) do
|
10
|
+
Account.create!(name: 'another client')
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:item) do
|
14
|
+
NoScopable.new(title: 'title X', slug: 'page-x')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.shared' do
|
19
|
+
it 'is not defined' do
|
20
|
+
expect(NoScopable).not_to respond_to(:shared)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.unshared' do
|
25
|
+
it 'is not defined' do
|
26
|
+
expect(NoScopable).not_to respond_to(:unshared)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -15,12 +15,6 @@ require 'mongoid'
|
|
15
15
|
require 'mongoid-multitenancy'
|
16
16
|
require 'mongoid-rspec'
|
17
17
|
|
18
|
-
if Mongoid::VERSION.start_with? '5'
|
19
|
-
Mongo::Logger.logger.level = ::Logger::FATAL
|
20
|
-
elsif Mongoid::VERSION.start_with? '4'
|
21
|
-
Moped.logger = nil
|
22
|
-
end
|
23
|
-
|
24
18
|
require_relative 'support/shared_examples'
|
25
19
|
require_relative 'support/mongoid_matchers'
|
26
20
|
|
@@ -30,7 +24,8 @@ Mongoid.configure do |config|
|
|
30
24
|
config.connect_to 'mongoid_multitenancy'
|
31
25
|
end
|
32
26
|
|
33
|
-
Mongoid.logger =
|
27
|
+
Mongoid.logger.level = Logger::INFO
|
28
|
+
Mongo::Logger.logger.level = Logger::INFO
|
34
29
|
|
35
30
|
RSpec.configure do |config|
|
36
31
|
config.include Mongoid::Matchers
|
@@ -1,7 +1,6 @@
|
|
1
1
|
shared_examples_for 'a tenantable model' do
|
2
|
-
|
3
|
-
it { is_expected.to
|
4
|
-
it { is_expected.to have_index_for(client_id: 1, title: 1) }
|
2
|
+
it { is_expected.to belong_to(:tenant) }
|
3
|
+
it { is_expected.to have_index_for(tenant_id: 1, title: 1) }
|
5
4
|
|
6
5
|
describe '#initialize' do
|
7
6
|
context 'within a client context' do
|
@@ -10,7 +9,7 @@ shared_examples_for 'a tenantable model' do
|
|
10
9
|
end
|
11
10
|
|
12
11
|
it 'set the client' do
|
13
|
-
expect(item.
|
12
|
+
expect(item.tenant).to eq client
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
@@ -20,7 +19,7 @@ shared_examples_for 'a tenantable model' do
|
|
20
19
|
end
|
21
20
|
|
22
21
|
it 'does not set any client' do
|
23
|
-
expect(item.
|
22
|
+
expect(item.tenant).to be_nil
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
@@ -33,7 +32,7 @@ shared_examples_for 'a tenantable model' do
|
|
33
32
|
|
34
33
|
context 'with the client id' do
|
35
34
|
before do
|
36
|
-
item.
|
35
|
+
item.tenant = client
|
37
36
|
end
|
38
37
|
|
39
38
|
it 'is valid' do
|
@@ -43,7 +42,7 @@ shared_examples_for 'a tenantable model' do
|
|
43
42
|
|
44
43
|
context 'with another client id' do
|
45
44
|
before do
|
46
|
-
item.
|
45
|
+
item.tenant = another_client
|
47
46
|
end
|
48
47
|
|
49
48
|
it 'is not valid' do
|
@@ -59,7 +58,7 @@ shared_examples_for 'a tenantable model' do
|
|
59
58
|
|
60
59
|
context 'with the client id' do
|
61
60
|
before do
|
62
|
-
item.
|
61
|
+
item.tenant = client
|
63
62
|
end
|
64
63
|
|
65
64
|
it 'is valid' do
|
@@ -69,7 +68,7 @@ shared_examples_for 'a tenantable model' do
|
|
69
68
|
|
70
69
|
context 'with another client id' do
|
71
70
|
before do
|
72
|
-
item.
|
71
|
+
item.tenant = another_client
|
73
72
|
end
|
74
73
|
|
75
74
|
it 'is valid' do
|
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:
|
4
|
+
version: 2.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:
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongoid
|
@@ -16,14 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '8'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '6'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '8'
|
27
33
|
description: MultiTenancy with Mongoid
|
28
34
|
email:
|
29
35
|
- aymeric.brisse@mperfect-memory.com
|
@@ -39,8 +45,8 @@ files:
|
|
39
45
|
- LICENSE.TXT
|
40
46
|
- README.md
|
41
47
|
- Rakefile
|
42
|
-
- gemfiles/Gemfile.mongoid-
|
43
|
-
- gemfiles/Gemfile.mongoid-
|
48
|
+
- gemfiles/Gemfile.mongoid-6
|
49
|
+
- gemfiles/Gemfile.mongoid-7
|
44
50
|
- lib/mongoid-multitenancy.rb
|
45
51
|
- lib/mongoid/multitenancy.rb
|
46
52
|
- lib/mongoid/multitenancy/document.rb
|
@@ -58,11 +64,15 @@ files:
|
|
58
64
|
- spec/models/mandatory.rb
|
59
65
|
- spec/models/mutable.rb
|
60
66
|
- spec/models/mutable_child.rb
|
67
|
+
- spec/models/no_scopable.rb
|
61
68
|
- spec/models/optional.rb
|
69
|
+
- spec/models/optional_exclude.rb
|
62
70
|
- spec/mongoid-multitenancy_spec.rb
|
63
71
|
- spec/mutable_child_spec.rb
|
64
72
|
- spec/mutable_spec.rb
|
73
|
+
- spec/optional_exclude_spec.rb
|
65
74
|
- spec/optional_spec.rb
|
75
|
+
- spec/scopable_spec.rb
|
66
76
|
- spec/spec_helper.rb
|
67
77
|
- spec/support/mongoid_matchers.rb
|
68
78
|
- spec/support/shared_examples.rb
|
@@ -86,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
96
|
version: '0'
|
87
97
|
requirements: []
|
88
98
|
rubyforge_project:
|
89
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.6.14.4
|
90
100
|
signing_key:
|
91
101
|
specification_version: 4
|
92
102
|
summary: Support of a multi-tenant database with Mongoid
|
@@ -101,12 +111,15 @@ test_files:
|
|
101
111
|
- spec/models/mandatory.rb
|
102
112
|
- spec/models/mutable.rb
|
103
113
|
- spec/models/mutable_child.rb
|
114
|
+
- spec/models/no_scopable.rb
|
104
115
|
- spec/models/optional.rb
|
116
|
+
- spec/models/optional_exclude.rb
|
105
117
|
- spec/mongoid-multitenancy_spec.rb
|
106
118
|
- spec/mutable_child_spec.rb
|
107
119
|
- spec/mutable_spec.rb
|
120
|
+
- spec/optional_exclude_spec.rb
|
108
121
|
- spec/optional_spec.rb
|
122
|
+
- spec/scopable_spec.rb
|
109
123
|
- spec/spec_helper.rb
|
110
124
|
- spec/support/mongoid_matchers.rb
|
111
125
|
- spec/support/shared_examples.rb
|
112
|
-
has_rdoc:
|
@@ -1,16 +0,0 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gem 'mongoid', '~> 4.0'
|
4
|
-
|
5
|
-
gem 'rake', '~> 10.0'
|
6
|
-
|
7
|
-
group :test do
|
8
|
-
gem 'database_cleaner', github: 'DatabaseCleaner/database_cleaner', ref: '1cab518'
|
9
|
-
gem 'coveralls', :require => false
|
10
|
-
gem 'rspec', '~> 3.1'
|
11
|
-
gem 'yard', '~> 0.8'
|
12
|
-
gem 'mongoid-rspec', '~> 2.1'
|
13
|
-
end
|
14
|
-
|
15
|
-
# Specify your gem's dependencies in mongoid-multitenancy.gemspec
|
16
|
-
gemspec :path => '..'
|
@@ -1,16 +0,0 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gem 'mongoid', '~> 5.0'
|
4
|
-
|
5
|
-
gem 'rake', '~> 10.0'
|
6
|
-
|
7
|
-
group :test do
|
8
|
-
gem 'database_cleaner', github: 'DatabaseCleaner/database_cleaner', ref: '1cab518'
|
9
|
-
gem 'coveralls', :require => false
|
10
|
-
gem 'rspec', '~> 3.1'
|
11
|
-
gem 'yard', '~> 0.8'
|
12
|
-
gem 'mongoid-rspec', '~> 3.0'
|
13
|
-
end
|
14
|
-
|
15
|
-
# Specify your gem's dependencies in mongoid-multitenancy.gemspec
|
16
|
-
gemspec :path => '..'
|