activerecord-multi-tenant 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -6
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +5 -1
- data/gemfiles/rails_3.2.gemfile.lock +3 -3
- data/gemfiles/rails_4.0.gemfile.lock +5 -5
- data/gemfiles/rails_4.1.gemfile.lock +5 -5
- data/gemfiles/rails_4.2.gemfile.lock +6 -6
- data/gemfiles/rails_5.0.gemfile.lock +6 -6
- data/lib/activerecord-multi-tenant/model_extensions.rb +44 -55
- data/lib/activerecord-multi-tenant/multi_tenant.rb +17 -38
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +44 -1
- data/spec/schema.rb +14 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c30e8a7b37b923b66c5043fb73f3c9e776cea85
|
4
|
+
data.tar.gz: 63a2537270723a549cab19a3a75e5c9eaee4fdac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fe0741bcd4279f6852aa8fc64a25921bb8fb9953cbce564a81ec611602bc0321ca6ededc84ade5053795d64c3e1d3b2a055c6e5c19aa54003ea18c65aa5651e
|
7
|
+
data.tar.gz: f59653a116f517ceafdd91677518113299bf86b281259a0816c66299c3ac5b2fa8ec2d347078034ace050adb21aeb12c40ff476e36fe8f9e238f695d99c1f3ad
|
data/.travis.yml
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
+
sudo: required
|
2
|
+
cache: bundler
|
3
|
+
|
1
4
|
language: ruby
|
2
5
|
|
3
6
|
rvm:
|
4
7
|
- 2.1
|
5
|
-
- 2.2.6
|
6
8
|
- 2.3.3
|
7
|
-
- 2.4
|
8
|
-
|
9
|
-
script: "bundle exec rake spec"
|
10
9
|
|
11
10
|
gemfile:
|
12
11
|
- gemfiles/rails_3.2.gemfile
|
@@ -21,5 +20,12 @@ matrix:
|
|
21
20
|
- gemfile: gemfiles/rails_5.0.gemfile
|
22
21
|
rvm: 2.1
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
services:
|
24
|
+
- docker
|
25
|
+
|
26
|
+
before_install:
|
27
|
+
- docker-compose up -d
|
28
|
+
- sleep 5 # wait for postgres to become available
|
29
|
+
|
30
|
+
script:
|
31
|
+
- bundle exec rake spec
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.4.0 2017-03-22
|
4
|
+
|
5
|
+
* Infer multi_tenant setting from parent classes [@webandtech](https://github.com/webandtech) [#6](https://github.com/citusdata/activerecord-multi-tenant/pull/6)
|
6
|
+
* Remove use of global tenant klass variable [@webandtech](https://github.com/webandtech) [#6](https://github.com/citusdata/activerecord-multi-tenant/pull/6)
|
7
|
+
* Support passing ID values to MultiTenant.with directly [@webandtech](https://github.com/webandtech) [#6](https://github.com/citusdata/activerecord-multi-tenant/pull/6)
|
8
|
+
* This effectively deprecates with_id, but we'll keep it around for now
|
9
|
+
* Remove unnecessary validation for invalid belongs_to association
|
10
|
+
|
11
|
+
|
3
12
|
## 0.3.4 2017-02-22
|
4
13
|
|
5
14
|
* Expand with_lock workaround to cover lock! as well
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -69,7 +69,11 @@ end
|
|
69
69
|
|
70
70
|
* **What if I have a table that doesn't relate to my tenant?** (e.g. templates that are the same in every account)
|
71
71
|
|
72
|
-
We recommend not using activerecord-multi-tenant on these tables. In case only some records in a table are not associated to a tenant (i.e. your templates are in the same table as actual objects), we recommend setting the tenant_id to 0, and then using MultiTenant.
|
72
|
+
We recommend not using activerecord-multi-tenant on these tables. In case only some records in a table are not associated to a tenant (i.e. your templates are in the same table as actual objects), we recommend setting the tenant_id to 0, and then using MultiTenant.with(0) to access these objects.
|
73
|
+
|
74
|
+
* **What if my tenant model is not defined in my application?**
|
75
|
+
|
76
|
+
The tenant model does not have to be defined. Use the gem as if the model was present. `MultiTenant.with` accepts either a tenant id or model instance.
|
73
77
|
|
74
78
|
## Credits
|
75
79
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.4.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -45,7 +45,7 @@ GEM
|
|
45
45
|
diff-lcs (1.3)
|
46
46
|
erubis (2.7.0)
|
47
47
|
hike (1.2.3)
|
48
|
-
i18n (0.8.
|
48
|
+
i18n (0.8.1)
|
49
49
|
journey (1.0.4)
|
50
50
|
json (1.8.6)
|
51
51
|
mail (2.5.4)
|
@@ -53,7 +53,7 @@ GEM
|
|
53
53
|
treetop (~> 1.4.8)
|
54
54
|
mime-types (1.25.1)
|
55
55
|
multi_json (1.12.1)
|
56
|
-
pg (0.
|
56
|
+
pg (0.20.0)
|
57
57
|
polyglot (0.3.5)
|
58
58
|
power_assert (1.0.1)
|
59
59
|
rack (1.4.7)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.4.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -38,11 +38,11 @@ GEM
|
|
38
38
|
thor (>= 0.14.0)
|
39
39
|
arel (4.0.2)
|
40
40
|
builder (3.1.4)
|
41
|
-
concurrent-ruby (1.0.
|
41
|
+
concurrent-ruby (1.0.5)
|
42
42
|
database_cleaner (1.3.0)
|
43
43
|
diff-lcs (1.3)
|
44
44
|
erubis (2.7.0)
|
45
|
-
i18n (0.8.
|
45
|
+
i18n (0.8.1)
|
46
46
|
mail (2.6.4)
|
47
47
|
mime-types (>= 1.16, < 4)
|
48
48
|
mime-types (3.1)
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
mime-types-data (3.2016.0521)
|
51
51
|
minitest (4.7.5)
|
52
52
|
multi_json (1.12.1)
|
53
|
-
pg (0.
|
53
|
+
pg (0.20.0)
|
54
54
|
rack (1.5.5)
|
55
55
|
rack-test (0.6.3)
|
56
56
|
rack (>= 1.0)
|
@@ -98,7 +98,7 @@ GEM
|
|
98
98
|
activesupport (>= 3.0)
|
99
99
|
sprockets (>= 2.8, < 4.0)
|
100
100
|
thor (0.19.4)
|
101
|
-
thread_safe (0.3.
|
101
|
+
thread_safe (0.3.6)
|
102
102
|
tzinfo (0.3.52)
|
103
103
|
|
104
104
|
PLATFORMS
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.4.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -40,11 +40,11 @@ GEM
|
|
40
40
|
thor (>= 0.14.0)
|
41
41
|
arel (5.0.1.20140414130214)
|
42
42
|
builder (3.2.3)
|
43
|
-
concurrent-ruby (1.0.
|
43
|
+
concurrent-ruby (1.0.5)
|
44
44
|
database_cleaner (1.3.0)
|
45
45
|
diff-lcs (1.3)
|
46
46
|
erubis (2.7.0)
|
47
|
-
i18n (0.8.
|
47
|
+
i18n (0.8.1)
|
48
48
|
json (1.8.6)
|
49
49
|
mail (2.6.4)
|
50
50
|
mime-types (>= 1.16, < 4)
|
@@ -52,7 +52,7 @@ GEM
|
|
52
52
|
mime-types-data (~> 3.2015)
|
53
53
|
mime-types-data (3.2016.0521)
|
54
54
|
minitest (5.10.1)
|
55
|
-
pg (0.
|
55
|
+
pg (0.20.0)
|
56
56
|
rack (1.5.5)
|
57
57
|
rack-test (0.6.3)
|
58
58
|
rack (>= 1.0)
|
@@ -102,7 +102,7 @@ GEM
|
|
102
102
|
activesupport (>= 3.0)
|
103
103
|
sprockets (>= 2.8, < 4.0)
|
104
104
|
thor (0.19.4)
|
105
|
-
thread_safe (0.3.
|
105
|
+
thread_safe (0.3.6)
|
106
106
|
tzinfo (1.2.2)
|
107
107
|
thread_safe (~> 0.1)
|
108
108
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.4.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -48,13 +48,13 @@ GEM
|
|
48
48
|
thor (>= 0.14.0)
|
49
49
|
arel (6.0.4)
|
50
50
|
builder (3.2.3)
|
51
|
-
concurrent-ruby (1.0.
|
51
|
+
concurrent-ruby (1.0.5)
|
52
52
|
database_cleaner (1.3.0)
|
53
53
|
diff-lcs (1.3)
|
54
54
|
erubis (2.7.0)
|
55
55
|
globalid (0.3.7)
|
56
56
|
activesupport (>= 4.1.0)
|
57
|
-
i18n (0.8.
|
57
|
+
i18n (0.8.1)
|
58
58
|
loofah (2.0.3)
|
59
59
|
nokogiri (>= 1.5.9)
|
60
60
|
mail (2.6.4)
|
@@ -64,9 +64,9 @@ GEM
|
|
64
64
|
mime-types-data (3.2016.0521)
|
65
65
|
mini_portile2 (2.1.0)
|
66
66
|
minitest (5.10.1)
|
67
|
-
nokogiri (1.7.
|
67
|
+
nokogiri (1.7.1)
|
68
68
|
mini_portile2 (~> 2.1.0)
|
69
|
-
pg (0.
|
69
|
+
pg (0.20.0)
|
70
70
|
rack (1.6.5)
|
71
71
|
rack-test (0.6.3)
|
72
72
|
rack (>= 1.0)
|
@@ -125,7 +125,7 @@ GEM
|
|
125
125
|
activesupport (>= 4.0)
|
126
126
|
sprockets (>= 3.0.0)
|
127
127
|
thor (0.19.4)
|
128
|
-
thread_safe (0.3.
|
128
|
+
thread_safe (0.3.6)
|
129
129
|
tzinfo (1.2.2)
|
130
130
|
thread_safe (~> 0.1)
|
131
131
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.4.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -51,13 +51,13 @@ GEM
|
|
51
51
|
thor (>= 0.14.0)
|
52
52
|
arel (7.1.4)
|
53
53
|
builder (3.2.3)
|
54
|
-
concurrent-ruby (1.0.
|
54
|
+
concurrent-ruby (1.0.5)
|
55
55
|
database_cleaner (1.3.0)
|
56
56
|
diff-lcs (1.3)
|
57
57
|
erubis (2.7.0)
|
58
58
|
globalid (0.3.7)
|
59
59
|
activesupport (>= 4.1.0)
|
60
|
-
i18n (0.8.
|
60
|
+
i18n (0.8.1)
|
61
61
|
loofah (2.0.3)
|
62
62
|
nokogiri (>= 1.5.9)
|
63
63
|
mail (2.6.4)
|
@@ -69,9 +69,9 @@ GEM
|
|
69
69
|
mini_portile2 (2.1.0)
|
70
70
|
minitest (5.10.1)
|
71
71
|
nio4r (1.2.1)
|
72
|
-
nokogiri (1.7.
|
72
|
+
nokogiri (1.7.1)
|
73
73
|
mini_portile2 (~> 2.1.0)
|
74
|
-
pg (0.
|
74
|
+
pg (0.20.0)
|
75
75
|
rack (2.0.1)
|
76
76
|
rack-test (0.6.3)
|
77
77
|
rack (>= 1.0)
|
@@ -129,7 +129,7 @@ GEM
|
|
129
129
|
activesupport (>= 4.0)
|
130
130
|
sprockets (>= 3.0.0)
|
131
131
|
thor (0.19.4)
|
132
|
-
thread_safe (0.3.
|
132
|
+
thread_safe (0.3.6)
|
133
133
|
tzinfo (1.2.2)
|
134
134
|
thread_safe (~> 0.1)
|
135
135
|
websocket-driver (0.6.5)
|
@@ -1,39 +1,45 @@
|
|
1
1
|
module MultiTenant
|
2
2
|
module ModelExtensionsClassMethods
|
3
|
-
|
4
|
-
# Workaround for https://github.com/citusdata/citus/issues/687
|
5
|
-
if to_s.underscore.to_sym == tenant
|
6
|
-
before_create -> { self.id ||= self.class.connection.select_value("SELECT nextval('" + [self.class.table_name, self.class.primary_key, 'seq'].join('_') + "'::regclass)") }
|
7
|
-
end
|
8
|
-
|
9
|
-
# Typically we don't need to run on the tenant model itself
|
10
|
-
if to_s.underscore.to_sym != tenant
|
11
|
-
MultiTenant.set_tenant_klass(tenant)
|
3
|
+
DEFAULT_ID_FIELD = 'id'.freeze
|
12
4
|
|
5
|
+
def multi_tenant(tenant_name, options = {})
|
6
|
+
if to_s.underscore.to_sym == tenant_name
|
7
|
+
# This is the tenant model itself. Workaround for https://github.com/citusdata/citus/issues/687
|
8
|
+
before_create -> { self.id ||= self.class.connection.select_value("SELECT nextval('" + [self.class.table_name, self.class.primary_key, 'seq'].join('_') + "'::regclass)") }
|
9
|
+
else
|
13
10
|
class << self
|
14
11
|
def scoped_by_tenant?
|
15
12
|
true
|
16
13
|
end
|
17
14
|
|
15
|
+
# Allow partition_key to be set from a superclass if not already set in this class
|
18
16
|
def partition_key
|
19
|
-
@partition_key
|
17
|
+
@partition_key ||= ancestors.detect{ |k| k.instance_variable_get(:@partition_key) }
|
18
|
+
.try(:instance_variable_get, :@partition_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Avoid primary_key errors when using composite primary keys (e.g. id, tenant_id)
|
22
|
+
def primary_key
|
23
|
+
return @primary_key if @primary_key
|
24
|
+
return @primary_key = super || DEFAULT_ID_FIELD if Rails::VERSION::MAJOR < 5
|
25
|
+
|
26
|
+
primary_object_keys = (connection.schema_cache.primary_keys(table_name) || []) - [partition_key]
|
27
|
+
if primary_object_keys.size == 1
|
28
|
+
@primary_key = primary_object_keys.first
|
29
|
+
else
|
30
|
+
@primary_key = DEFAULT_ID_FIELD
|
31
|
+
end
|
20
32
|
end
|
21
33
|
end
|
22
34
|
|
23
|
-
@partition_key = options[:partition_key] || MultiTenant.partition_key
|
35
|
+
@partition_key = options[:partition_key] || MultiTenant.partition_key(tenant_name)
|
24
36
|
partition_key = @partition_key
|
25
37
|
|
26
|
-
#
|
27
|
-
if
|
28
|
-
|
29
|
-
self.primary_key = primary_object_keys.first if primary_object_keys.size == 1
|
30
|
-
else
|
31
|
-
self.primary_key = 'id' if primary_key.nil?
|
38
|
+
# Create an implicit belongs_to association only if tenant class exists
|
39
|
+
if MultiTenant.tenant_klass_defined?(tenant_name)
|
40
|
+
belongs_to tenant_name, options.slice(:class_name, :inverse_of).merge(foreign_key: partition_key)
|
32
41
|
end
|
33
42
|
|
34
|
-
# Create the association
|
35
|
-
belongs_to tenant, options.slice(:class_name, :inverse_of).merge(foreign_key: partition_key)
|
36
|
-
|
37
43
|
# Ensure all queries include the partition key
|
38
44
|
default_scope lambda {
|
39
45
|
if MultiTenant.current_tenant_id
|
@@ -50,43 +56,26 @@ module MultiTenant
|
|
50
56
|
end
|
51
57
|
}, on: :create
|
52
58
|
|
53
|
-
# Validate that associations belong to the tenant, currently only for belongs_to
|
54
|
-
polymorphic_foreign_keys = reflect_on_all_associations(:belongs_to).select do |a|
|
55
|
-
a.options[:polymorphic]
|
56
|
-
end.map { |a| a.foreign_key }
|
57
|
-
|
58
|
-
reflect_on_all_associations(:belongs_to).each do |a|
|
59
|
-
unless a == reflect_on_association(tenant) || polymorphic_foreign_keys.include?(a.foreign_key)
|
60
|
-
association_class = a.options[:class_name].nil? ? a.name.to_s.classify.constantize : a.options[:class_name].constantize
|
61
|
-
validates_each a.foreign_key.to_sym do |record, attr, value|
|
62
|
-
primary_key = if association_class.respond_to?(:primary_key)
|
63
|
-
association_class.primary_key
|
64
|
-
else
|
65
|
-
a.primary_key
|
66
|
-
end.to_sym
|
67
|
-
record.errors.add attr, 'association is invalid [MultiTenant]' unless value.nil? || association_class.where(primary_key => value).any?
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
59
|
to_include = Module.new do
|
73
|
-
define_method "#{partition_key}=" do |
|
74
|
-
write_attribute("#{partition_key}",
|
60
|
+
define_method "#{partition_key}=" do |tenant_id|
|
61
|
+
write_attribute("#{partition_key}", tenant_id)
|
75
62
|
raise MultiTenant::TenantIsImmutable if send("#{partition_key}_changed?") && persisted? && !send("#{partition_key}_was").nil?
|
76
|
-
|
63
|
+
tenant_id
|
77
64
|
end
|
78
65
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
66
|
+
if MultiTenant.tenant_klass_defined?(tenant_name)
|
67
|
+
define_method "#{tenant_name}=" do |model|
|
68
|
+
super(model)
|
69
|
+
raise MultiTenant::TenantIsImmutable if send("#{partition_key}_changed?") && persisted? && !send("#{partition_key}_was").nil?
|
70
|
+
model
|
71
|
+
end
|
84
72
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
73
|
+
define_method "#{tenant_name}" do
|
74
|
+
if !MultiTenant.current_tenant_is_id? && MultiTenant.current_tenant_id && public_send(partition_key) == MultiTenant.current_tenant_id
|
75
|
+
return MultiTenant.current_tenant
|
76
|
+
else
|
77
|
+
super()
|
78
|
+
end
|
90
79
|
end
|
91
80
|
end
|
92
81
|
end
|
@@ -94,7 +83,7 @@ module MultiTenant
|
|
94
83
|
|
95
84
|
around_save -> (record, block) {
|
96
85
|
if persisted? && MultiTenant.current_tenant_id.nil?
|
97
|
-
MultiTenant.
|
86
|
+
MultiTenant.with(record.public_send(partition_key)) { block.call }
|
98
87
|
else
|
99
88
|
block.call
|
100
89
|
end
|
@@ -102,7 +91,7 @@ module MultiTenant
|
|
102
91
|
|
103
92
|
around_update -> (record, block) {
|
104
93
|
if MultiTenant.current_tenant_id.nil?
|
105
|
-
MultiTenant.
|
94
|
+
MultiTenant.with(record.public_send(partition_key)) { block.call }
|
106
95
|
else
|
107
96
|
block.call
|
108
97
|
end
|
@@ -110,7 +99,7 @@ module MultiTenant
|
|
110
99
|
|
111
100
|
around_destroy -> (record, block) {
|
112
101
|
if MultiTenant.current_tenant_id.nil?
|
113
|
-
MultiTenant.
|
102
|
+
MultiTenant.with(record.public_send(partition_key)) { block.call }
|
114
103
|
else
|
115
104
|
block.call
|
116
105
|
end
|
@@ -1,18 +1,12 @@
|
|
1
1
|
require 'request_store'
|
2
2
|
|
3
3
|
module MultiTenant
|
4
|
-
|
5
|
-
|
6
|
-
def self.set_tenant_klass(klass)
|
7
|
-
@@tenant_klass = klass
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.tenant_klass
|
11
|
-
@@tenant_klass
|
4
|
+
def self.tenant_klass_defined?(tenant_name)
|
5
|
+
!!tenant_name.to_s.classify.safe_constantize
|
12
6
|
end
|
13
7
|
|
14
|
-
def self.partition_key
|
15
|
-
"#{
|
8
|
+
def self.partition_key(tenant_name)
|
9
|
+
"#{tenant_name.to_s}_id"
|
16
10
|
end
|
17
11
|
|
18
12
|
# Workaroud to make "with_lock" work until https://github.com/citusdata/citus/issues/1236 is fixed
|
@@ -28,43 +22,28 @@ module MultiTenant
|
|
28
22
|
RequestStore.store[:current_tenant]
|
29
23
|
end
|
30
24
|
|
31
|
-
def self.current_tenant_id
|
32
|
-
|
25
|
+
def self.current_tenant_id
|
26
|
+
current_tenant_is_id? ? current_tenant : current_tenant.try(:id)
|
33
27
|
end
|
34
28
|
|
35
|
-
def self.
|
36
|
-
current_tenant.
|
29
|
+
def self.current_tenant_is_id?
|
30
|
+
current_tenant.is_a?(String) || current_tenant.is_a?(Integer)
|
37
31
|
end
|
38
32
|
|
39
33
|
def self.with(tenant, &block)
|
34
|
+
return block.call if self.current_tenant == tenant
|
40
35
|
old_tenant = self.current_tenant
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.current_tenant = old_tenant
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.with_id(tenant_id, &block)
|
50
|
-
if MultiTenant.current_tenant_id == tenant_id
|
51
|
-
block.call
|
52
|
-
else
|
53
|
-
MultiTenant.with(TenantIdWrapper.new(id: tenant_id), &block)
|
36
|
+
begin
|
37
|
+
self.current_tenant = tenant
|
38
|
+
return block.call
|
39
|
+
ensure
|
40
|
+
self.current_tenant = old_tenant
|
54
41
|
end
|
55
42
|
end
|
56
43
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
class TenantIdWrapper
|
61
|
-
attr_reader :id
|
44
|
+
# Preserve backward compatibility for people using .with_id
|
45
|
+
singleton_class.send(:alias_method, :with_id, :with)
|
62
46
|
|
63
|
-
|
64
|
-
@id = id
|
65
|
-
end
|
66
|
-
|
67
|
-
def new_record?; true; end
|
68
|
-
def touch; nil; end
|
47
|
+
class TenantIsImmutable < StandardError
|
69
48
|
end
|
70
49
|
end
|
@@ -56,6 +56,15 @@ describe MultiTenant do
|
|
56
56
|
it { expect(@custom_partition_key_task.account).to eq(@account) }
|
57
57
|
end
|
58
58
|
|
59
|
+
describe 'Tenant model not defined' do
|
60
|
+
before do
|
61
|
+
MultiTenant.current_tenant = 77
|
62
|
+
@partition_key_not_model_task = PartitionKeyNotModelTask.create! name: 'foo'
|
63
|
+
end
|
64
|
+
|
65
|
+
it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
|
66
|
+
end
|
67
|
+
|
59
68
|
# Scoping models
|
60
69
|
describe 'Project.all should be scoped to the current tenant if set' do
|
61
70
|
before do
|
@@ -77,7 +86,7 @@ describe MultiTenant do
|
|
77
86
|
before do
|
78
87
|
@account = Account.create! name: 'foo'
|
79
88
|
@project = @account.projects.create! name: 'foobar'
|
80
|
-
MultiTenant.current_tenant= @account1
|
89
|
+
MultiTenant.current_tenant = @account1
|
81
90
|
end
|
82
91
|
|
83
92
|
it { @project.account }
|
@@ -137,6 +146,40 @@ describe MultiTenant do
|
|
137
146
|
end
|
138
147
|
end
|
139
148
|
|
149
|
+
describe 'Subclass of Multi Tenant Model' do
|
150
|
+
let(:account) { Account.create!(name: 'foo') }
|
151
|
+
let(:project) { Project.create!(name: 'project', account: account) }
|
152
|
+
let(:task) { project.tasks.create!(name: 'task') }
|
153
|
+
let(:sti_task) { StiSubTask.create!(task: task, name: 'sub task') }
|
154
|
+
|
155
|
+
it 'has partition key' do
|
156
|
+
expect(StiSubTask.partition_key).to eq 'account_id'
|
157
|
+
expect(StiSubTask.instance_variable_get(:@partition_key)).to eq 'account_id'
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'has primary key' do
|
161
|
+
expect(StiSubTask.primary_key).to eq 'id'
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'handles belongs_to through' do
|
165
|
+
MultiTenant.with(account) do
|
166
|
+
expect(sti_task.project).to eq project
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'handles has_many through' do
|
171
|
+
MultiTenant.with(account) do
|
172
|
+
expect(project.sub_tasks).to eq [sti_task]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'handles unscoped' do
|
177
|
+
MultiTenant.with(account) do
|
178
|
+
expect(StiSubTask.unscoped.find(sti_task.id)).to eq sti_task
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
140
183
|
# ::with
|
141
184
|
describe "::with" do
|
142
185
|
it "should set current_tenant to the specified tenant inside the block" do
|
data/spec/schema.rb
CHANGED
@@ -28,6 +28,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
28
28
|
t.column :account_id, :integer
|
29
29
|
t.column :name, :string
|
30
30
|
t.column :task_id, :integer
|
31
|
+
t.column :type, :string
|
31
32
|
end
|
32
33
|
|
33
34
|
create_table :countries, force: true do |t|
|
@@ -55,6 +56,11 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
55
56
|
t.column :commentable_type, :string
|
56
57
|
end
|
57
58
|
|
59
|
+
create_table :partition_key_not_model_tasks, force: true, partition_key: :non_model_id do |t|
|
60
|
+
t.column :non_model_id, :integer
|
61
|
+
t.column :name, :string
|
62
|
+
end
|
63
|
+
|
58
64
|
create_distributed_table :accounts, :id
|
59
65
|
create_distributed_table :projects, :account_id
|
60
66
|
create_distributed_table :managers, :account_id
|
@@ -63,6 +69,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
63
69
|
create_distributed_table :aliased_tasks, :account_id
|
64
70
|
create_distributed_table :custom_partition_key_tasks, :accountID
|
65
71
|
create_distributed_table :comments, :account_id
|
72
|
+
create_distributed_table :partition_key_not_model_tasks, :non_model_id
|
66
73
|
end
|
67
74
|
|
68
75
|
class Account < ActiveRecord::Base
|
@@ -104,6 +111,9 @@ class SubTask < ActiveRecord::Base
|
|
104
111
|
has_one :project, through: :task
|
105
112
|
end
|
106
113
|
|
114
|
+
class StiSubTask < SubTask
|
115
|
+
end
|
116
|
+
|
107
117
|
class UnscopedModel < ActiveRecord::Base
|
108
118
|
validates_uniqueness_of :name
|
109
119
|
end
|
@@ -123,6 +133,10 @@ class CustomPartitionKeyTask < ActiveRecord::Base
|
|
123
133
|
end
|
124
134
|
end
|
125
135
|
|
136
|
+
class PartitionKeyNotModelTask < ActiveRecord::Base
|
137
|
+
multi_tenant :non_model
|
138
|
+
end
|
139
|
+
|
126
140
|
class Comment < ActiveRecord::Base
|
127
141
|
multi_tenant :account
|
128
142
|
belongs_to :commentable, polymorphic: true
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-multi-tenant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Citus Data
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: request_store
|