activerecord-multi-tenant 1.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) 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 +51 -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 -23
  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 +98 -34
  89. data/lib/activerecord-multi-tenant/multi_tenant.rb +102 -29
  90. data/lib/activerecord-multi-tenant/query_monitor.rb +21 -5
  91. data/lib/activerecord-multi-tenant/query_rewriter.rb +122 -91
  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 +347 -143
  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 +43 -2
  109. data/spec/spec_helper.rb +52 -16
  110. data/spec/support/format_sql.rb +20 -0
  111. metadata +126 -36
  112. data/.github/workflows/CI.yml +0 -63
  113. data/gemfiles/.bundle/config +0 -2
  114. data/gemfiles/active_record_5.2.gemfile +0 -16
  115. data/gemfiles/active_record_6.0.gemfile +0 -8
  116. data/gemfiles/active_record_6.1.gemfile +0 -8
  117. data/gemfiles/active_record_7.0.gemfile +0 -8
  118. data/gemfiles/rails_5.2.gemfile +0 -16
  119. data/gemfiles/rails_6.0.gemfile +0 -8
  120. data/gemfiles/rails_6.1.gemfile +0 -8
  121. data/gemfiles/rails_7.0.gemfile +0 -8
  122. data/lib/activerecord-multi-tenant/persistence_extension.rb +0 -13
  123. data/lib/activerecord-multi-tenant/with_lock.rb +0 -15
  124. 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,72 @@ describe MultiTenant do
70
72
  it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
71
73
  end
72
74
 
75
+ describe 'Tenant model with a nonstandard class name' do
76
+ let(:account_klass) do
77
+ Class.new(ActiveRecord::Base) do
78
+ self.table_name = 'account'
79
+
80
+ def self.name
81
+ 'UserAccount'
82
+ end
83
+
84
+ multi_tenant(:account)
85
+ end
86
+ end
87
+ it 'does not register the tenant model' do
88
+ expect(MultiTenant).not_to receive(:register_multi_tenant_model)
89
+ account_klass
90
+ end
91
+ end
92
+
93
+ describe 'Changes table_name after multi_tenant called' do
94
+ before do
95
+ account_klass.has_many(:posts, anonymous_class: post_klass)
96
+ post_klass.belongs_to(:account, anonymous_class: account_klass)
97
+
98
+ @account1 = account_klass.create! name: 'foo'
99
+ @account2 = account_klass.create! name: 'bar'
100
+
101
+ @post1 = @account1.posts.create! name: 'foobar'
102
+ @post2 = @account2.posts.create! name: 'baz'
103
+
104
+ MultiTenant.current_tenant = @account1
105
+ @posts = post_klass.all
106
+ end
107
+
108
+ let(:account_klass) do
109
+ Class.new(Account) do
110
+ def self.name
111
+ 'Account'
112
+ end
113
+ end
114
+ end
115
+
116
+ let(:post_klass) do
117
+ Class.new(ActiveRecord::Base) do
118
+ self.table_name = 'unknown'
119
+
120
+ multi_tenant(:account)
121
+
122
+ self.table_name = 'posts'
123
+
124
+ def self.name
125
+ 'Post'
126
+ end
127
+ end
128
+ end
129
+
130
+ it { expect(@posts.length).to eq(1) }
131
+ it { expect(@posts).to eq([@post1]) }
132
+ end
133
+
134
+ describe 'inspect method filters senstive column values' do
135
+ it 'filters senstive value' do
136
+ account = Account.new(name: 'foo', password: 'baz')
137
+ expect(account.inspect).to eq '#<Account id: nil, name: nil, subdomain: nil, domain: nil, password: [FILTERED]>'
138
+ end
139
+ end
140
+
73
141
  # Scoping models
74
142
  describe 'Project.all should be scoped to the current tenant if set' do
75
143
  before do
@@ -112,16 +180,16 @@ describe MultiTenant do
112
180
  end
