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,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  RSpec.describe MultiTenant do
4
- describe ".load_current_tenant!" do
6
+ describe '.load_current_tenant!' do
5
7
  let(:fake_tenant) { OpenStruct.new(id: 1) }
6
8
  let(:mock_klass) { double(find: fake_tenant) }
7
9
 
@@ -14,14 +16,14 @@ RSpec.describe MultiTenant do
14
16
  MultiTenant.default_tenant_class = @original_default_class
15
17
  end
16
18
 
17
- it "sets and returns the loaded current_tenant" do
19
+ it 'sets and returns the loaded current_tenant' do
18
20
  expect(mock_klass).to receive(:find).once.with(1)
19
21
  MultiTenant.current_tenant = 1
20
22
  expect(MultiTenant.load_current_tenant!).to eq(fake_tenant)
21
23
  expect(MultiTenant.current_tenant).to eq(fake_tenant)
22
24
  end
23
25
 
24
- it "respects `.with` lifecycle" do
26
+ it 'respects `.with` lifecycle' do
25
27
  expect(mock_klass).to receive(:find).once.with(2)
26
28
  expect(MultiTenant.current_tenant).to eq(nil)
27
29
  MultiTenant.with(2) do
@@ -31,34 +33,88 @@ RSpec.describe MultiTenant do
31
33
  expect(MultiTenant.current_tenant).to eq(nil)
32
34
  end
33
35
 
34
- context "with a loaded current_tenant" do
35
- it "returns the tenant without fetching it" do
36
+ context 'with a loaded current_tenant' do
37
+ it 'returns the tenant without fetching it' do
36
38
  expect(mock_klass).not_to receive(:find)
37
39
  MultiTenant.current_tenant = fake_tenant
38
40
  expect(MultiTenant.load_current_tenant!).to eq(fake_tenant)
39
41
  end
40
42
  end
41
43
 
42
- context "with a nil current_tenant" do
43
- it "raises an error, as there is not enough information to load the tenant" do
44
+ context 'with a nil current_tenant' do
45
+ it 'raises an error, as there is not enough information to load the tenant' do
44
46
  expect(mock_klass).not_to receive(:find)
45
- expect {
47
+ expect do
46
48
  MultiTenant.load_current_tenant!
47
- }.to raise_error(RuntimeError, 'MultiTenant.current_tenant must be set to load')
49
+ end.to raise_error(RuntimeError, 'MultiTenant.current_tenant must be set to load')
48
50
  end
49
51
  end
50
52
 
51
- context "without a default class set" do
53
+ context 'without a default class set' do
52
54
  before do
53
55
  MultiTenant.default_tenant_class = nil
54
56
  end
55
57
 
56
- it "raises an error, as there is not enough information to load the tenant" do
58
+ it 'raises an error, as there is not enough information to load the tenant' do
57
59
  expect(mock_klass).not_to receive(:find)
58
60
  MultiTenant.current_tenant = 1
59
- expect {
61
+ expect do
60
62
  MultiTenant.load_current_tenant!
61
- }.to raise_error(RuntimeError, 'Only have tenant id, and no default tenant class set')
63
+ end.to raise_error(RuntimeError, 'Only have tenant id, and no default tenant class set')
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '.tenant_klass_defined?' do
69
+ context 'without options' do
70
+ before(:all) do
71
+ class SampleTenant < ActiveRecord::Base
72
+ multi_tenant :sample_tenant
73
+ end
74
+ end
75
+
76
+ it 'return true with valid tenant_name' do
77
+ expect(MultiTenant.tenant_klass_defined?(:sample_tenant)).to eq(true)
78
+ end
79
+
80
+ it 'return false with invalid_tenant_name' do
81
+ invalid_tenant_name = :tenant
82
+ expect(MultiTenant.tenant_klass_defined?(invalid_tenant_name)).to eq(false)
83
+ end
84
+ end
85
+
86
+ context 'with options' do
87
+ context 'and valid class_name' do
88
+ it 'return true' do
89
+ class SampleTenant < ActiveRecord::Base
90
+ multi_tenant :tenant
91
+ end
92
+
93
+ tenant_name = :tenant
94
+ options = {
95
+ class_name: 'SampleTenant'
96
+ }
97
+ expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
98
+ end
99
+
100
+ it 'return true when tenant class is nested' do
101
+ module SampleModule
102
+ class SampleNestedTenant < ActiveRecord::Base
103
+ multi_tenant :tenant
104
+ end
105
+ # rubocop:disable Layout/TrailingWhitespace
106
+ # Trailing whitespace is intentionally left here
107
+
108
+ class AnotherTenant < ActiveRecord::Base
109
+ end
110
+ # rubocop:enable Layout/TrailingWhitespace
111
+ end
112
+ tenant_name = :tenant
113
+ options = {
114
+ class_name: 'SampleModule::SampleNestedTenant'
115
+ }
116
+ expect(MultiTenant.tenant_klass_defined?(tenant_name, options)).to eq(true)
117
+ end
62
118
  end
