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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/active-record-multi-tenant-tests.yml +83 -0
  3. data/.gitignore +6 -0
  4. data/.readthedocs.yaml +15 -0
  5. data/.rspec +0 -0
  6. data/.rubocop.yml +51 -0
  7. data/Appraisals +6 -22
  8. data/CHANGELOG.md +42 -0
  9. data/Gemfile +3 -1
  10. data/LICENSE +0 -0
  11. data/README.md +3 -2
  12. data/Rakefile +1 -1
  13. data/activerecord-multi-tenant.gemspec +28 -22
  14. data/docker-compose.yml +24 -18
  15. data/docs/.gitignore +3 -0
  16. data/docs/Makefile +28 -0
  17. data/docs/api-reference.sh +10 -0
  18. data/docs/requirements.in +4 -0
  19. data/docs/requirements.txt +62 -0
  20. data/docs/source/_static/api-reference/ActiveRecord/Associations/Association.html +285 -0
  21. data/docs/source/_static/api-reference/ActiveRecord/Associations/ClassMethods.html +255 -0
  22. data/docs/source/_static/api-reference/ActiveRecord/Associations.html +117 -0
  23. data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters/SchemaStatements.html +232 -0
  24. data/docs/source/_static/api-reference/ActiveRecord/ConnectionAdapters.html +126 -0
  25. data/docs/source/_static/api-reference/ActiveRecord/QueryMethods.html +336 -0
  26. data/docs/source/_static/api-reference/ActiveRecord/SchemaDumper.html +121 -0
  27. data/docs/source/_static/api-reference/ActiveRecord.html +130 -0
  28. data/docs/source/_static/api-reference/MultiTenant/ArelTenantVisitor.html +755 -0
  29. data/docs/source/_static/api-reference/MultiTenant/ArelVisitorsDepthFirst.html +208 -0
  30. data/docs/source/_static/api-reference/MultiTenant/BaseTenantEnforcementClause.html +462 -0
  31. data/docs/source/_static/api-reference/MultiTenant/Context.html +659 -0
  32. data/docs/source/_static/api-reference/MultiTenant/ControllerExtensions.html +202 -0
  33. data/docs/source/_static/api-reference/MultiTenant/CopyFromClient.html +186 -0
  34. data/docs/source/_static/api-reference/MultiTenant/CopyFromClientHelper.html +362 -0
  35. data/docs/source/_static/api-reference/MultiTenant/Current.html +124 -0
  36. data/docs/source/_static/api-reference/MultiTenant/DatabaseStatements.html +366 -0
  37. data/docs/source/_static/api-reference/MultiTenant/FastTruncate.html +226 -0
  38. data/docs/source/_static/api-reference/MultiTenant/MigrationExtensions.html +554 -0
  39. data/docs/source/_static/api-reference/MultiTenant/MissingTenantError.html +124 -0
  40. data/docs/source/_static/api-reference/MultiTenant/ModelExtensionsClassMethods.html +492 -0
  41. data/docs/source/_static/api-reference/MultiTenant/QueryMonitor.html +257 -0
  42. data/docs/source/_static/api-reference/MultiTenant/Table.html +419 -0
  43. data/docs/source/_static/api-reference/MultiTenant/TenantEnforcementClause.html +148 -0
  44. data/docs/source/_static/api-reference/MultiTenant/TenantIsImmutable.html +135 -0
  45. data/docs/source/_static/api-reference/MultiTenant/TenantJoinEnforcementClause.html +310 -0
  46. data/docs/source/_static/api-reference/MultiTenant/TenantValueVisitor.html +239 -0
  47. data/docs/source/_static/api-reference/MultiTenant.html +1454 -0
  48. data/docs/source/_static/api-reference/MultiTenantFindBy.html +180 -0
  49. data/docs/source/_static/api-reference/Sidekiq/Client.html +302 -0
  50. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Client.html +217 -0
  51. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant/Server.html +219 -0
  52. data/docs/source/_static/api-reference/Sidekiq/Middleware/MultiTenant.html +126 -0
  53. data/docs/source/_static/api-reference/Sidekiq.html +126 -0
  54. data/docs/source/_static/api-reference/_index.html +399 -0
  55. data/docs/source/_static/api-reference/class_list.html +51 -0
  56. data/docs/source/_static/api-reference/css/common.css +1 -0
  57. data/docs/source/_static/api-reference/css/full_list.css +58 -0
  58. data/docs/source/_static/api-reference/css/style.css +497 -0
  59. data/docs/source/_static/api-reference/file.README.html +167 -0
  60. data/docs/source/_static/api-reference/file_list.html +56 -0
  61. data/docs/source/_static/api-reference/frames.html +17 -0
  62. data/docs/source/_static/api-reference/index.html +167 -0
  63. data/docs/source/_static/api-reference/js/app.js +314 -0
  64. data/docs/source/_static/api-reference/js/full_list.js +216 -0
  65. data/docs/source/_static/api-reference/js/jquery.js +4 -0
  66. data/docs/source/_static/api-reference/method_list.html +715 -0
  67. data/docs/source/_static/api-reference/top-level-namespace.html +126 -0
  68. data/docs/source/_templates/.gitignore +4 -0
  69. data/docs/source/api-reference.rst +8 -0
  70. data/docs/source/appendix.rst +26 -0
  71. data/docs/source/changelog.rst +8 -0
  72. data/docs/source/community-and-support.rst +26 -0
  73. data/docs/source/conf.py +30 -0
  74. data/docs/source/contributing.rst +70 -0
  75. data/docs/source/getting-started.rst +37 -0
  76. data/docs/source/guides-and-tutorials.rst +129 -0
  77. data/docs/source/index.rst +54 -0
  78. data/docs/source/introduction.rst +33 -0
  79. data/docs/source/license.rst +22 -0
  80. data/docs/source/troubleshooting.rst +41 -0
  81. data/docs/source/usage-guide.rst +59 -0
  82. data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +183 -174
  83. data/lib/activerecord-multi-tenant/controller_extensions.rb +15 -4
  84. data/lib/activerecord-multi-tenant/copy_from_client.rb +4 -0
  85. data/lib/activerecord-multi-tenant/fast_truncate.rb +4 -2
  86. data/lib/activerecord-multi-tenant/habtm.rb +50 -0
  87. data/lib/activerecord-multi-tenant/migrations.rb +87 -10
  88. data/lib/activerecord-multi-tenant/model_extensions.rb +96 -38
  89. data/lib/activerecord-multi-tenant/multi_tenant.rb +83 -24
  90. data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
  91. data/lib/activerecord-multi-tenant/query_rewriter.rb +121 -87
  92. data/lib/activerecord-multi-tenant/sidekiq.rb +46 -19
  93. data/lib/activerecord-multi-tenant/table_node.rb +13 -0
  94. data/lib/activerecord-multi-tenant/version.rb +1 -1
  95. data/lib/activerecord-multi-tenant.rb +3 -13
  96. data/lib/activerecord_multi_tenant.rb +13 -0
  97. data/spec/activerecord-multi-tenant/associations_spec.rb +42 -0
  98. data/spec/activerecord-multi-tenant/controller_extensions_spec.rb +3 -2
  99. data/spec/activerecord-multi-tenant/fast_truncate_spec.rb +8 -6
  100. data/spec/activerecord-multi-tenant/model_extensions_spec.rb +300 -147
  101. data/spec/activerecord-multi-tenant/multi_tenant_spec.rb +69 -13
  102. data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +60 -59
  103. data/spec/activerecord-multi-tenant/record_callback_spec.rb +0 -0
  104. data/spec/activerecord-multi-tenant/record_finding_spec.rb +11 -11
  105. data/spec/activerecord-multi-tenant/record_modifications_spec.rb +23 -4
  106. data/spec/activerecord-multi-tenant/sidekiq_spec.rb +10 -10
  107. data/spec/database.yml +0 -0
  108. data/spec/schema.rb +20 -3
  109. data/spec/spec_helper.rb +46 -17
  110. data/spec/support/format_sql.rb +20 -0
  111. metadata +134 -32
  112. data/.github/workflows/CI.yml +0 -73
  113. data/gemfiles/.bundle/config +0 -2
  114. data/gemfiles/active_record_5.2.3.gemfile +0 -16
  115. data/gemfiles/active_record_5.2.gemfile +0 -16
  116. data/gemfiles/active_record_6.0.gemfile +0 -8
  117. data/gemfiles/active_record_6.1.gemfile +0 -8
  118. data/gemfiles/active_record_7.0.gemfile +0 -8
  119. data/gemfiles/rails_5.2.3.gemfile +0 -16
  120. data/gemfiles/rails_5.2.gemfile +0 -16
  121. data/gemfiles/rails_6.0.gemfile +0 -8
  122. data/gemfiles/rails_6.1.gemfile +0 -8
  123. data/gemfiles/rails_7.0.gemfile +0 -8
  124. data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
  125. data/lib/activerecord-multi-tenant/with_lock.rb +0 -15
  126. 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 = Account.create! name: 'foo'
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 "It should be possible to use aliased associations" do
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(:name => 'foo', :project_alias => @project2).valid?).to eq(true) }
189
+ it { expect(AliasedTask.create(name: 'foo', project_alias: @project2).valid?).to eq(true) }
163
190
  end