113
181
  end
114
182
 
115
- describe "It should be possible to use aliased associations" do
183
+ describe 'It should be possible to use aliased associations' do
116
184
  before do
117
185
  @account = Account.create! name: 'baz'
118
186
  MultiTenant.current_tenant = @account
119
187
  end
120
188
 
121
- it { expect(AliasedTask.create(:name => 'foo', :project_alias => @project2).valid?).to eq(true) }
189
+ it { expect(AliasedTask.create(name: 'foo', project_alias: @project2).valid?).to eq(true) }
122
190
  end
123
191
 
124
- 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
125
193
  before do
126
194
  @account = Account.create!(name: 'foo')
127
195
  MultiTenant.current_tenant = @account
@@ -144,6 +212,22 @@ describe MultiTenant do
144
212
  end
145
213
  end
146
214
 
215
+ it 'handles belongs_to with optional: true' do
216
+ record = OptionalSubTask.create(sub_task_id: sub_task.id)
217
+ expect(record.reload.sub_task).to eq(sub_task)
218
+ expect(record.account_id).to eq(nil)
219
+ end
220
+
221
+ it 'handles changing tenant from nil to a value' do
222
+ record = OptionalSubTask.create(sub_task_id: sub_task.id)
223
+ expect(record.reload.sub_task).to eq(sub_task)
224
+ expect(record.account_id).to eq(nil)
225
+
226
+ record.account = account
227
+ record.save!
228
+ expect(record.reload.account_id).to eq(account.id)
229
+ end
230
+
147
231
  it 'handles has_many through' do
148
232
  MultiTenant.with(account) do
149
233
  expect(project.sub_tasks).to eq [sub_task]
@@ -162,7 +246,7 @@ describe MultiTenant do
162
246
  MultiTenant.with(account) do
163
247
  sub_task
164
248
  manager
165
- expect(Project.eager_load([{manager: :project}, {tasks: :project}]).first).to eq project
249
+ expect(Project.eager_load([{ manager: :project }, { tasks: :project }]).first).to eq project
166
250
  end
167
251
  end
168
252
  end
@@ -173,7 +257,8 @@ describe MultiTenant do
173
257
 
174
258
  it 'rewrites sub-selects correctly' do
175
259
  MultiTenant.with(account) do