63
119
  end
64
120
  end
@@ -1,112 +1,113 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe "Query Rewriter" do
3
+ require 'spec_helper'
4
4
 
5
- context "when bulk updating" do
6
- let!(:account) { Account.create!(name: "Test Account") }
7
- let!(:project) { Project.create(name: "Project 1", account: account) }
8
- let!(:manager) { Manager.create(name: "Manager", project: project, account: account) }
5
+ describe 'Query Rewriter' do
6
+ context 'when bulk updating' do
7
+ let!(:account) { Account.create!(name: 'Test Account') }
8
+ let!(:project) { Project.create(name: 'Project 1', account: account) }
9
+ let!(:manager) { Manager.create(name: 'Manager', project: project, account: account) }
9
10
 
10
- it "updates the records" do
11
- expect {
11
+ it 'updates the records' do
12
+ expect do
12
13
  MultiTenant.with(account) do
13
- Project.joins(:manager).update_all(name: "New Name")
14
+ Project.joins(:manager).update_all(name: 'New Name')
14
15
  end
15
- }.to change { project.reload.name }.from("Project 1").to("New Name")
16
+ end.to change { project.reload.name }.from('Project 1').to('New Name')
16
17
  end
17
18
 
18
- it "updates the records without a current tenant" do
19
- expect {
20
- Project.joins(:manager).update_all(name: "New Name")
21
- }.to change { project.reload.name }.from("Project 1").to("New Name")
19
+ it 'updates the records without a current tenant' do
20
+ expect do
21
+ Project.joins(:manager).update_all(name: 'New Name')
22
+ end.to change { project.reload.name }.from('Project 1').to('New Name')
22
23
  end
23
24
 
24
- it "update the record" do
25
- expect {
25
+ it 'update the record' do
26
+ expect do
26
27
  MultiTenant.with(account) do
27
- project.update(name: "New Name")
28
+ project.update(name: 'New Name')
28
29
  end
29
- }.to change { project.reload.name }.from("Project 1").to("New Name")
30
+ end.to change { project.reload.name }.from('Project 1').to('New Name')
30
31
  end
31
32
 
32
- it "update the record without a current tenant" do
33
- expect {
34
- project.update(name: "New Name")
35
- }.to change { project.reload.name }.from("Project 1").to("New Name")
33
+ it 'update the record without a current tenant' do
34
+ expect do
35
+ project.update(name: 'New Name')
36
+ end.to change { project.reload.name }.from('Project 1').to('New Name')
36
37
  end
37
38
  end
38
39
 
39
- context "when bulk deleting" do
40
- let!(:account) { Account.create!(name: "Test Account") }
41
- let!(:project1) { Project.create(name: "Project 1", account: account) }
42
- let!(:project2) { Project.create(name: "Project 2", account: account) }
43
- let!(:project3) { Project.create(name: "Project 3", account: account) }
44
- let!(:manager1) { Manager.create(name: "Manager 1", project: project1, account: account) }
45
- let!(:manager2) { Manager.create(name: "Manager 2", project: project2, account: account) }
40
+ context 'when bulk deleting' do
41
+ let!(:account) { Account.create!(name: 'Test Account') }
42
+ let!(:project1) { Project.create(name: 'Project 1', account: account) }
43
+ let!(:project2) { Project.create(name: 'Project 2', account: account) }
44
+ let!(:project3) { Project.create(name: 'Project 3', account: account) }
45
+ let!(:manager1) { Manager.create(name: 'Manager 1', project: project1, account: account) }
46
+ let!(:manager2) { Manager.create(name: 'Manager 2', project: project2, account: account) }
46
47
 
