activerecord-multi-tenant 2.0.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +42 -0
- data/Gemfile +3 -1
- data/LICENSE +0 -0
- data/README.md +3 -2
- data/Rakefile +1 -1
- data/activerecord-multi-tenant.gemspec +28 -22
- 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 +96 -38
- data/lib/activerecord-multi-tenant/multi_tenant.rb +83 -24
- data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
- data/lib/activerecord-multi-tenant/query_rewriter.rb +121 -87
- 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 +300 -147
- 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 +20 -3
- data/spec/spec_helper.rb +46 -17
- data/spec/support/format_sql.rb +20 -0
- metadata +134 -32
- data/.github/workflows/CI.yml +0 -73
- data/gemfiles/.bundle/config +0 -2
- data/gemfiles/active_record_5.2.3.gemfile +0 -16
- 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.3.gemfile +0 -16
- 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,24 @@ 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
|
+
|
73
93
|
describe 'Changes table_name after multi_tenant called' do
|
74
94
|
before do
|
75
95
|
account_klass.has_many(:posts, anonymous_class: post_klass)
|
@@ -111,6 +131,13 @@ describe MultiTenant do
|
|
111
131
|
it { expect(@posts).to eq([@post1]) }
|
112
132
|
end
|
113
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
|
+
|
114
141
|
# Scoping models
|
115
142
|
describe 'Project.all should be scoped to the current tenant if set' do
|
116
143
|
before do
|
@@ -153,16 +180,16 @@ describe MultiTenant do
|
|
153
180
|
end
|
154
181
|
end
|
155
182
|
|
156
|
-
describe
|
183
|
+
describe 'It should be possible to use aliased associations' do
|
157
184
|
before do
|
158
185
|
@account = Account.create! name: 'baz'
|
159
186
|
MultiTenant.current_tenant = @account
|
160
187
|
end
|
161
188
|
|
162
|
-
it { expect(AliasedTask.create(:
|
189
|
+
it { expect(AliasedTask.create(name: 'foo', project_alias: @project2).valid?).to eq(true) }
|
163
190
|
end
|
164
191
|
|
165
|
-
describe
|
192
|
+
describe 'It should be possible to use associations with partition_key from polymorphic' do
|
166
193
|
before do
|
167
194
|
@account = Account.create!(name: 'foo')
|
168
195
|
MultiTenant.current_tenant = @account
|
@@ -186,13 +213,19 @@ describe MultiTenant do
|
|
186
213
|
end
|
187
214
|
|
188
215
|
it 'handles belongs_to with optional: true' do
|
189
|
-
|
190
|
-
|
191
|
-
|
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
|
192
220
|
|
193
|
-
|
221
|
+
it 'handles changing tenant from nil to a value' do
|
222
|
+
record = OptionalSubTask.create(sub_task_id: sub_task.id)
|
194
223
|
expect(record.reload.sub_task).to eq(sub_task)
|
195
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)
|
196
229
|
end
|
197
230
|
|
198
231
|
it 'handles has_many through' do
|
@@ -213,7 +246,7 @@ describe MultiTenant do
|
|
213
246
|
MultiTenant.with(account) do
|
214
247
|
sub_task
|
215
248
|
manager
|
216
|
-
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
|
217
250
|
end
|
218
251
|
end
|
219
252
|
end
|
@@ -224,7 +257,8 @@ describe MultiTenant do
|
|
224
257
|
|
225
258
|
it 'rewrites sub-selects correctly' do
|
226
259
|
MultiTenant.with(account) do
|
227
|
-
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
|
228
262
|
end
|
229
263
|
end
|
230
264
|
end
|
@@ -253,70 +287,132 @@ describe MultiTenant do
|
|
253
287
|
end
|
254
288
|
|
255
289
|
describe 'non-STI Subclass of abstract Multi Tenant Model' do
|
256
|
-
let(:
|
257
|
-
let(:
|
290
|
+
let(:tenant_id1) { 42 }
|
291
|
+
let(:tenant_id2) { 314_158 }
|
258
292
|
let(:name) { 'fooname' }
|
259
|
-
let(:
|
260
|
-
MultiTenant.with(
|
293
|
+
let(:subclass_task1) do
|
294
|
+
MultiTenant.with(tenant_id1) { SubclassTask.create! name: name }
|
261
295
|
end
|
262
|
-
let(:
|
263
|
-
MultiTenant.with(
|
296
|
+
let(:subclass_task2) do
|
297
|
+
MultiTenant.with(tenant_id2) { SubclassTask.create! name: name }
|
264
298
|
end
|
265
299
|
|
266
300
|
before do
|
267
|
-
|
268
|
-
|
301
|
+
subclass_task1
|
302
|
+
subclass_task2
|
269
303
|
end
|
270
304
|
|
271
305
|
it 'injects tenant_id on create' do
|
272
|
-
expect(
|
273
|
-
expect(
|
306
|
+
expect(subclass_task1.non_model_id).to be tenant_id1
|
307
|
+
expect(subclass_task2.non_model_id).to be tenant_id2
|
274
308
|
end
|
275
309
|
|
276
310
|
it 'rewrites query' do
|
277
|
-
MultiTenant.with(
|
311
|
+
MultiTenant.with(tenant_id1) do
|
278
312
|
expect(SubclassTask.where(name: name).count).to eq 1
|
279
|
-
expect(SubclassTask.where(name: name).first).to eq
|
313
|
+
expect(SubclassTask.where(name: name).first).to eq subclass_task1
|
280
314
|
end
|
281
|
-
MultiTenant.with(
|
315
|
+
MultiTenant.with(tenant_id2) do
|
282
316
|
expect(SubclassTask.where(name: name).count).to eq 1
|
283
|
-
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
|
284
381
|
end
|
285
382
|
end
|
286
383
|
end
|
287
384
|
|
288
385
|
# ::with
|
289
|
-
describe
|
290
|
-
it
|
291
|
-
@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')
|
292
389
|
|
293
390
|
MultiTenant.with(@account) do
|
294
391
|
expect(MultiTenant.current_tenant).to eq(@account)
|
295
392
|
end
|
296
393
|
end
|
297
394
|
|
298
|
-
it
|
299
|
-
@account1 = Account.create!(:
|
300
|
-
@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')
|
301
398
|
|
302
399
|
MultiTenant.current_tenant = @account1
|
303
400
|
MultiTenant.with @account2 do
|
304
|
-
|
305
401
|
end
|
306
402
|
|
307
403
|
expect(MultiTenant.current_tenant).to eq(@account1)
|
308
404
|
end
|
309
405
|
|
310
|
-
it
|
311
|
-
@account1 = Account.create!(:
|
312
|
-
@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')
|
313
409
|
|
314
410
|
MultiTenant.current_tenant = @account1
|
315
411
|
value = MultiTenant.with @account2 do
|
316
|
-
|
412
|
+
'something'
|
317
413
|
end
|
318
414
|
|
319
|
-
expect(value).to eq
|
415
|
+
expect(value).to eq 'something'
|
320
416
|
end
|
321
417
|
|
322
418
|
it 'supports reload inside the block' do
|
@@ -331,9 +427,9 @@ describe MultiTenant do
|
|
331
427
|
end
|
332
428
|
|
333
429
|
# ::without
|
334
|
-
describe
|
335
|
-
it
|
336
|
-
@account = Account.create!(:
|
430
|
+
describe '::without' do
|
431
|
+
it 'should unset current_tenant inside the block' do
|
432
|
+
@account = Account.create!(name: 'baz')
|
337
433
|
|
338
434
|
MultiTenant.current_tenant = @account
|
339
435
|
MultiTenant.without do
|
@@ -341,39 +437,25 @@ describe MultiTenant do
|
|
341
437
|
end
|
342
438
|
end
|
343
439
|
|
344
|
-
it
|
345
|
-
@account1 = Account.create!(:
|
440
|
+
it 'should reset current_tenant to the previous tenant once exiting the block' do
|
441
|
+
@account1 = Account.create!(name: 'foo')
|
346
442
|
|
347
443
|
MultiTenant.current_tenant = @account1
|
348
444
|
MultiTenant.without do
|
349
|
-
|
350
445
|
end
|
351
446
|
|
352
447
|
expect(MultiTenant.current_tenant).to eq(@account1)
|
353
448
|
end
|
354
449
|
|
355
|
-
it
|
356
|
-
@account1 = Account.create!(:
|
450
|
+
it 'should return the value of the block' do
|
451
|
+
@account1 = Account.create!(name: 'foo')
|
357
452
|
|
358
453
|
MultiTenant.current_tenant = @account1
|
359
454
|
value = MultiTenant.without do
|
360
|
-
|
455
|
+
'something'
|
361
456
|
end
|
362
457
|
|
363
|
-
expect(value).to eq
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
describe '.with_lock' do
|
368
|
-
it 'supports with_lock blocks inside the block' do
|
369
|
-
@account = Account.create!(name: 'foo')
|
370
|
-
|
371
|
-
MultiTenant.with @account do
|
372
|
-
project = @account.projects.create!(name: 'project')
|
373
|
-
project.with_lock do
|
374
|
-
expect(project.name).to eq 'project'
|
375
|
-
end
|
376
|
-
end
|
458
|
+
expect(value).to eq 'something'
|
377
459
|
end
|
378
460
|
end
|
379
461
|
|
@@ -398,13 +480,20 @@ describe MultiTenant do
|
|
398
480
|
end
|
399
481
|
end
|
400
482
|
|
401
|
-
it
|
402
|
-
option1 = <<-
|
403
|
-
SELECT "sub_tasks"
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
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
|
408
497
|
|
409
498
|
account1 = Account.create! name: 'Account 1'
|
410
499
|
|
@@ -412,27 +501,38 @@ describe MultiTenant do
|
|
412
501
|
project1 = Project.create! name: 'Project 1'
|
413
502
|
task1 = Task.create! name: 'Task 1', project: project1
|
414
503
|
subtask1 = SubTask.create! task: task1
|
415
|
-
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)))
|
416
506
|
expect(project1.sub_tasks).to include(subtask1)
|
417
507
|
end
|
418
508
|
|
419
509
|
MultiTenant.without do
|
420
|
-
expected_sql = <<-
|
421
|
-
|
422
|
-
|
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
|
423
517
|
|
424
518
|
project = Project.first
|
425
|
-
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))
|
426
520
|
end
|
427
521
|
end
|
428
522
|
|
429
|
-
it
|
430
|
-
option1 = <<-
|
431
|
-
SELECT "categories"
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
436
536
|
|
437
537
|
account1 = Account.create! name: 'Account 1'
|
438
538
|
category1 = Category.create! name: 'Category 1'
|
@@ -441,47 +541,72 @@ describe MultiTenant do
|
|
441
541
|
project1 = Project.create! name: 'Project 1'
|
442
542
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
443
543
|
|
444
|
-
expect(project1.categories.to_sql)
|
544
|
+
expect(format_sql(project1.categories.to_sql))
|
545
|
+
.to eq(format_sql(option1)).or(eq(format_sql(option2)))
|
445
546
|
expect(project1.categories).to include(category1)
|
446
547
|
expect(project1.project_categories).to include(projectcategory)
|
447
548
|
end
|
448
549
|
|
449
550
|
MultiTenant.without do
|
450
|
-
expected_sql = <<-
|
451
|
-
|
452
|
-
|
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
|
453
557
|
|
454
558
|
project = Project.first
|
455
|
-
expect(project.categories.to_sql)
|
559
|
+
expect(format_sql(project.categories.to_sql))
|
560
|
+
.to eq(format_sql(expected_sql.strip))
|
456
561
|
expect(project.categories).to include(category1)
|
457
562
|
|
458
|
-
expected_sql = <<-
|
459
|
-
|
460
|
-
|
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
|
461
570
|
|
462
|
-
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))
|
463
573
|
project = Project.where(account_id: 1).joins(:categories).first
|
464
574
|
expect(project.categories).to include(category1)
|
465
575
|
end
|
466
576
|
end
|
467
577
|
|
468
|
-
|
469
|
-
it "test eager_load" do
|
578
|
+
it 'test eager_load' do
|
470
579
|
account1 = Account.create! name: 'Account 1'
|
471
580
|
category1 = Category.create! name: 'Category 1'
|
472
581
|
|
473
|
-
option1 = <<-
|
474
|
-
SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2,
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
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
|
479
603
|
|
480
604
|
MultiTenant.with(account1) do
|
481
605
|
project1 = Project.create! name: 'Project 1'
|
482
606
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
483
607
|
|
484
|
-
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)))
|
485
610
|
|
486
611
|
project = Project.eager_load(:categories).first
|
487
612
|
expect(project.categories).to include(category1)
|
@@ -489,92 +614,120 @@ describe MultiTenant do
|
|
489
614
|
end
|
490
615
|
|
491
616
|
MultiTenant.without do
|
492
|
-
expected_sql = <<-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
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))
|
497
629
|
|
498
630
|
project = Project.where(account_id: 1).eager_load(:categories).first
|
499
631
|
expect(project.categories).to include(category1)
|
500
|
-
|
501
632
|
end
|
502
633
|
end
|
503
634
|
|
504
|
-
it
|
635
|
+
it 'test raw SQL joins' do
|
505
636
|
account1 = Account.create! name: 'Account 1'
|
506
637
|
category1 = Category.create! name: 'Category 1'
|
507
638
|
|
508
639
|
MultiTenant.with(account1) do
|
509
|
-
option1 = <<-
|
510
|
-
SELECT "tasks".* FROM "tasks"
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
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
|
515
653
|
|
516
654
|
project1 = Project.create! name: 'Project 1'
|
517
|
-
|
655
|
+
ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
518
656
|
|
519
657
|
project1.tasks.create! name: 'baz'
|
520
|
-
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)))
|
521
663
|
end
|
522
664
|
|
523
665
|
MultiTenant.without do
|
524
|
-
expected_sql = <<-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
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))
|
530
677
|
end
|
531
|
-
|
532
678
|
end
|
533
679
|
|
534
|
-
it
|
680
|
+
it 'only applies clauses when a tenant is set' do
|
535
681
|
account = Account.create! name: 'Account 1'
|
536
682
|
project = Project.create! name: 'Project 1', account: account
|
537
683
|
project2 = Project.create! name: 'Project 2', account: Account.create!(name: 'Account2')
|
538
684
|
|
539
685
|
MultiTenant.with(account) do
|
540
|
-
option1 = <<-
|
541
|
-
SELECT "projects".* FROM "projects"
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
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
|
549
699
|
|
550
700
|
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
551
|
-
# 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
|
552
703
|
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
553
|
-
expect(args[0]).to(eq(option1)
|
554
|
-
|
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])
|
555
707
|
end
|
556
708
|
expect(Project.find(project.id)).to eq(project)
|
557
709
|
end
|
558
710
|
|
559
711
|
MultiTenant.without do
|
560
|
-
option1 = <<-
|
561
|
-
SELECT "projects".* FROM "projects"
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
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
|
566
720
|
|
567
721
|
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
568
722
|
# expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)), any_args).and_call_original
|
569
723
|
expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
|
570
|
-
expect(args[0]).to(eq(option1).or(eq(option2)))
|
571
|
-
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])
|
572
726
|
end
|
573
727
|
expect(Project.find(project2.id)).to eq(project2)
|
574
728
|
end
|
575
729
|
end
|
576
730
|
|
577
|
-
|
578
731
|
describe 'with unsaved association' do
|
579
732
|
before do
|
580
733
|
@account = Account.create!(name: 'reflection tenant')
|
@@ -588,13 +741,13 @@ describe MultiTenant do
|
|
588
741
|
end
|
589
742
|
end
|
590
743
|
|
591
|
-
it
|
744
|
+
it 'test value of RETURNING insert in table with no pkey' do
|
592
745
|
account1 = Account.create(name: 'test1')
|
593
746
|
|
594
747
|
MultiTenant.with(account1) do
|
595
|
-
|
748
|
+
AllowedPlace.create! name: 'something1'
|
596
749
|
|
597
|
-
|
750
|
+
Project.create! name: 'Project 1'
|
598
751
|
end
|
599
752
|
end
|
600
753
|
end
|