176
- expect(Project.where(id: Project.where(id: project.id)).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
177
262
  end
178
263
  end
179
264
  end
@@ -202,70 +287,132 @@ describe MultiTenant do
202
287
  end
203
288
 
204
289
  describe 'non-STI Subclass of abstract Multi Tenant Model' do
205
- let(:tenant_id_1) { 42 }
206
- let(:tenant_id_2) { 314158 }
290
+ let(:tenant_id1) { 42 }
291
+ let(:tenant_id2) { 314_158 }
207
292
  let(:name) { 'fooname' }
208
- let(:subclass_task_1) do
209
- MultiTenant.with(tenant_id_1) { SubclassTask.create! name: name }
293
+ let(:subclass_task1) do
294
+ MultiTenant.with(tenant_id1) { SubclassTask.create! name: name }
210
295
  end
211
- let(:subclass_task_2) do
212
- MultiTenant.with(tenant_id_2) { SubclassTask.create! name: name }
296
+ let(:subclass_task2) do
297
+ MultiTenant.with(tenant_id2) { SubclassTask.create! name: name }
213
298
  end
214
299
 
215
300
  before do
216
- subclass_task_1
217
- subclass_task_2
301
+ subclass_task1
302
+ subclass_task2
218
303
  end
219
304
 
220
305
  it 'injects tenant_id on create' do
221
- expect(subclass_task_1.non_model_id).to be tenant_id_1
222
- 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
223
308
  end
224
309
 
225
310
  it 'rewrites query' do
226
- MultiTenant.with(tenant_id_1) do
311
+ MultiTenant.with(tenant_id1) do
227
312
  expect(SubclassTask.where(name: name).count).to eq 1
228
- expect(SubclassTask.where(name: name).first).to eq subclass_task_1
313
+ expect(SubclassTask.where(name: name).first).to eq subclass_task1
229
314
  end
230
- MultiTenant.with(tenant_id_2) do
315
+ MultiTenant.with(tenant_id2) do
231
316
  expect(SubclassTask.where(name: name).count).to eq 1
232
- expect(SubclassTask.where(name: name).first).to eq 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
233
381
  end
234
382
  end
235
383
  end
236
384
 
237
385
  # ::with
238
- describe "::with" do
239
- it "should set current_tenant to the specified tenant inside the block" do
240
- @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')
241
389
 
242
390
  MultiTenant.with(@account) do
243
391
  expect(MultiTenant.current_tenant).to eq(@account)
244
392
  end
245
393
  end
246
394
 
247
- it "should reset current_tenant to the previous tenant once exiting the block" do
248
- @account1 = Account.create!(:name => 'foo')
249
- @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')
250
398
 
251
399
  MultiTenant.current_tenant = @account1
252
400
  MultiTenant.with @account2 do
253
-
254
401
  end
255
402
 
256
403
  expect(MultiTenant.current_tenant).to eq(@account1)
257
404
  end
258
405
 
259
- it "should return the value of the block" do
260
- @account1 = Account.create!(:name => 'foo')
261
- @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')
262
409
 
263
410
  MultiTenant.current_tenant = @account1
264
411
  value = MultiTenant.with @account2 do
265
- "something"
412
+ 'something'
266
413
  end
267
414
 
268
- expect(value).to eq "something"
415
+ expect(value).to eq 'something'
269
416
  end
270
417
 
271
418
  it 'supports reload inside the block' do
@@ -280,9 +427,9 @@ describe MultiTenant do
280
427
  end
281
428
 
282
429
  # ::without
283
- describe "::without" do
284
- it "should unset current_tenant inside the block" do
285
- @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')
286
433
 
287
434
  MultiTenant.current_tenant = @account
288
435
  MultiTenant.without do
@@ -290,39 +437,25 @@ describe MultiTenant do
290
437
  end
291
438
  end
292
439
 
293
- it "should reset current_tenant to the previous tenant once exiting the block" do
294
- @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')
295
442
 
296
443
  MultiTenant.current_tenant = @account1
297
444
  MultiTenant.without do
298
-
299
445
  end
300
446
 
301
447
  expect(MultiTenant.current_tenant).to eq(@account1)
302
448
  end
303
449
 
304
- it "should return the value of the block" do
305
- @account1 = Account.create!(:name => 'foo')
450
+ it 'should return the value of the block' do
451
+ @account1 = Account.create!(name: 'foo')
306
452
 
307
453
  MultiTenant.current_tenant = @account1
308
454
  value = MultiTenant.without do
309
- "something"
455
+ 'something'
310
456
  end
311
457
 
312
- expect(value).to eq "something"
313
- end
314
- end
315
-
316
- describe '.with_lock' do
317
- it 'supports with_lock blocks inside the block' do
318
- @account = Account.create!(name: 'foo')
319
-
320
- MultiTenant.with @account do
321
- project = @account.projects.create!(name: 'project')
322
- project.with_lock do
323
- expect(project.name).to eq 'project'
324
- end
325
- end
458
+ expect(value).to eq 'something'
326
459
  end
327
460
  end
328
461
 
@@ -347,13 +480,20 @@ describe MultiTenant do
347
480
  end
348
481
  end
349
482
 
350
- it "applies the team_id conditions in the where clause" do
351
- option1 = <<-sql.strip
352
- 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
353
- sql
354
- option2 = <<-sql.strip
355
- 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
356
- 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
357
497
 
358
498
  account1 = Account.create! name: 'Account 1'
359
499
 
@@ -361,27 +501,38 @@ describe MultiTenant do
361
501
  project1 = Project.create! name: 'Project 1'
362
502
  task1 = Task.create! name: 'Task 1', project: project1
363
503
  subtask1 = SubTask.create! task: task1
364
- expect(project1.sub_tasks.to_sql).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)))
365
506
  expect(project1.sub_tasks).to include(subtask1)