164
191
 
165
- describe "It should be possible to use associations with partition_key from polymorphic" do
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
- MultiTenant.with(account) do
190
- sub_task
191
- end
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
- record = sub_task.optional_sub_tasks.create!
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)).where(id: Project.where(id: project.id)).first).to eq project
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(:tenant_id_1) { 42 }
257
- let(:tenant_id_2) { 314158 }
290
+ let(:tenant_id1) { 42 }
291
+ let(:tenant_id2) { 314_158 }
258
292
  let(:name) { 'fooname' }
259
- let(:subclass_task_1) do
260
- MultiTenant.with(tenant_id_1) { SubclassTask.create! name: name }
293
+ let(:subclass_task1) do
294
+ MultiTenant.with(tenant_id1) { SubclassTask.create! name: name }
261
295
  end
262
- let(:subclass_task_2) do
263
- MultiTenant.with(tenant_id_2) { SubclassTask.create! name: name }
296
+ let(:subclass_task2) do
297
+ MultiTenant.with(tenant_id2) { SubclassTask.create! name: name }
264
298
  end
265
299
 
266
300
  before do
267
- subclass_task_1
268
- subclass_task_2
301
+ subclass_task1
302
+ subclass_task2
269
303
  end
270
304
 
271
305
  it 'injects tenant_id on create' do
