activerecord-multi-tenant 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -3
- data/activerecord-multi-tenant.gemspec +0 -1
- data/docker-compose.yml +3 -3
- data/gemfiles/active_record_5.1.gemfile.lock +0 -2
- data/gemfiles/rails_3.2.gemfile.lock +0 -2
- data/gemfiles/rails_4.0.gemfile.lock +0 -2
- data/gemfiles/rails_4.1.gemfile.lock +0 -2
- data/gemfiles/rails_4.2.gemfile.lock +0 -2
- data/gemfiles/rails_5.0.gemfile.lock +0 -2
- data/gemfiles/rails_5.1.gemfile.lock +0 -2
- data/lib/activerecord-multi-tenant/fast_truncate.rb +3 -1
- data/lib/activerecord-multi-tenant/model_extensions.rb +2 -0
- data/lib/activerecord-multi-tenant/multi_tenant.rb +10 -1
- data/lib/activerecord-multi-tenant/query_rewriter.rb +34 -30
- data/lib/activerecord-multi-tenant/sidekiq.rb +0 -1
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +16 -0
- data/spec/activerecord-multi-tenant/record_modifications_spec.rb +7 -7
- data/spec/database.yml +2 -0
- data/spec/spec_helper.rb +2 -8
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3923b91001e63778ed81b5d797c2204857ef31d8
|
4
|
+
data.tar.gz: 8b4c76c828bcfa5b29d80fb0c7e8b4bdbb0ff7c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 259c8320b79b56b232a3d981609b3419757cac0315087350e27c5c9613852c899487d773fd3fb56f4a684cad7fc7633fa208b0b3b203e0331c3b78400f83decc
|
7
|
+
data.tar.gz: 7ec81cf673bf8eae9e53267c65222d104421b2cee3df0d12e72fe5d12f8723da069617ced3b74c75651a4fd5aa2d091d1bd6e0437492ec959b472a88d1bfeffd
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.6.0 2017-06-09
|
4
|
+
|
5
|
+
* Query rewriter
|
6
|
+
* Change hook from per-relation to be pre-SQL statement thats being output
|
7
|
+
- This should resolve issues where we added conditions in the wrong place
|
8
|
+
* Use table name to model klass registry
|
9
|
+
* Improve tests for activerecord-multi-tenant
|
10
|
+
* Use lower shard count to speed up tests
|
11
|
+
* Drop database cleaner dependency
|
12
|
+
|
13
|
+
|
3
14
|
## 0.5.0 2017-05-08
|
4
15
|
|
5
16
|
* Write-only mode that enables step-by-step migrations
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
activerecord-multi-tenant (0.
|
4
|
+
activerecord-multi-tenant (0.6.0)
|
5
5
|
rails (>= 3.1)
|
6
6
|
request_store (>= 1.0.5)
|
7
7
|
|
@@ -52,7 +52,6 @@ GEM
|
|
52
52
|
arel (7.1.4)
|
53
53
|
builder (3.2.2)
|
54
54
|
concurrent-ruby (1.0.4)
|
55
|
-
database_cleaner (1.3.0)
|
56
55
|
diff-lcs (1.2.5)
|
57
56
|
erubis (2.7.0)
|
58
57
|
globalid (0.4.0)
|
@@ -142,7 +141,6 @@ PLATFORMS
|
|
142
141
|
DEPENDENCIES
|
143
142
|
activerecord-multi-tenant!
|
144
143
|
appraisal
|
145
|
-
database_cleaner (~> 1.3.0)
|
146
144
|
pg
|
147
145
|
rake
|
148
146
|
rspec (>= 3.0)
|
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
s.add_development_dependency 'rspec', '>= 3.0'
|
22
22
|
s.add_development_dependency 'rspec-rails'
|
23
|
-
s.add_development_dependency 'database_cleaner', '~> 1.3.0'
|
24
23
|
s.add_development_dependency 'pg'
|
25
24
|
s.add_development_dependency 'rake'
|
26
25
|
s.add_development_dependency 'thor'
|
data/docker-compose.yml
CHANGED
@@ -2,15 +2,15 @@ version: '2'
|
|
2
2
|
|
3
3
|
services:
|
4
4
|
master:
|
5
|
-
image: 'citusdata/citus:6.1
|
5
|
+
image: 'citusdata/citus:6.2.1'
|
6
6
|
ports: ['5600:5432']
|
7
7
|
labels: ['com.citusdata.role=Master']
|
8
8
|
volumes: ['/var/run/postgresql']
|
9
9
|
worker1:
|
10
|
-
image: 'citusdata/citus:6.1
|
10
|
+
image: 'citusdata/citus:6.2.1'
|
11
11
|
labels: ['com.citusdata.role=Worker']
|
12
12
|
worker2:
|
13
|
-
image: 'citusdata/citus:6.1
|
13
|
+
image: 'citusdata/citus:6.2.1'
|
14
14
|
labels: ['com.citusdata.role=Worker']
|
15
15
|
config:
|
16
16
|
image: 'citusdata/workerlist-gen:2.0.0'
|
@@ -52,7 +52,6 @@ GEM
|
|
52
52
|
arel (8.0.0)
|
53
53
|
builder (3.2.3)
|
54
54
|
concurrent-ruby (1.0.5)
|
55
|
-
database_cleaner (1.3.0)
|
56
55
|
diff-lcs (1.3)
|
57
56
|
erubi (1.6.0)
|
58
57
|
globalid (0.4.0)
|
@@ -143,7 +142,6 @@ DEPENDENCIES
|
|
143
142
|
activerecord (= 5.1.0)
|
144
143
|
activerecord-multi-tenant!
|
145
144
|
appraisal
|
146
|
-
database_cleaner (~> 1.3.0)
|
147
145
|
pg
|
148
146
|
rake
|
149
147
|
rspec (>= 3.0)
|
@@ -41,7 +41,6 @@ GEM
|
|
41
41
|
thor (>= 0.14.0)
|
42
42
|
arel (3.0.3)
|
43
43
|
builder (3.0.4)
|
44
|
-
database_cleaner (1.3.0)
|
45
44
|
diff-lcs (1.3)
|
46
45
|
erubis (2.7.0)
|
47
46
|
hike (1.2.3)
|
@@ -123,7 +122,6 @@ PLATFORMS
|
|
123
122
|
DEPENDENCIES
|
124
123
|
activerecord-multi-tenant!
|
125
124
|
appraisal
|
126
|
-
database_cleaner (~> 1.3.0)
|
127
125
|
pg
|
128
126
|
rails (= 3.2.22.5)
|
129
127
|
rake
|
@@ -39,7 +39,6 @@ GEM
|
|
39
39
|
arel (4.0.2)
|
40
40
|
builder (3.1.4)
|
41
41
|
concurrent-ruby (1.0.5)
|
42
|
-
database_cleaner (1.3.0)
|
43
42
|
diff-lcs (1.3)
|
44
43
|
erubis (2.7.0)
|
45
44
|
i18n (0.8.1)
|
@@ -107,7 +106,6 @@ PLATFORMS
|
|
107
106
|
DEPENDENCIES
|
108
107
|
activerecord-multi-tenant!
|
109
108
|
appraisal
|
110
|
-
database_cleaner (~> 1.3.0)
|
111
109
|
pg
|
112
110
|
rails (= 4.0.13)
|
113
111
|
rake
|
@@ -41,7 +41,6 @@ GEM
|
|
41
41
|
arel (5.0.1.20140414130214)
|
42
42
|
builder (3.2.3)
|
43
43
|
concurrent-ruby (1.0.5)
|
44
|
-
database_cleaner (1.3.0)
|
45
44
|
diff-lcs (1.3)
|
46
45
|
erubis (2.7.0)
|
47
46
|
i18n (0.8.1)
|
@@ -112,7 +111,6 @@ PLATFORMS
|
|
112
111
|
DEPENDENCIES
|
113
112
|
activerecord-multi-tenant!
|
114
113
|
appraisal
|
115
|
-
database_cleaner (~> 1.3.0)
|
116
114
|
pg
|
117
115
|
rails (= 4.1.16)
|
118
116
|
rake
|
@@ -49,7 +49,6 @@ GEM
|
|
49
49
|
arel (6.0.4)
|
50
50
|
builder (3.2.3)
|
51
51
|
concurrent-ruby (1.0.5)
|
52
|
-
database_cleaner (1.3.0)
|
53
52
|
diff-lcs (1.3)
|
54
53
|
erubis (2.7.0)
|
55
54
|
globalid (0.3.7)
|
@@ -135,7 +134,6 @@ PLATFORMS
|
|
135
134
|
DEPENDENCIES
|
136
135
|
activerecord-multi-tenant!
|
137
136
|
appraisal
|
138
|
-
database_cleaner (~> 1.3.0)
|
139
137
|
pg
|
140
138
|
rails (= 4.2.8)
|
141
139
|
rake
|
@@ -52,7 +52,6 @@ GEM
|
|
52
52
|
arel (7.1.4)
|
53
53
|
builder (3.2.3)
|
54
54
|
concurrent-ruby (1.0.5)
|
55
|
-
database_cleaner (1.3.0)
|
56
55
|
diff-lcs (1.3)
|
57
56
|
erubis (2.7.0)
|
58
57
|
globalid (0.3.7)
|
@@ -142,7 +141,6 @@ PLATFORMS
|
|
142
141
|
DEPENDENCIES
|
143
142
|
activerecord-multi-tenant!
|
144
143
|
appraisal
|
145
|
-
database_cleaner (~> 1.3.0)
|
146
144
|
pg
|
147
145
|
rails (= 5.0.1)
|
148
146
|
rake
|
@@ -52,7 +52,6 @@ GEM
|
|
52
52
|
arel (8.0.0)
|
53
53
|
builder (3.2.3)
|
54
54
|
concurrent-ruby (1.0.5)
|
55
|
-
database_cleaner (1.3.0)
|
56
55
|
diff-lcs (1.3)
|
57
56
|
erubi (1.6.0)
|
58
57
|
globalid (0.4.0)
|
@@ -142,7 +141,6 @@ PLATFORMS
|
|
142
141
|
DEPENDENCIES
|
143
142
|
activerecord-multi-tenant!
|
144
143
|
appraisal
|
145
|
-
database_cleaner (~> 1.3.0)
|
146
144
|
pg
|
147
145
|
rails (= 5.1.0)
|
148
146
|
rake
|
@@ -25,7 +25,9 @@ module MultiTenant
|
|
25
25
|
END IF;
|
26
26
|
END LOOP;
|
27
27
|
|
28
|
-
|
28
|
+
IF array_length(tables, 1) > 0 THEN
|
29
|
+
EXECUTE 'TRUNCATE TABLE ' || array_to_string(tables, ', ') || ' RESTART IDENTITY CASCADE';
|
30
|
+
END IF;
|
29
31
|
END$$;), exclude.map { |t| "'" + t + "'" }.join('\n'))
|
30
32
|
end
|
31
33
|
end
|
@@ -11,7 +11,7 @@ module MultiTenant
|
|
11
11
|
|
12
12
|
# In some cases we only have an ID - if defined we'll return the default tenant class in such cases
|
13
13
|
def self.default_tenant_class=(tenant_class); @@default_tenant_class = tenant_class; end
|
14
|
-
def self.default_tenant_class; @@default_tenant_class; end
|
14
|
+
def self.default_tenant_class; @@default_tenant_class ||= nil; end
|
15
15
|
|
16
16
|
# Write-only Mode - this only adds the tenant_id to new records, but doesn't
|
17
17
|
# require its presence for SELECTs/UPDATEs/DELETEs
|
@@ -23,6 +23,15 @@ module MultiTenant
|
|
23
23
|
def self.enable_with_lock_workaround; @@enable_with_lock_workaround = true; end
|
24
24
|
def self.with_lock_workaround_enabled?; @@enable_with_lock_workaround; end
|
25
25
|
|
26
|
+
# Registry that maps table names to models (used by the query rewriter)
|
27
|
+
def self.register_multi_tenant_model(table_name, model_klass)
|
28
|
+
@@multi_tenant_models ||= {}
|
29
|
+
@@multi_tenant_models[table_name.to_s] = model_klass
|
30
|
+
end
|
31
|
+
def self.multi_tenant_model_for_table(table_name)
|
32
|
+
@@multi_tenant_models[table_name.to_s]
|
33
|
+
end
|
34
|
+
|
26
35
|
def self.current_tenant=(tenant)
|
27
36
|
RequestStore.store[:current_tenant] = tenant
|
28
37
|
end
|
@@ -1,47 +1,51 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module MultiTenant
|
4
|
+
class ArelTenantVisitor < Arel::Visitors::DepthFirst
|
5
|
+
def initialize(arel)
|
6
|
+
super(Proc.new {})
|
7
|
+
@tenant_relations = []
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
accept(arel.ast)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
def tenant_relations
|
13
|
+
@tenant_relations.uniq
|
14
|
+
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
def visit_Arel_Table(table, _collector = nil)
|
17
|
+
@tenant_relations << table if tenant_relation?(table)
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def visit_Arel_Nodes_TableAlias(table_alias, _collector = nil)
|
21
|
+
@tenant_relations << table_alias if tenant_relation?(table_alias.left)
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
+
private
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
def tenant_relation?(table)
|
27
|
+
MultiTenant.multi_tenant_model_for_table(table.name).present?
|
28
|
+
end
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
module ActiveRecord
|
32
|
-
module
|
33
|
-
|
34
|
-
|
35
|
-
arel
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
arel.
|
33
|
+
module ConnectionAdapters # :nodoc:
|
34
|
+
module DatabaseStatements
|
35
|
+
alias :to_sql_orig :to_sql
|
36
|
+
# Converts an arel AST to SQL
|
37
|
+
def to_sql(arel, binds = [])
|
38
|
+
if MultiTenant.current_tenant_id && !MultiTenant.with_write_only_mode_enabled? &&
|
39
|
+
[Arel::SelectManager, Arel::UpdateManager, Arel::DeleteManager, ActiveRecord::Relation].include?(arel.class)
|
40
|
+
relations_needing_tenant_id = MultiTenant::ArelTenantVisitor.new(arel).tenant_relations
|
41
|
+
arel = relations_needing_tenant_id.reduce(arel) do |arel, relation|
|
42
|
+
model = MultiTenant.multi_tenant_model_for_table(relation.table_name)
|
43
|
+
next arel unless model.present?
|
44
|
+
arel.where(relation[model.partition_key].eq(MultiTenant.current_tenant_id))
|
45
|
+
end
|
41
46
|
end
|
47
|
+
to_sql_orig(arel, binds)
|
42
48
|
end
|
43
|
-
|
44
|
-
arel
|
45
49
|
end
|
46
50
|
end
|
47
51
|
end
|
@@ -146,6 +146,22 @@ describe MultiTenant do
|
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
149
|
+
describe 'eager loading' do
|
150
|
+
let(:account) { Account.create!(name: 'foo') }
|
151
|
+
let(:project) { Project.create!(name: 'project', account: account) }
|
152
|
+
let(:manager) { Manager.create!(name: 'manager', account: account, project: project) }
|
153
|
+
let(:task) { project.tasks.create!(name: 'task') }
|
154
|
+
let(:sub_task) { task.sub_tasks.create!(name: 'sub task') }
|
155
|
+
|
156
|
+
it 'handles table aliases through joins' do
|
157
|
+
MultiTenant.with(account) do
|
158
|
+
sub_task
|
159
|
+
manager
|
160
|
+
expect(Project.eager_load([{manager: :project}, {tasks: :project}]).first).to eq project
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
149
165
|
describe 'Subclass of Multi Tenant Model' do
|
150
166
|
let(:account) { Account.create!(name: 'foo') }
|
151
167
|
let(:project) { Project.create!(name: 'project', account: account) }
|
@@ -4,18 +4,18 @@ describe MultiTenant, 'Record modifications' do
|
|
4
4
|
let(:account) { Account.create! name: 'test' }
|
5
5
|
let(:project) { account.projects.create! name: 'something' }
|
6
6
|
|
7
|
-
it 'includes the tenant_id in
|
8
|
-
project.
|
9
|
-
project.save!
|
7
|
+
it 'includes the tenant_id in DELETEs' do
|
8
|
+
project.destroy
|
10
9
|
MultiTenant.with(account) do
|
11
|
-
expect(Project.
|
10
|
+
expect(Project.where(id: project.id).first).not_to be_present
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
|
-
it 'includes the tenant_id in
|
16
|
-
project.
|
14
|
+
it 'includes the tenant_id in UPDATEs' do
|
15
|
+
project.name = 'something else'
|
16
|
+
project.save!
|
17
17
|
MultiTenant.with(account) do
|
18
|
-
expect(Project.
|
18
|
+
expect(Project.find(project.id).name).to eq 'something else'
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/spec/database.yml
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -4,7 +4,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
4
4
|
require 'active_record/railtie'
|
5
5
|
require 'action_controller/railtie'
|
6
6
|
require 'rspec/rails'
|
7
|
-
require 'database_cleaner'
|
8
7
|
|
9
8
|
require 'activerecord-multi-tenant'
|
10
9
|
|
@@ -22,19 +21,14 @@ RSpec.configure do |config|
|
|
22
21
|
end
|
23
22
|
|
24
23
|
config.before(:suite) do
|
25
|
-
|
26
|
-
DatabaseCleaner[:active_record].clean
|
24
|
+
MultiTenant::FastTruncate.run
|
27
25
|
|
28
26
|
# Keep this here until https://github.com/citusdata/citus/issues/1236 is fixed
|
29
27
|
MultiTenant.enable_with_lock_workaround
|
30
28
|
end
|
31
29
|
|
32
|
-
config.before(:each) do
|
33
|
-
DatabaseCleaner[:active_record].start
|
34
|
-
end
|
35
|
-
|
36
30
|
config.after(:each) do
|
37
|
-
|
31
|
+
MultiTenant::FastTruncate.run
|
38
32
|
end
|
39
33
|
end
|
40
34
|
|
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.6.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-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: request_store
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: database_cleaner
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 1.3.0
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 1.3.0
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: pg
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
179
|
version: '0'
|
194
180
|
requirements: []
|
195
181
|
rubyforge_project:
|
196
|
-
rubygems_version: 2.
|
182
|
+
rubygems_version: 2.5.1
|
197
183
|
signing_key:
|
198
184
|
specification_version: 4
|
199
185
|
summary: ActiveRecord/Rails integration for multi-tenant databases, in particular
|