366
507
  end
367
508
 
368
509
  MultiTenant.without do
369
- expected_sql = <<-sql
370
- 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
371
- 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
372
517
 
373
518
  project = Project.first
374
- expect(project.sub_tasks.to_sql).to eq(expected_sql.strip)
519
+ expect(format_sql(project.sub_tasks.to_sql)).to eq(format_sql(expected_sql.strip))
375
520
  end
376
521
  end
377
522
 
378
- it "tests joins between distributed and reference table" do
379
- option1 = <<-sql.strip
380
- 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
381
- sql
382
- option2 = <<-sql.strip
383
- 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
384
- 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
385
536
 
386
537
  account1 = Account.create! name: 'Account 1'
387
538
  category1 = Category.create! name: 'Category 1'
@@ -390,47 +541,72 @@ describe MultiTenant do
390
541
  project1 = Project.create! name: 'Project 1'
391
542
  projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
392
543
 
393
- expect(project1.categories.to_sql).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)))
394
546
  expect(project1.categories).to include(category1)
395
547
  expect(project1.project_categories).to include(projectcategory)
396
548
  end
397
549
 
398
550
  MultiTenant.without do
399
- expected_sql = <<-sql
400
- SELECT "categories".* FROM "categories" INNER JOIN "project_categories" ON "categories"."id" = "project_categories"."category_id" WHERE "project_categories"."project_id" = 1
401
- 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
402
557
 
403
558
  project = Project.first
404
- 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))
405
561
  expect(project.categories).to include(category1)
406
562
 
407
- expected_sql = <<-sql
408
- 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
409
- 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
410
570
 
411
- 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))
412
573
  project = Project.where(account_id: 1).joins(:categories).first
413
574
  expect(project.categories).to include(category1)
414
575
  end
415
576
  end
416
577
 
417
-
418
- it "test eager_load" do
578
+ it 'test eager_load' do
419
579
  account1 = Account.create! name: 'Account 1'
420
580
  category1 = Category.create! name: 'Category 1'
421
581
 
422
- option1 = <<-sql.strip
423
- 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
424
- sql
425
- option2 = <<-sql.strip
426
- 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
427
- 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
428
603
 
429
604
  MultiTenant.with(account1) do
430
605
  project1 = Project.create! name: 'Project 1'
431
606
  projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
432
607
 
433
- expect(Project.eager_load(:categories).to_sql).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)))
434
610
 
435
611
  project = Project.eager_load(:categories).first
436
612
  expect(project.categories).to include(category1)
@@ -438,92 +614,120 @@ describe MultiTenant do
438
614
  end
439
615
 
440
616
  MultiTenant.without do
441
- expected_sql = <<-sql
442
- 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
443
- sql
444
-
445
- 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))
446
629
 
447
630
  project = Project.where(account_id: 1).eager_load(:categories).first
448
631
  expect(project.categories).to include(category1)
449
-
450
632
  end
451
633
  end
452
634
 
453
- it "test raw SQL joins" do
635
+ it 'test raw SQL joins' do
454
636
  account1 = Account.create! name: 'Account 1'
455
637
  category1 = Category.create! name: 'Category 1'
456
638
 
457
639
  MultiTenant.with(account1) do
458
- option1 = <<-sql.strip
459
- 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
460
- sql
461
- option2 = <<-sql.strip
462
- 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
463
- 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
464
653
 
465
654
  project1 = Project.create! name: 'Project 1'
466
- projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
655
+ ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
467
656
 