272
- expect(subclass_task_1.non_model_id).to be tenant_id_1
273
- expect(subclass_task_2.non_model_id).to be tenant_id_2
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(tenant_id_1) do
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 subclass_task_1
313
+ expect(SubclassTask.where(name: name).first).to eq subclass_task1
280
314
  end
281
- MultiTenant.with(tenant_id_2) do
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 subclass_task_2
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 "::with" do
290
- it "should set current_tenant to the specified tenant inside the block" do
291
- @account = Account.create!(:name => 'baz')
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 "should reset current_tenant to the previous tenant once exiting the block" do
299
- @account1 = Account.create!(:name => 'foo')
300
- @account2 = Account.create!(:name => 'bar')
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 "should return the value of the block" do
311
- @account1 = Account.create!(:name => 'foo')
312
- @account2 = Account.create!(:name => 'bar')
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
- "something"
412
+ 'something'
317
413
  end
318
414
 
319
- expect(value).to eq "something"
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 "::without" do
335
- it "should unset current_tenant inside the block" do
336
- @account = Account.create!(:name => 'baz')
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 "should reset current_tenant to the previous tenant once exiting the block" do
345
- @account1 = Account.create!(:name => 'foo')
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 "should return the value of the block" do
356
- @account1 = Account.create!(:name => 'foo')
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
- "something"
455
+ 'something'
361
456
  end
362
457
 
