activerecord-multi-tenant 1.2.0 → 2.4.0
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 +4 -4
- data/.github/workflows/active-record-multi-tenant-tests.yml +83 -0
- data/.gitignore +6 -0
- data/.readthedocs.yaml +15 -0
- data/.rspec +0 -0
- data/.rubocop.yml +51 -0
- data/Appraisals +6 -22
- data/CHANGELOG.md +51 -0
- data/Gemfile +3 -1
- data/LICENSE +0 -0
- data/README.md +3 -2
- data/Rakefile +1 -1
- data/activerecord-multi-tenant.gemspec +28 -23
- data/docker-compose.yml +24 -18
- data/docs/.gitignore +3 -0
- data/docs/Makefile +28 -0
- data/docs/api-reference.sh +10 -0
- data/docs/requirements.in +4 -0
- data/docs/requirements.txt +62 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations/Association.html +285 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations/ClassMethods.html +255 -0
- data/docs/source/_static/api-reference/ActiveRecord/Associations.html +117 -0
- data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters/SchemaStatements.html +232 -0
- data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters.html +126 -0
- data/docs/source/_static/api-reference/ActiveRecord/QueryMethods.html +336 -0
- data/docs/source/_static/api-reference/ActiveRecord/SchemaDumper.html +121 -0
- data/docs/source/_static/api-reference/ActiveRecord.html +130 -0
- data/docs/source/_static/api-reference/MultiTenant/ArelTenantVisitor.html +755 -0
- data/docs/source/_static/api-reference/MultiTenant/ArelVisitorsDepthFirst.html +208 -0
- data/docs/source/_static/api-reference/MultiTenant/BaseTenantEnforcementClause.html +462 -0
- data/docs/source/_static/api-reference/MultiTenant/Context.html +659 -0
- data/docs/source/_static/api-reference/MultiTenant/ControllerExtensions.html +202 -0
- data/docs/source/_static/api-reference/MultiTenant/CopyFromClient.html +186 -0
- data/docs/source/_static/api-reference/MultiTenant/CopyFromClientHelper.html +362 -0
- data/docs/source/_static/api-reference/MultiTenant/Current.html +124 -0
- data/docs/source/_static/api-reference/MultiTenant/DatabaseStatements.html +366 -0
- data/docs/source/_static/api-reference/MultiTenant/FastTruncate.html +226 -0
- data/docs/source/_static/api-reference/MultiTenant/MigrationExtensions.html +554 -0
- data/docs/source/_static/api-reference/MultiTenant/MissingTenantError.html +124 -0
- data/docs/source/_static/api-reference/MultiTenant/ModelExtensionsClassMethods.html +492 -0
- data/docs/source/_static/api-reference/MultiTenant/QueryMonitor.html +257 -0
- data/docs/source/_static/api-reference/MultiTenant/Table.html +419 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantEnforcementClause.html +148 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantIsImmutable.html +135 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantJoinEnforcementClause.html +310 -0
- data/docs/source/_static/api-reference/MultiTenant/TenantValueVisitor.html +239 -0
- data/docs/source/_static/api-reference/MultiTenant.html +1454 -0
- data/docs/source/_static/api-reference/MultiTenantFindBy.html +180 -0
- data/docs/source/_static/api-reference/Sidekiq/Client.html +302 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Client.html +217 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Server.html +219 -0
- data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant.html +126 -0
- data/docs/source/_static/api-reference/Sidekiq.html +126 -0
- data/docs/source/_static/api-reference/_index.html +399 -0
- data/docs/source/_static/api-reference/class_list.html +51 -0
- data/docs/source/_static/api-reference/css/common.css +1 -0
- data/docs/source/_static/api-reference/css/full_list.css +58 -0
- data/docs/source/_static/api-reference/css/style.css +497 -0
- data/docs/source/_static/api-reference/file.README.html +167 -0
- data/docs/source/_static/api-reference/file_list.html +56 -0
- data/docs/source/_static/api-reference/frames.html +17 -0
- data/docs/source/_static/api-reference/index.html +167 -0
- data/docs/source/_static/api-reference/js/app.js +314 -0
- data/docs/source/_static/api-reference/js/full_list.js +216 -0
- data/docs/source/_static/api-reference/js/jquery.js +4 -0
- data/docs/source/_static/api-reference/method_list.html +715 -0
- data/docs/source/_static/api-reference/top-level-namespace.html +126 -0
- data/docs/source/_templates/.gitignore +4 -0
- data/docs/source/api-reference.rst +8 -0
- data/docs/source/appendix.rst +26 -0
- data/docs/source/changelog.rst +8 -0
- data/docs/source/community-and-support.rst +26 -0
- data/docs/source/conf.py +30 -0
- data/docs/source/contributing.rst +70 -0
- data/docs/source/getting-started.rst +37 -0
- data/docs/source/guides-and-tutorials.rst +129 -0
- data/docs/source/index.rst +54 -0
- data/docs/source/introduction.rst +33 -0
- data/docs/source/license.rst +22 -0
- data/docs/source/troubleshooting.rst +41 -0
- data/docs/source/usage-guide.rst +59 -0
- data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +183 -174
- data/lib/activerecord-multi-tenant/controller_extensions.rb +15 -4
- data/lib/activerecord-multi-tenant/copy_from_client.rb +4 -0
- data/lib/activerecord-multi-tenant/fast_truncate.rb +4 -2
- data/lib/activerecord-multi-tenant/habtm.rb +50 -0
- data/lib/activerecord-multi-tenant/migrations.rb +87 -10
- data/lib/activerecord-multi-tenant/model_extensions.rb +98 -34
- data/lib/activerecord-multi-tenant/multi_tenant.rb +102 -29
- data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
- data/lib/activerecord-multi-tenant/query_rewriter.rb +122 -91
- data/lib/activerecord-multi-tenant/sidekiq.rb +46 -19
- data/lib/activerecord-multi-tenant/table_node.rb +13 -0
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/lib/activerecord-multi-tenant.rb +3 -13
- data/lib/activerecord_multi_tenant.rb +13 -0
- data/spec/activerecord-multi-tenant/associations_spec.rb +42 -0
- data/spec/activerecord-multi-tenant/controller_extensions_spec.rb +3 -2
- data/spec/activerecord-multi-tenant/fast_truncate_spec.rb +8 -6
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +347 -143
- data/spec/activerecord-multi-tenant/multi_tenant_spec.rb +69 -13
- data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +60 -59
- data/spec/activerecord-multi-tenant/record_callback_spec.rb +0 -0
- data/spec/activerecord-multi-tenant/record_finding_spec.rb +11 -11
- data/spec/activerecord-multi-tenant/record_modifications_spec.rb +23 -4
- data/spec/activerecord-multi-tenant/sidekiq_spec.rb +10 -10
- data/spec/database.yml +0 -0
- data/spec/schema.rb +43 -2
- data/spec/spec_helper.rb +52 -16
- data/spec/support/format_sql.rb +20 -0
- metadata +126 -36
- data/.github/workflows/CI.yml +0 -63
- data/gemfiles/.bundle/config +0 -2
- data/gemfiles/active_record_5.2.gemfile +0 -16
- data/gemfiles/active_record_6.0.gemfile +0 -8
- data/gemfiles/active_record_6.1.gemfile +0 -8
- data/gemfiles/active_record_7.0.gemfile +0 -8
- data/gemfiles/rails_5.2.gemfile +0 -16
- data/gemfiles/rails_6.0.gemfile +0 -8
- data/gemfiles/rails_6.1.gemfile +0 -8
- data/gemfiles/rails_7.0.gemfile +0 -8
- data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
- data/lib/activerecord-multi-tenant/with_lock.rb +0 -15
- data/spec/activerecord-multi-tenant/schema_dumper_tester.rb +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe MultiTenant do
|
@@ -10,11 +12,11 @@ describe MultiTenant do
|
|
10
12
|
end
|
11
13
|
|
12
14
|
describe 'is_scoped_as_tenant should return the correct value when true' do
|
13
|
-
it {expect(Project.respond_to?(:scoped_by_tenant?)).to eq(true)}
|
15
|
+
it { expect(Project.respond_to?(:scoped_by_tenant?)).to eq(true) }
|
14
16
|
end
|
15
17
|
|
16
18
|
describe 'is_scoped_as_tenant should return the correct value when false' do
|
17
|
-
it {expect(UnscopedModel.respond_to?(:scoped_by_tenant?)).to eq(false)}
|
19
|
+
it { expect(UnscopedModel.respond_to?(:scoped_by_tenant?)).to eq(false) }
|
18
20
|
end
|
19
21
|
|
20
22
|
context 'immutability' do
|
@@ -43,17 +45,17 @@ describe MultiTenant do
|
|
43
45
|
@account = Account.create! name: 'foo'
|
44
46
|
MultiTenant.current_tenant = @account
|
45
47
|
end
|
46
|
-
it {expect(Project.new.account_id).to eq(@account.id)}
|
48
|
+
it { expect(Project.new.account_id).to eq(@account.id) }
|
47
49
|
it 'should handle partial selects' do
|
48
50
|
project = Project.create!
|
49
|
-
expect{project = Project.select(:name).find(project.id)}.not_to raise_error
|
51
|
+
expect { project = Project.select(:name).find(project.id) }.not_to raise_error
|
50
52
|
expect(project.account_id).to eq(@account.id)
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
describe 'Handles custom partition_key on tenant model' do
|
55
57
|
before do
|
56
|
-
@account
|
58
|
+
@account = Account.create! name: 'foo'
|
57
59
|
MultiTenant.current_tenant = @account
|
58
60
|
@custom_partition_key_task = CustomPartitionKeyTask.create! name: 'foo'
|
59
61
|
end
|
@@ -70,6 +72,72 @@ describe MultiTenant do
|
|
70
72
|
it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
|
71
73
|
end
|
72
74
|
|
75
|
+
describe 'Tenant model with a nonstandard class name' do
|
76
|
+
let(:account_klass) do
|
77
|
+
Class.new(ActiveRecord::Base) do
|
78
|
+
self.table_name = 'account'
|
79
|
+
|
80
|
+
def self.name
|
81
|
+
'UserAccount'
|
82
|
+
end
|
83
|
+
|
84
|
+
multi_tenant(:account)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
it 'does not register the tenant model' do
|
88
|
+
expect(MultiTenant).not_to receive(:register_multi_tenant_model)
|
89
|
+
account_klass
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'Changes table_name after multi_tenant called' do
|
94
|
+
before do
|
95
|
+
account_klass.has_many(:posts, anonymous_class: post_klass)
|
96
|
+
post_klass.belongs_to(:account, anonymous_class: account_klass)
|
97
|
+
|
98
|
+
@account1 = account_klass.create! name: 'foo'
|
99
|
+
@account2 = account_klass.create! name: 'bar'
|
100
|
+
|
101
|
+
@post1 = @account1.posts.create! name: 'foobar'
|
102
|
+
@post2 = @account2.posts.create! name: 'baz'
|
103
|
+
|
104
|
+
MultiTenant.current_tenant = @account1
|
105
|
+
@posts = post_klass.all
|
106
|
+
end
|
107
|
+
|
108
|
+
let(:account_klass) do
|
109
|
+
Class.new(Account) do
|
110
|
+
def self.name
|
111
|
+
'Account'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:post_klass) do
|
117
|
+
Class.new(ActiveRecord::Base) do
|
118
|
+
self.table_name = 'unknown'
|
119
|
+
|
120
|
+
multi_tenant(:account)
|
121
|
+
|
122
|
+
self.table_name = 'posts'
|
123
|
+
|
124
|
+
def self.name
|
125
|
+
'Post'
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it { expect(@posts.length).to eq(1) }
|
131
|
+
it { expect(@posts).to eq([@post1]) }
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'inspect method filters senstive column values' do
|
135
|
+
it 'filters senstive value' do
|
136
|
+
account = Account.new(name: 'foo', password: 'baz')
|
137
|
+
expect(account.inspect).to eq '#<Account id: nil, name: nil, subdomain: nil, domain: nil, password: [FILTERED]>'
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
73
141
|
# Scoping models
|
74
142
|
describe 'Project.all should be scoped to the current tenant if set' do
|
75
143
|
before do
|
@@ -112,16 +180,16 @@ describe MultiTenant do
|
|
112
180
|
end
|
113
181
|
end
|
114
182
|
|
115
|
-
describe
|
183
|
+
describe 'It should be possible to use aliased associations' do
|
116
184
|
before do
|
117
185
|
@account = Account.create! name: 'baz'
|
118
186
|
MultiTenant.current_tenant = @account
|
119
187
|
end
|
120
188
|
|
121
|
-
it { expect(AliasedTask.create(:
|
189
|
+
it { expect(AliasedTask.create(name: 'foo', project_alias: @project2).valid?).to eq(true) }
|
122
190
|
end
|
123
191
|
|
124
|
-
describe
|
192
|
+
describe 'It should be possible to use associations with partition_key from polymorphic' do
|
125
193
|
before do
|
126
194
|
@account = Account.create!(name: 'foo')
|
127
195
|
MultiTenant.current_tenant = @account
|
@@ -144,6 +212,22 @@ describe MultiTenant do
|
|
144
212
|
end
|
145
213
|
end
|
146
214
|
|
215
|
+
it 'handles belongs_to with optional: true' do
|
216
|
+
record = OptionalSubTask.create(sub_task_id: sub_task.id)
|
217
|
+
expect(record.reload.sub_task).to eq(sub_task)
|
218
|
+
expect(record.account_id).to eq(nil)
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'handles changing tenant from nil to a value' do
|
222
|
+
record = OptionalSubTask.create(sub_task_id: sub_task.id)
|
223
|
+
expect(record.reload.sub_task).to eq(sub_task)
|
224
|
+
expect(record.account_id).to eq(nil)
|
225
|
+
|
226
|
+
record.account = account
|
227
|
+
record.save!
|
228
|
+
expect(record.reload.account_id).to eq(account.id)
|
229
|
+
end
|
230
|
+
|
147
231
|
it 'handles has_many through' do
|
148
232
|
MultiTenant.with(account) do
|
149
233
|
expect(project.sub_tasks).to eq [sub_task]
|
@@ -162,7 +246,7 @@ describe MultiTenant do
|
|
162
246
|
MultiTenant.with(account) do
|
163
247
|
sub_task
|
164
248
|
manager
|
165
|
-
expect(Project.eager_load([{manager: :project}, {tasks: :project}]).first).to eq project
|
249
|
+
expect(Project.eager_load([{ manager: :project }, { tasks: :project }]).first).to eq project
|
166
250
|
end
|
167
251
|
end
|
168
252
|
end
|
@@ -173,7 +257,8 @@ describe MultiTenant do
|
|
173
257
|
|
174
258
|
it 'rewrites sub-selects correctly' do
|
175
259
|
MultiTenant.with(account) do
|
176
|
-
expect(Project.where(id: Project.where(id: project.id))
|
260
|
+
expect(Project.where(id: Project.where(id: project.id))
|
261
|
+
.where(id: Project.where(id: project.id)).first).to eq project
|
177
262
|
end
|
178
263
|
end
|
179
264
|
end
|
@@ -202,70 +287,132 @@ describe MultiTenant do
|
|
202
287
|
end
|
203
288
|
|
204
289
|
describe 'non-STI Subclass of abstract Multi Tenant Model' do
|
205
|
-
let(:
|
206
|
-
let(:
|
290
|
+
let(:tenant_id1) { 42 }
|
291
|
+
let(:tenant_id2) { 314_158 }
|
207
292
|
let(:name) { 'fooname' }
|
208
|
-
let(:
|
209
|
-
MultiTenant.with(
|
293
|
+
let(:subclass_task1) do
|
294
|
+
MultiTenant.with(tenant_id1) { SubclassTask.create! name: name }
|
210
295
|
end
|
211
|
-
let(:
|
212
|
-
MultiTenant.with(
|
296
|
+
let(:subclass_task2) do
|
297
|
+
MultiTenant.with(tenant_id2) { SubclassTask.create! name: name }
|
213
298
|
end
|
214
299
|
|
215
300
|
before do
|
216
|
-
|
217
|
-
|
301
|
+
subclass_task1
|
302
|
+
subclass_task2
|
218
303
|
end
|
219
304
|
|
220
305
|
it 'injects tenant_id on create' do
|
221
|
-
expect(
|
222
|
-
expect(
|
306
|
+
expect(subclass_task1.non_model_id).to be tenant_id1
|
307
|
+
expect(subclass_task2.non_model_id).to be tenant_id2
|
223
308
|
end
|
224
309
|
|
225
310
|
it 'rewrites query' do
|
226
|
-
MultiTenant.with(
|
311
|
+
MultiTenant.with(tenant_id1) do
|
227
312
|
expect(SubclassTask.where(name: name).count).to eq 1
|
228
|
-
expect(SubclassTask.where(name: name).first).to eq
|
313
|
+
expect(SubclassTask.where(name: name).first).to eq subclass_task1
|
229
314
|
end
|
230
|
-
MultiTenant.with(
|
315
|
+
MultiTenant.with(tenant_id2) do
|
231
316
|
expect(SubclassTask.where(name: name).count).to eq 1
|
232
|
-
expect(SubclassTask.where(name: name).first).to eq
|
317
|
+
expect(SubclassTask.where(name: name).first).to eq subclass_task2
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
# Joins
|
323
|
+
describe 'joins for models' do
|
324
|
+
context 'for models with where condition in associations' do
|
325
|
+
let(:account) { Account.create!(name: 'Account 1') }
|
326
|
+
|
327
|
+
it 'should add tenant condition to the queries when tenant is set' do
|
328
|
+
expected_join_sql = <<-SQL.strip
|
329
|
+
SELECT "comments".*#{' '}
|
330
|
+
FROM "comments"#{' '}
|
331
|
+
INNER JOIN "tasks" ON "tasks"."id" = "comments"."commentable_id"#{' '}
|
332
|
+
AND "comments"."commentable_type" = 'Task' AND "tasks"."account_id" = 1#{' '}
|
333
|
+
WHERE "comments"."account_id" = 1
|
334
|
+
SQL
|
335
|
+
|
336
|
+
MultiTenant.with(account) do
|
337
|
+
expect(format_sql(Comment.joins(:task).to_sql)).to eq(format_sql(expected_join_sql))
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'should add tenant condition to the queries when tenant is not set' do
|
342
|
+
MultiTenant.without do
|
343
|
+
expected_join_sql = <<-SQL.strip
|
344
|
+
SELECT "comments".*#{' '}
|
345
|
+
FROM "comments"#{' '}
|
346
|
+
INNER JOIN "tasks" ON "tasks"."id" = "comments"."commentable_id"#{' '}
|
347
|
+
AND "comments"."commentable_type" = 'Task' AND "comments"."account_id" = "tasks"."account_id"
|
348
|
+
SQL
|
349
|
+
expect(format_sql(Comment.joins(:task).to_sql)).to eq(format_sql(expected_join_sql))
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context 'for models with default associations' do
|
355
|
+
let(:account) { Account.create!(name: 'Account 1') }
|
356
|
+
|
357
|
+
it 'should add tenant condition to the queries when tenant is set' do
|
358
|
+
expected_join_sql = <<-SQL.strip
|
359
|
+
SELECT "projects".*#{' '}
|
360
|
+
FROM "projects"#{' '}
|
361
|
+
INNER JOIN "tasks" ON "tasks"."project_id" = "projects"."id"#{' '}
|
362
|
+
AND "tasks"."account_id" = 1#{' '}
|
363
|
+
WHERE "projects"."account_id" = 1
|
364
|
+
SQL
|
365
|
+
|
366
|
+
MultiTenant.with(account) do
|
367
|
+
expect(format_sql(Project.joins(:tasks).to_sql)).to eq(format_sql(expected_join_sql))
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'should add tenant condition to the queries when tenant is not set' do
|
372
|
+
MultiTenant.without do
|
373
|
+
expected_join_sql = <<-SQL.strip
|
374
|
+
SELECT "projects".*
|
375
|
+
FROM "projects"
|
376
|
+
INNER JOIN "tasks" ON "tasks"."project_id" = "projects"."id"
|
377
|
+
AND "projects"."account_id" = "tasks"."account_id"
|
378
|
+
SQL
|
379
|
+
expect(format_sql(Project.joins(:tasks).to_sql)).to eq(format_sql(expected_join_sql))
|
380
|
+
end
|
233
381
|
end
|
234
382
|
end
|
235
383
|
end
|
236
384
|
|
237
385
|
# ::with
|
238
|
-
describe
|
239
|
-
it
|
240
|
-
@account = Account.create!(:
|
386
|
+
describe '::with' do
|
387
|
+
it 'should set current_tenant to the specified tenant inside the block' do
|
388
|
+
@account = Account.create!(name: 'baz')
|
241
389
|
|
242
390
|
MultiTenant.with(@account) do
|
243
391
|
expect(MultiTenant.current_tenant).to eq(@account)
|
244
392
|
end
|
245
393
|
end
|
246
394
|
|
247
|
-
it
|
248
|
-
@account1 = Account.create!(:
|
249
|
-
@account2 = Account.create!(:
|
395
|
+
it 'should reset current_tenant to the previous tenant once exiting the block' do
|
396
|
+
@account1 = Account.create!(name: 'foo')
|
397
|
+
@account2 = Account.create!(name: 'bar')
|
250
398
|
|
251
399
|
MultiTenant.current_tenant = @account1
|
252
400
|
MultiTenant.with @account2 do
|
253
|
-
|
254
401
|
end
|
255
402
|
|
256
403
|
expect(MultiTenant.current_tenant).to eq(@account1)
|
257
404
|
end
|
258
405
|
|
259
|
-
it
|
260
|
-
@account1 = Account.create!(:
|
261
|
-
@account2 = Account.create!(:
|
406
|
+
it 'should return the value of the block' do
|
407
|
+
@account1 = Account.create!(name: 'foo')
|
408
|
+
@account2 = Account.create!(name: 'bar')
|
262
409
|
|
263
410
|
MultiTenant.current_tenant = @account1
|
264
411
|
value = MultiTenant.with @account2 do
|
265
|
-
|
412
|
+
'something'
|
266
413
|
end
|
267
414
|
|
268
|
-
expect(value).to eq
|
415
|
+
expect(value).to eq 'something'
|
269
416
|
end
|
270
417
|
|
271
418
|
it 'supports reload inside the block' do
|
@@ -280,9 +427,9 @@ describe MultiTenant do
|
|
280
427
|
end
|
281
428
|
|
282
429
|
# ::without
|
283
|
-
describe
|
284
|
-
it
|
285
|
-
@account = Account.create!(:
|
430
|
+
describe '::without' do
|
431
|
+
it 'should unset current_tenant inside the block' do
|
432
|
+
@account = Account.create!(name: 'baz')
|
286
433
|
|
287
434
|
MultiTenant.current_tenant = @account
|
288
435
|
MultiTenant.without do
|
@@ -290,39 +437,25 @@ describe MultiTenant do
|
|
290
437
|
end
|
291
438
|
end
|
292
439
|
|
293
|
-
it
|
294
|
-
@account1 = Account.create!(:
|
440
|
+
it 'should reset current_tenant to the previous tenant once exiting the block' do
|
441
|
+
@account1 = Account.create!(name: 'foo')
|
295
442
|
|
296
443
|
MultiTenant.current_tenant = @account1
|
297
444
|
MultiTenant.without do
|
298
|
-
|
299
445
|
end
|
300
446
|
|
301
447
|
expect(MultiTenant.current_tenant).to eq(@account1)
|
302
448
|
end
|
303
449
|
|
304
|
-
it
|
305
|
-
@account1 = Account.create!(:
|
450
|
+
it 'should return the value of the block' do
|
451
|
+
@account1 = Account.create!(name: 'foo')
|
306
452
|
|
307
453
|
MultiTenant.current_tenant = @account1
|
308
454
|
value = MultiTenant.without do
|
309
|
-
|
455
|
+
'something'
|
310
456
|
end
|
311
457
|
|
312
|
-
expect(value).to eq
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
describe '.with_lock' do
|
317
|
-
it 'supports with_lock blocks inside the block' do
|
318
|
-
@account = Account.create!(name: 'foo')
|
319
|
-
|
320
|
-
MultiTenant.with @account do
|
321
|
-
project = @account.projects.create!(name: 'project')
|
322
|
-
project.with_lock do
|
323
|
-
expect(project.name).to eq 'project'
|
324
|
-
end
|
325
|
-
end
|
458
|
+
expect(value).to eq 'something'
|
326
459
|
end
|
327
460
|
end
|
328
461
|
|
@@ -347,13 +480,20 @@ describe MultiTenant do
|
|
347
480
|
end
|
348
481
|
end
|
349
482
|
|
350
|
-
it
|
351
|
-
option1 = <<-
|
352
|
-
SELECT "sub_tasks"
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
483
|
+
it 'applies the team_id conditions in the where clause' do
|
484
|
+
option1 = <<-SQL.strip
|
485
|
+
SELECT "sub_tasks".*#{' '}
|
486
|
+
FROM "sub_tasks"#{' '}
|
487
|
+
INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "tasks"."account_id" = "sub_tasks"."account_id"#{' '}
|
488
|
+
WHERE "tasks"."project_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."account_id" = 1
|
489
|
+
SQL
|
490
|
+
option2 = <<-SQL.strip
|
491
|
+
SELECT "sub_tasks".*#{' '}
|
492
|
+
FROM "sub_tasks"#{' '}
|
493
|
+
INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id"#{' '}
|
494
|
+
AND "tasks"."account_id" = "sub_tasks"."account_id"#{' '}
|
495
|
+
WHERE "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1 AND "tasks"."account_id" = 1
|
496
|
+
SQL
|
357
497
|
|
358
498
|
account1 = Account.create! name: 'Account 1'
|
359
499
|
|
@@ -361,27 +501,38 @@ describe MultiTenant do
|
|
361
501
|
project1 = Project.create! name: 'Project 1'
|
362
502
|
task1 = Task.create! name: 'Task 1', project: project1
|
363
503
|
subtask1 = SubTask.create! task: task1
|
364
|
-
expect(project1.sub_tasks.to_sql)
|
504
|
+
expect(format_sql(project1.sub_tasks.to_sql))
|
505
|
+
.to eq(format_sql(option1)).or(eq(format_sql(option2)))
|
365
506
|
expect(project1.sub_tasks).to include(subtask1)
|
366
507
|
end
|
367
508
|
|
368
509
|
MultiTenant.without do
|
369
|
-
expected_sql = <<-
|
370
|
-
|
371
|
-
|
510
|
+
expected_sql = <<-SQL
|
511
|
+
SELECT "sub_tasks".*#{' '}
|
512
|
+
FROM "sub_tasks"#{' '}
|
513
|
+
INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id"#{' '}
|
514
|
+
AND "tasks"."account_id" = "sub_tasks"."account_id"#{' '}
|
515
|
+
WHERE "tasks"."project_id" = 1
|
516
|
+
SQL
|
372
517
|
|
373
518
|
project = Project.first
|
374
|
-
expect(project.sub_tasks.to_sql).to eq(expected_sql.strip)
|
519
|
+
expect(format_sql(project.sub_tasks.to_sql)).to eq(format_sql(expected_sql.strip))
|
375
520
|
end
|
376
521
|
end
|
377
522
|
|
378
|
-
it
|
379
|
-
option1 = <<-
|
380
|
-
SELECT "categories"
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
523
|
+
it 'tests joins between distributed and reference table' do
|
524
|
+
option1 = <<-SQL.strip
|
525
|
+
SELECT "categories".*#{' '}
|
526
|
+
FROM "categories"#{' '}
|
527
|
+
INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id"#{' '}
|
528
|
+
WHERE "project_categories"."project_id" = 1 AND "project_categories"."account_id" = 1
|
529
|
+
SQL
|
530
|
+
option2 = <<-SQL.strip
|
531
|
+
SELECT "categories".*#{' '}
|
532
|
+
FROM "categories"#{' '}
|
533
|
+
INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id"#{' '}
|
534
|
+
WHERE "project_categories"."account_id" = 1 AND "project_categories"."project_id" = 1
|
535
|
+
SQL
|
385
536
|
|
386
537
|
account1 = Account.create! name: 'Account 1'
|
387
538
|
category1 = Category.create! name: 'Category 1'
|
@@ -390,47 +541,72 @@ describe MultiTenant do
|
|
390
541
|
project1 = Project.create! name: 'Project 1'
|
391
542
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
392
543
|
|
393
|
-
expect(project1.categories.to_sql)
|
544
|
+
expect(format_sql(project1.categories.to_sql))
|
545
|
+
.to eq(format_sql(option1)).or(eq(format_sql(option2)))
|
394
546
|
expect(project1.categories).to include(category1)
|
395
547
|
expect(project1.project_categories).to include(projectcategory)
|
396
548
|
end
|
397
549
|
|
398
550
|
MultiTenant.without do
|
399
|
-
expected_sql = <<-
|
400
|
-
|
401
|
-
|
551
|
+
expected_sql = <<-SQL
|
552
|
+
SELECT "categories".*#{' '}
|
553
|
+
FROM "categories"#{' '}
|
554
|
+
INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id"#{' '}
|
555
|
+
WHERE "project_categories"."project_id" = 1
|
556
|
+
SQL
|
402
557
|
|
403
558
|
project = Project.first
|
404
|
-
expect(project.categories.to_sql)
|
559
|
+
expect(format_sql(project.categories.to_sql))
|
560
|
+
.to eq(format_sql(expected_sql.strip))
|
405
561
|
expect(project.categories).to include(category1)
|
406
562
|
|
407
|
-
expected_sql = <<-
|
408
|
-
|
409
|
-
|
563
|
+
expected_sql = <<-SQL
|
564
|
+
SELECT "projects".* FROM "projects"#{' '}
|
565
|
+
INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id"#{' '}
|
566
|
+
AND "projects"."account_id" = "project_categories"."account_id"#{' '}
|
567
|
+
INNER JOIN "categories" ON "categories"."id" = "project_categories"."category_id"#{' '}
|
568
|
+
WHERE "projects"."account_id" = 1
|
569
|
+
SQL
|
410
570
|
|
411
|
-
expect(Project.where(account_id: 1).joins(:categories).to_sql)
|
571
|
+
expect(format_sql(Project.where(account_id: 1).joins(:categories).to_sql))
|
572
|
+
.to eq(format_sql(expected_sql.strip))
|
412
573
|
project = Project.where(account_id: 1).joins(:categories).first
|
413
574
|
expect(project.categories).to include(category1)
|
414
575
|
end
|
415
576
|
end
|
416
577
|
|
417
|
-
|
418
|
-
it "test eager_load" do
|
578
|
+
it 'test eager_load' do
|
419
579
|
account1 = Account.create! name: 'Account 1'
|
420
580
|
category1 = Category.create! name: 'Category 1'
|
421
581
|
|
422
|
-
option1 = <<-
|
423
|
-
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2,
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
582
|
+
option1 = <<-SQL.strip
|
583
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2,
|
584
|
+
"categories"."id" AS t1_r0, "categories"."name" AS t1_r1
|
585
|
+
FROM "projects"
|
586
|
+
LEFT OUTER JOIN "project_categories"
|
587
|
+
ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = 1
|
588
|
+
AND "projects"."account_id" = 1#{' '}
|
589
|
+
LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id"
|
590
|
+
AND "project_categories"."account_id" = 1
|
591
|
+
WHERE "projects"."account_id" = 1
|
592
|
+
SQL
|
593
|
+
option2 = <<-SQL.strip
|
594
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2,#{' '}
|
595
|
+
"categories"."id" AS t1_r0, "categories"."name" AS t1_r1#{' '}
|
596
|
+
FROM "projects"#{' '}
|
597
|
+
LEFT OUTER JOIN "project_categories"#{' '}
|
598
|
+
ON "project_categories"."account_id" = 1#{' '}
|
599
|
+
AND "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = 1#{' '}
|
600
|
+
LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id"#{' '}
|
601
|
+
AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
|
602
|
+
SQL
|
428
603
|
|
429
604
|
MultiTenant.with(account1) do
|
430
605
|
project1 = Project.create! name: 'Project 1'
|
431
606
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
432
607
|
|
433
|
-
expect(Project.eager_load(:categories).to_sql)
|
608
|
+
expect(format_sql(Project.eager_load(:categories).to_sql))
|
609
|
+
.to eq(format_sql(option1)).or(eq(format_sql(option2)))
|
434
610
|
|
435
611
|
project = Project.eager_load(:categories).first
|
436
612
|
expect(project.categories).to include(category1)
|
@@ -438,92 +614,120 @@ describe MultiTenant do
|
|
438
614
|
end
|
439
615
|
|
440
616
|
MultiTenant.without do
|
441
|
-
expected_sql = <<-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
617
|
+
expected_sql = <<-SQL
|
618
|
+
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2,#{' '}
|
619
|
+
"categories"."id" AS t1_r0, "categories"."name" AS t1_r1#{' '}
|
620
|
+
FROM "projects" LEFT OUTER JOIN "project_categories"#{' '}
|
621
|
+
ON "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = "project_categories"."account_id"#{' '}
|
622
|
+
LEFT OUTER JOIN "categories"#{' '}
|
623
|
+
ON "categories"."id" = "project_categories"."category_id"#{' '}
|
624
|
+
WHERE "projects"."account_id" = 1
|
625
|
+
SQL
|
626
|
+
|
627
|
+
expect(format_sql(Project.where(account_id: 1).eager_load(:categories).to_sql))
|
628
|
+
.to eq(format_sql(expected_sql.strip))
|
446
629
|
|
447
630
|
project = Project.where(account_id: 1).eager_load(:categories).first
|
448
631
|
expect(project.categories).to include(category1)
|
449
|
-
|
450
632
|
end
|
451
633
|
end
|
452
634
|
|
453
|
-
it
|
635
|
+
it 'test raw SQL joins' do
|
454
636
|
account1 = Account.create! name: 'Account 1'
|
455
637
|
category1 = Category.create! name: 'Category 1'
|
456
638
|
|
457
639
|
MultiTenant.with(account1) do
|
458
|
-
option1 = <<-
|
459
|
-
SELECT "tasks".* FROM "tasks"
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
640
|
+
option1 = <<-SQL.strip
|
641
|
+
SELECT "tasks".* FROM "tasks"
|
642
|
+
INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = 1
|
643
|
+
LEFT JOIN project_categories pc ON project.category_id = pc.id#{' '}
|
644
|
+
WHERE "tasks"."account_id" = 1
|
645
|
+
SQL
|
646
|
+
option2 = <<-SQL.strip
|
647
|
+
SELECT "tasks".* FROM "tasks"
|
648
|
+
INNER JOIN "projects" ON "projects"."account_id" = 1#{' '}
|
649
|
+
AND "projects"."id" = "tasks"."project_id"
|
650
|
+
LEFT JOIN project_categories pc ON project.category_id = pc.id#{' '}
|
651
|
+
WHERE "tasks"."account_id" = 1
|
652
|
+
SQL
|
464
653
|
|
465
654
|
project1 = Project.create! name: 'Project 1'
|
466
|
-
|
655
|
+
ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
467
656
|
|
468
657
|
project1.tasks.create! name: 'baz'
|
469
|
-
expect(
|
658
|
+
expect(
|
659
|
+
format_sql(
|
660
|
+
Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql
|
661
|
+
)
|
662
|
+
).to eq(format_sql(option1)).or(eq(format_sql(option2)))
|
470
663
|
end
|
471
664
|
|
472
665
|
MultiTenant.without do
|
473
|
-
expected_sql = <<-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
666
|
+
expected_sql = <<-SQL.strip
|
667
|
+
SELECT "tasks".* FROM "tasks"
|
668
|
+
INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id"
|
669
|
+
AND "tasks"."account_id" = "projects"."account_id"
|
670
|
+
LEFT JOIN project_categories pc ON project.category_id = pc.id
|
671
|
+
WHERE "tasks"."account_id" = 1
|
672
|
+
SQL
|
673
|
+
|
674
|
+
expect(format_sql(Task.where(account_id: 1).joins(:project)
|
675
|
+
.joins('LEFT JOIN project_categories pc ON project.category_id = pc.id')
|
676
|
+
.to_sql)).to eq(format_sql(expected_sql.strip))
|
479
677
|
end
|
480
|
-
|
481
678
|
end
|
482
679
|
|
483
|
-
it
|
680
|
+
it 'only applies clauses when a tenant is set' do
|
484
681
|
account = Account.create! name: 'Account 1'
|
485
682
|
project = Project.create! name: 'Project 1', account: account
|
486
683
|
project2 = Project.create! name: 'Project 2', account: Account.create!(name: 'Account2')
|
487
684
|
|
488
685
|
MultiTenant.with(account) do
|
489
|
-
option1 = <<-
|
490
|
-
SELECT "projects".* FROM "projects"
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
686
|
+
option1 = <<-SQL.strip
|
687
|
+
SELECT "projects".* FROM "projects"#{' '}
|
688
|
+
WHERE "projects"."account_id" = #{account.id} AND "projects"."id" = $1 LIMIT $2
|
689
|
+
SQL
|
690
|
+
option2 = <<-SQL.strip
|
691
|
+
SELECT "projects".* FROM "projects"#{' '}
|
692
|
+
WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
|
693
|
+
SQL
|
694
|
+
option3 = <<-SQL.strip
|
695
|
+
SELECT "projects".* FROM "projects"
|
696
|
+
WHERE "projects"."id" = $1
|
697
|
+
AND "projects"."account_id" = #{account.id} LIMIT $2
|
698
|
+
SQL
|
498
699
|
|
499
700
|
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
500
|
-
# expect(Project).to receive(:find_by_sql).with(eq(option1).
|
701
|
+
# expect(Project).to receive(:find_by_sql).with(eq(option1).
|
702
|
+
# or(eq(option2)).or(eq(option3)), any_args).and_call_original
|
501
703
|
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
502
|
-
expect(args[0]).to(eq(option1)
|
503
|
-
|
704
|
+
expect(format_sql(args[0])).to(eq(format_sql(option1))
|
705
|
+
.or(eq(format_sql(option2))).or(eq(format_sql(option3))))
|
706
|
+
m.call(args[0], args[1], preparable: args[2][:preparable])
|
504
707
|
end
|
505
708
|
expect(Project.find(project.id)).to eq(project)
|
506
709
|
end
|
507
710
|
|
508
711
|
MultiTenant.without do
|
509
|
-
option1 = <<-
|
510
|
-
SELECT "projects".* FROM "projects"
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
712
|
+
option1 = <<-SQL.strip
|
713
|
+
SELECT "projects".* FROM "projects"#{' '}
|
714
|
+
WHERE "projects"."id" = $1 LIMIT $2
|
715
|
+
SQL
|
716
|
+
option2 = <<-SQL.strip
|
717
|
+
SELECT "projects".* FROM "projects"#{' '}
|
718
|
+
WHERE "projects"."id" = $1 LIMIT $2
|
719
|
+
SQL
|
515
720
|
|
516
721
|
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
517
722
|
# expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)), any_args).and_call_original
|
518
723
|
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
519
|
-
expect(args[0]).to(eq(option1).or(eq(option2)))
|
520
|
-
m.call(args[0], args[1], preparable:args[2][:preparable])
|
724
|
+
expect(format_sql(args[0])).to(eq(format_sql(option1)).or(eq(format_sql(option2))))
|
725
|
+
m.call(args[0], args[1], preparable: args[2][:preparable])
|
521
726
|
end
|
522
727
|
expect(Project.find(project2.id)).to eq(project2)
|
523
728
|
end
|
524
729
|
end
|
525
730
|
|
526
|
-
|
527
731
|
describe 'with unsaved association' do
|
528
732
|
before do
|
529
733
|
@account = Account.create!(name: 'reflection tenant')
|
@@ -537,13 +741,13 @@ describe MultiTenant do
|
|
537
741
|
end
|
538
742
|
end
|
539
743
|
|
540
|
-
it
|
744
|
+
it 'test value of RETURNING insert in table with no pkey' do
|
541
745
|
account1 = Account.create(name: 'test1')
|
542
746
|
|
543
747
|
MultiTenant.with(account1) do
|
544
|
-
|
748
|
+
AllowedPlace.create! name: 'something1'
|
545
749
|
|
546
|
-
|
750
|
+
Project.create! name: 'Project 1'
|
547
751
|
end
|
548
752
|
end
|
549
753
|
end
|