activerecord-multi-tenant 2.0.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 +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
|