363
- expect(value).to eq "something"
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 "applies the team_id conditions in the where clause" do
402
- option1 = <<-sql.strip
403
- SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."project_id" = 1 AND "sub_tasks"."account_id" = 1 AND "tasks"."account_id" = 1
404
- sql
405
- option2 = <<-sql.strip
406
- SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "sub_tasks"."account_id" = 1 AND "tasks"."project_id" = 1 AND "tasks"."account_id" = 1
407
- sql
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).to eq(option1).or(eq(option2))
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 = <<-sql
421
- SELECT "sub_tasks".* FROM "sub_tasks" INNER JOIN "tasks" ON "sub_tasks"."task_id" = "tasks"."id" AND "sub_tasks"."account_id" = "tasks"."account_id" WHERE "tasks"."project_id" = 1
422
- sql
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 "tests joins between distributed and reference table" do
430
- option1 = <<-sql.strip
431
- SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = 1 AND "project_categories"."account_id" = 1
432
- sql
433
- option2 = <<-sql.strip
434
- SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."account_id" = 1 AND "project_categories"."project_id" = 1
435
- sql
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).to eq(option1).or(eq(option2))
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 = <<-sql
451
- SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = 1
452
- sql
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).to eq(expected_sql.strip)
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 = <<-sql
459
- SELECT "projects".* FROM "projects" INNER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = "projects"."account_id" INNER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
460
- sql
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).to eq(expected_sql.strip)
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 = <<-sql.strip
474
- SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = 1 AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
475
- sql
476
- option2 = <<-sql.strip
477
- SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."account_id" = 1 AND "project_categories"."project_id" = "projects"."id" AND "projects"."account_id" = 1 LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" AND "project_categories"."account_id" = 1 WHERE "projects"."account_id" = 1
478
- sql
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).to eq(option1).or(eq(option2))
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 = <<-sql
493
- SELECT "projects"."id" AS t0_r0, "projects"."account_id" AS t0_r1, "projects"."name" AS t0_r2, "categories"."id" AS t1_r0, "categories"."name" AS t1_r1 FROM "projects" LEFT OUTER JOIN "project_categories" ON "project_categories"."project_id" = "projects"."id" AND "project_categories"."account_id" = "projects"."account_id" LEFT OUTER JOIN "categories" ON "categories"."id" = "project_categories"."category_id" WHERE "projects"."account_id" = 1
494
- sql
495
-
496
- expect(Project.where(account_id: 1).eager_load(:categories).to_sql).to eq(expected_sql.strip)
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 "test raw SQL joins" do
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 = <<-sql.strip
510
- SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = 1 LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
511
- sql
512
- option2 = <<-sql.strip
513
- SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."account_id" = 1 AND "projects"."id" = "tasks"."project_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
514
- sql
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
- projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
655
+ ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
518
656
 
519
657
  project1.tasks.create! name: 'baz'
520
- expect(Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(option1).or(eq(option2))
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 = <<-sql
525
- SELECT "tasks".* FROM "tasks" INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id" AND "projects"."account_id" = "tasks"."account_id" LEFT JOIN project_categories pc ON project.category_id = pc.id WHERE "tasks"."account_id" = 1
526
- sql
527
-
528
- expect(Task.where(account_id: 1).joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(expected_sql.strip)
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 "only applies clauses when a tenant is set" do
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 = <<-sql.strip
541
- SELECT "projects".* FROM "projects" WHERE "projects"."account_id" = #{account.id} AND "projects"."id" = $1 LIMIT $2
542
- sql
543
- option2 = <<-sql.strip
544
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
545
- sql
546
- option3 = <<-sql.strip
547
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
548
- sql
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).or(eq(option2)).or(eq(option3)), any_args).and_call_original
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).or(eq(option2)).or(eq(option3)))
554
- m.call(args[0], args[1], preparable:args[2][:preparable])
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 = <<-sql.strip
561
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
562
- sql
563
- option2 = <<-sql.strip
564
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
565
- sql
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 "test value of RETURNING insert in table with no pkey" do
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
- allowed_place = AllowedPlace.create! name: 'something1'
748
+ AllowedPlace.create! name: 'something1'
596
749
 
597
- project = Project.create! name: 'Project 1'
750
+ Project.create! name: 'Project 1'
598
751
  end
599
752
  end
600
753
  end