47
- it "delete_all the records" do
48
- expect {
48
+ it 'delete_all the records' do
49
+ expect do
49
50
  MultiTenant.with(account) do
50
51
  Project.joins(:manager).delete_all
51
52
  end
52
- }.to change { Project.count }.from(3).to(1)
53
+ end.to change { Project.count }.from(3).to(1)
53
54
  end
54
55
 
55
- it "delete_all the records without a current tenant" do
56
- expect {
56
+ it 'delete_all the records without a current tenant' do
57
+ expect do
57
58
  Project.joins(:manager).delete_all
58
- }.to change { Project.count }.from(3).to(1)
59
+ end.to change { Project.count }.from(3).to(1)
59
60
  end
60
61
 
61
- it "delete the record" do
62
- expect {
62
+ it 'delete the record' do
63
+ expect do
63
64
  MultiTenant.with(account) do
64
65
  project1.delete
65
66
  Project.delete(project2.id)
66
67
  end
67
- }.to change { Project.count }.from(3).to(1)
68
+ end.to change { Project.count }.from(3).to(1)
68
69
  end
69
70
 
70
- it "delete the record without a current tenant" do
71
- expect {
71
+ it 'delete the record without a current tenant' do
72
+ expect do
72
73
  project1.delete
73
74
  Project.delete(project2.id)
74
- }.to change { Project.count }.from(3).to(1)
75
+ end.to change { Project.count }.from(3).to(1)
75
76
  end
76
77
 
77
- it "destroy the record" do
78
- expect {
78
+ it 'destroy the record' do
79
+ expect do
79
80
  MultiTenant.with(account) do
80
81
  project1.destroy
81
82
  Project.destroy(project2.id)
82
83
  end
83
- }.to change { Project.count }.from(3).to(1)
84
+ end.to change { Project.count }.from(3).to(1)
84
85
  end
85
86
 
86
- it "destroy the record without a current tenant" do
87
- expect {
87
+ it 'destroy the record without a current tenant' do
88
+ expect do
88
89
  project1.destroy
89
90
  Project.destroy(project2.id)
90
- }.to change { Project.count }.from(3).to(1)
91
+ end.to change { Project.count }.from(3).to(1)
91
92
  end
92
93
  end
93
94
 
94
- context "when update without arel" do
95
- it "can call method" do
96
- expect {
97
- ActiveRecord::Base.connection.update("SELECT 1")
98
- }.not_to raise_error
95
+ context 'when update without arel' do
96
+ it 'can call method' do
97
+ expect do
98
+ ActiveRecord::Base.connection.update('SELECT 1')
99
+ end.not_to raise_error
99
100
  end
100
101
  end
101
102
 
102
- context "when joining with a model with a default scope" do
103
- let!(:account) { Account.create!(name: "Test Account") }
103
+ context 'when joining with a model with a default scope' do
104
+ let!(:account) { Account.create!(name: 'Test Account') }
104
105
 
105
- it "fetches only records within the default scope" do
106
- alive = Domain.create(name: "alive", account: account)
107
- deleted = Domain.create(name: "deleted", deleted: true, account: account)
108
- page_in_alive_domain = Page.create(name: "alive", account: account, domain: alive)
109
- page_in_deleted_domain = Page.create(name: "deleted", account: account, domain: deleted)
106
+ it 'fetches only records within the default scope' do
107
+ alive = Domain.create(name: 'alive', account: account)
108
+ deleted = Domain.create(name: 'deleted', deleted: true, account: account)
109
+ page_in_alive_domain = Page.create(name: 'alive', account: account, domain: alive)
110
+ Page.create(name: 'deleted', account: account, domain: deleted)
110
111
 