468
657
  project1.tasks.create! name: 'baz'
469
- 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)))
470
663
  end
471
664
 
472
665
  MultiTenant.without do
473
- expected_sql = <<-sql
474
- 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
475
- sql
476
-
477
- 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)
478
-
666
+ expected_sql = <<-SQL.strip
667
+ SELECT "tasks".* FROM "tasks"
668
+ INNER JOIN "projects" ON "projects"."id" = "tasks"."project_id"
669
+ AND "tasks"."account_id" = "projects"."account_id"
670
+ LEFT JOIN project_categories pc ON project.category_id = pc.id
671
+ WHERE "tasks"."account_id" = 1
672
+ SQL
673
+
674
+ expect(format_sql(Task.where(account_id: 1).joins(:project)
675
+ .joins('LEFT JOIN project_categories pc ON project.category_id = pc.id')
676
+ .to_sql)).to eq(format_sql(expected_sql.strip))
479
677
  end
480
-
481
678
  end
482
679
 
483
- it "only applies clauses when a tenant is set" do
680
+ it 'only applies clauses when a tenant is set' do
484
681
  account = Account.create! name: 'Account 1'
485
682
  project = Project.create! name: 'Project 1', account: account
486
683
  project2 = Project.create! name: 'Project 2', account: Account.create!(name: 'Account2')
487
684
 
488
685
  MultiTenant.with(account) do
489
- option1 = <<-sql.strip
490
- SELECT "projects".* FROM "projects" WHERE "projects"."account_id" = #{account.id} AND "projects"."id" = $1 LIMIT $2
491
- sql
492
- option2 = <<-sql.strip
493
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
494
- sql
495
- option3 = <<-sql.strip
496
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 AND "projects"."account_id" = #{account.id} LIMIT $2
497
- 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
498
699
 
499
700
  # Couldn't make the following line pass for some reason, so came up with an uglier alternative
500
- # expect(Project).to receive(:find_by_sql).with(eq(option1).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
501
703
  expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
502
- expect(args[0]).to(eq(option1).or(eq(option2)).or(eq(option3)))
503
- 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])
504
707
  end
505
708
  expect(Project.find(project.id)).to eq(project)
506
709
  end
507
710
 
508
711
  MultiTenant.without do
509
- option1 = <<-sql.strip
510
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
511
- sql
512
- option2 = <<-sql.strip
513
- SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2
514
- 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
515
720
 
516
721
  # Couldn't make the following line pass for some reason, so came up with an uglier alternative
517
722
  # expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)), any_args).and_call_original
518
723
  expect(Project).to receive(:find_by_sql).and_wrap_original do |m, *args|
519
- expect(args[0]).to(eq(option1).or(eq(option2)))
520
- m.call(args[0], args[1], preparable:args[2][:preparable])
724
+ expect(format_sql(args[0])).to(eq(format_sql(option1)).or(eq(format_sql(option2))))
725
+ m.call(args[0], args[1], preparable: args[2][:preparable])
521
726
  end
522
727
  expect(Project.find(project2.id)).to eq(project2)
523
728
  end
524
729
  end
525
730
 
526
-
527
731
  describe 'with unsaved association' do
528
732
  before do
529
733
  @account = Account.create!(name: 'reflection tenant')
@@ -537,13 +741,13 @@ describe MultiTenant do
537
741
  end
538
742
  end
539
743
 
540
- it "test value of RETURNING insert in table with no pkey" do
744
+ it 'test value of RETURNING insert in table with no pkey' do
541
745
  account1 = Account.create(name: 'test1')
542
746
 
543
747
  MultiTenant.with(account1) do
544
- allowed_place = AllowedPlace.create! name: 'something1'
748
+ AllowedPlace.create! name: 'something1'
545
749
 
546
- project = Project.create! name: 'Project 1'
750
+ Project.create! name: 'Project 1'
547
751
  end
548
752
  end
549
753
  end