111
112
  expect(
112
113
  MultiTenant.with(account) do
@@ -61,36 +61,36 @@ describe MultiTenant, 'Record finding' do
61
61
  end
62
62
 
63
63
  context 'model with has_many relation through multi-tenant model' do
64
- let(:tenant_1) { Account.create! name: 'Tenant 1' }
65
- let(:project_1) { tenant_1.projects.create! }
64
+ let(:tenant1) { Account.create! name: 'Tenant 1' }
65
+ let(:project1) { tenant1.projects.create! }
66
66
 
67
- let(:tenant_2) { Account.create! name: 'Tenant 2' }
68
- let(:project_2) { tenant_2.projects.create! }
67
+ let(:tenant2) { Account.create! name: 'Tenant 2' }
68
+ let(:project2) { tenant2.projects.create! }
69
69
 
70
70
  let(:category) { Category.create! name: 'Category' }
71
71
 
72
72
  before do
73
- ProjectCategory.create! account: tenant_1, name: '1', project: project_1, category: category
74
- ProjectCategory.create! account: tenant_2, name: '2', project: project_2, category: category
73
+ ProjectCategory.create! account: tenant1, name: '1', project: project1, category: category
74
+ ProjectCategory.create! account: tenant2, name: '2', project: project2, category: category
75
75
  end
76
76
 
77
77
  it 'can get model without creating query cache' do
78
- MultiTenant.with(tenant_1) do
79
- found_category = Project.find(project_1.id).categories.to_a.first
78
+ MultiTenant.with(tenant1) do
79
+ found_category = Project.find(project1.id).categories.to_a.first
80
80
  expect(found_category).to eq(category)
81
81
  end
82
82
  end
83
83
 
84
84
  it 'can get model for other tenant' do
85
- MultiTenant.with(tenant_2) do
86
- found_category = Project.find(project_2.id).categories.to_a.first
85
+ MultiTenant.with(tenant2) do
86
+ found_category = Project.find(project2.id).categories.to_a.first
87
87
  expect(found_category).to eq(category)
88
88
  end
89
89
  end
90
90
 
91
91
  it 'can get model without current_tenant' do
92
92
  MultiTenant.without do
93
- found_category = Project.find(project_2.id).categories.to_a.first
93
+ found_category = Project.find(project2.id).categories.to_a.first
94
94
  expect(found_category).to eq(category)
95
95
  end
96
96
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe MultiTenant, 'Record modifications' do
@@ -6,7 +8,6 @@ describe MultiTenant, 'Record modifications' do
6
8
  let(:project) { Project.create! name: 'something', account: account }
7
9
  let(:project2) { Project.create! name: 'something2', account: account2, id: project.id }
8
10
 
9
-
10
11
  it 'includes the tenant_id in DELETEs when using object.destroy' do
11
12
  # two records with same id but different account_id
12
13
  # when doing project.destroy it should delete only the current one
@@ -16,7 +17,7 @@ describe MultiTenant, 'Record modifications' do
16
17
  expect(project2.account).to eq(account2)
17
18
  expect(project.id).to eq(project2.id)
18
19
 
19
- MultiTenant.without() do
20
+ MultiTenant.without do
20
21
  expect(Project.count).to eq(2)
21
22
  project.destroy
22
23
  expect(Project.count).to eq(1)
@@ -28,7 +29,6 @@ describe MultiTenant, 'Record modifications' do
28
29
  MultiTenant.with(account2) do
29
30
  expect(Project.where(id: project2.id).first).to be_present
30
31
  end
31
-
32
32
  end
33
33
 
34
34
  it 'includes the tenant_id in DELETEs when using object.delete' do
@@ -40,7 +40,7 @@ describe MultiTenant, 'Record modifications' do
40
40
  expect(project2.account).to eq(account2)
41
41
  expect(project.id).to eq(project2.id)
42
42
 
43
- MultiTenant.without() do
43
+ MultiTenant.without do
44
44
  expect(Project.count).to eq(2)
45
45
  project.delete
46
46
  expect(Project.count).to eq(1)
@@ -54,6 +54,25 @@ describe MultiTenant, 'Record modifications' do
54
54
  end
55
55
  end
56
56
 
57
+ it 'should not update other objects with same id when calling object.update_columns' do
58
+ # When two records with same id but different account_id are updated, it should only update the current one
59
+ expect(project.account).to eq(account)
60
+ expect(project2.account).to eq(account2)
61
+ expect(project.id).to eq(project2.id)
62
+
63
+ MultiTenant.without do
64
+ project2.update_columns(name: 'newthing2')
65
+ expect(project.reload.name).to eq('something')
66
+ expect(project2.reload.name).to eq('newthing2')
67
+ end
68
+ end
69
+
70
+ it 'should return the same object when calling object.reload' do
71
+ # When two records with same id but different account_id are updated, it should not return the other object
72
+ expect(project.reload.account_id).to eq(account.id)
73
+ expect(project2.reload.account_id).to eq(account2.id)
74
+ end
75
+
57
76
  it 'test delete for reference tables' do
58
77
  category1 = Category.create! name: 'Category 1'
59
78
  expect(Category.count).to eq(1)
@@ -5,29 +5,29 @@ require 'activerecord-multi-tenant/sidekiq'
5
5
  describe MultiTenant, 'Sidekiq' do
6
6
  let(:server) { Sidekiq::Middleware::MultiTenant::Server.new }
7
7
  let(:account) { Account.create(name: 'test') }
8
- let(:deleted_acount) { Account.create(name: 'deleted') }
8
+ let(:deleted_account) { Account.create(name: 'deleted') }
9
9
 
10
- before { deleted_acount.destroy! }
10
+ before { deleted_account.destroy! }
11
11
 
12
12
  describe 'server middleware' do
13
13
  it 'sets the multitenant context when provided in message' do
14
- server.call(double,{'bogus' => 'message',
15
- 'multi_tenant' => { 'class' => account.class.name, 'id' => account.id}},
16
- 'bogus_queue') do
14
+ server.call(double, { 'bogus' => 'message',
15
+ 'multi_tenant' => { 'class' => account.class.name, 'id' => account.id } },
16
+ 'bogus_queue') do
17
17
  expect(MultiTenant.current_tenant).to eq(account)
18
18
  end
19
19
  end
20
20
 
21
21
  it 'sets the multitenant context (id) even if tenant not found' do
22
- server.call(double,{'bogus' => 'message',
23
- 'multi_tenant' => { 'class' => deleted_acount.class.name, 'id' => deleted_acount.id}},
24
- 'bogus_queue') do
25
- expect(MultiTenant.current_tenant).to eq(deleted_acount.id)
22
+ server.call(double, { 'bogus' => 'message',
23
+ 'multi_tenant' => { 'class' => deleted_account.class.name, 'id' => deleted_account.id } },
24
+ 'bogus_queue') do
25
+ expect(MultiTenant.current_tenant).to eq(deleted_account.id)
26
26
  end
27
27
  end
28
28
 
29
29
  it 'does not set the multitenant context when no tenant provided' do
30
- server.call(double, {'bogus' => 'message'}, 'bogus_queue') do
30
+ server.call(double, { 'bogus' => 'message' }, 'bogus_queue') do
31
31
  expect(MultiTenant.current_tenant).to be_nil
32
32
  end
33
33
  end
data/spec/database.yml CHANGED
File without changes
data/spec/schema.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Resets the database, except when we are only running a specific spec
2
4
  ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
3
5
  enable_extension_on_all_nodes 'uuid-ossp'
@@ -7,6 +9,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
7
9
  t.column :name, :string
8
10
  t.column :subdomain, :string
9
11
  t.column :domain, :string
12
+ t.column :password, :string
10
13
  end
11
14
 
12
15
  create_table :projects, force: true, partition_key: :account_id do |t|
@@ -27,6 +30,17 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
27
30
  t.column :completed, :boolean
28
31
  end
29
32
 
33
+ create_table :managers_tasks, force: true, partition_key: :account_id do |t|
34
+ t.column :account_id, :integer
35
+ t.column :manager_id, :integer
36
+ t.column :task_id, :integer
37
+ end
38
+
39
+ create_table :managers_projects, force: true do |t|
40
+ t.column :project_id, :integer
41
+ t.column :manager_id, :integer
42
+ end
43
+
30
44
  create_table :sub_tasks, force: true, partition_key: :account_id do |t|
31
45
  t.column :account_id, :integer
32
46
  t.column :name, :string
@@ -34,6 +48,13 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
34
48
  t.column :type, :string
35
49
  end
36
50
 
51
+ create_table :optional_sub_tasks, force: true do |t|
52
+ t.references :account, :integer
53
+ t.column :sub_task_id, :integer
54
+ t.column :name, :string
55
+ t.column :type, :string
56
+ end
57
+
37
58
  create_table :countries, force: true do |t|
38
59
  t.column :name, :string
39
60
  end
@@ -106,6 +127,11 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
106
127
  t.column :domain_id, :integer
107
128
  end
108
129
 
130
+ create_table :posts, force: true, partition_key: :account_id do |t|
131
+ t.column :account_id, :integer
132
+ t.column :name, :string
133
+ end
134
+
109
135
  create_distributed_table :accounts, :id
110
136
  create_distributed_table :projects, :account_id
111
137
  create_distributed_table :managers, :account_id
@@ -121,6 +147,7 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
121
147
  create_distributed_table :allowed_places, :account_id
122
148
  create_distributed_table :domains, :account_id
123
149
  create_distributed_table :pages, :account_id
150
+ create_distributed_table :posts, :account_id
124
151
  create_reference_table :categories
125
152
  end
126
153
 
@@ -128,6 +155,7 @@ class Account < ActiveRecord::Base
128
155
  multi_tenant :account
129
156
  has_many :projects
130
157
  has_one :manager, inverse_of: :account
158
+ has_many :optional_sub_tasks
131
159
  end
132
160
 
133
161
  class Project < ActiveRecord::Base
@@ -138,6 +166,7 @@ class Project < ActiveRecord::Base
138
166
 
139
167
  has_many :project_categories
140
168
  has_many :categories, through: :project_categories
169
+ has_and_belongs_to_many :managers
141
170
 
142
171
  validates_uniqueness_of :name, scope: [:account]
143
172
  end
@@ -145,12 +174,15 @@ end
145
174
  class Manager < ActiveRecord::Base
146
175
  multi_tenant :account
147
176
  belongs_to :project
177
+ has_and_belongs_to_many :tasks, tenant_column: :account_id, tenant_enabled: true,
178
+ tenant_class_name: 'Account'
148
179
  end
149
180
 
150
181
  class Task < ActiveRecord::Base
151
182
  multi_tenant :account
152
183
  belongs_to :project
153
184
  has_many :sub_tasks
185
+ has_and_belongs_to_many :managers, tenant_column: :account_id, tenant_enabled: true
154
186
 
155
187
  validates_uniqueness_of :name
156
188
  end
@@ -159,6 +191,14 @@ class SubTask < ActiveRecord::Base
159
191
  multi_tenant :account
160
192
  belongs_to :task
161
193
  has_one :project, through: :task
194
+ has_many :optional_sub_tasks
195
+ end
196
+
197
+ with_belongs_to_required_by_default do
198
+ class OptionalSubTask < ActiveRecord::Base
199
+ multi_tenant :account, optional: true
200
+ belongs_to :sub_task
201
+ end
162
202
  end
163
203
 
164
204
  class StiSubTask < SubTask
@@ -194,10 +234,11 @@ end
194
234
  class Comment < ActiveRecord::Base
195
235
  multi_tenant :account
196
236
  belongs_to :commentable, polymorphic: true
197
- belongs_to :task, -> { where(comments: { commentable_type: 'Task' }) }, foreign_key: 'commentable_id'
237
+ belongs_to :task, -> { where(comments: { commentable_type: 'Task' }) }, foreign_key: 'commentable_id'
198
238
  end
199
239
 
200
240
  class Organization < ActiveRecord::Base
241
+ multi_tenant :organization
201
242
  has_many :uuid_records
202
243
  end
203
244
 
@@ -206,7 +247,7 @@ class UuidRecord < ActiveRecord::Base
206
247
  end
207
248
 
208
249
  class Category < ActiveRecord::Base
209
- has_many :project_categories
250
+ has_many :project_categories
210
251
  has_many :projects, through: :project_categories
211
252
  end
212
253