activerecord-multi-tenant 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +73 -0
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Appraisals +16 -0
- data/CHANGELOG.md +28 -0
- data/activerecord-multi-tenant.gemspec +1 -2
- data/gemfiles/active_record_5.2.3.gemfile +16 -0
- data/gemfiles/active_record_6.1.gemfile +8 -0
- data/gemfiles/active_record_7.0.gemfile +8 -0
- data/gemfiles/rails_5.2.3.gemfile +16 -0
- data/gemfiles/rails_6.1.gemfile +8 -0
- data/gemfiles/rails_7.0.gemfile +8 -0
- data/lib/activerecord-multi-tenant/arel_visitors_depth_first.rb +200 -0
- data/lib/activerecord-multi-tenant/copy_from_client.rb +2 -2
- data/lib/activerecord-multi-tenant/model_extensions.rb +16 -4
- data/lib/activerecord-multi-tenant/multi_tenant.rb +22 -8
- data/lib/activerecord-multi-tenant/query_rewriter.rb +14 -9
- data/lib/activerecord-multi-tenant/sidekiq.rb +5 -1
- data/lib/activerecord-multi-tenant/version.rb +1 -1
- data/spec/activerecord-multi-tenant/model_extensions_spec.rb +109 -50
- data/spec/activerecord-multi-tenant/query_rewriter_spec.rb +17 -0
- data/spec/activerecord-multi-tenant/record_finding_spec.rb +36 -0
- data/spec/activerecord-multi-tenant/sidekiq_spec.rb +11 -0
- data/spec/schema.rb +51 -4
- data/spec/spec_helper.rb +7 -0
- metadata +14 -25
- data/.travis.yml +0 -32
- data/Gemfile.lock +0 -197
- data/gemfiles/active_record_5.2.gemfile.lock +0 -188
- data/gemfiles/active_record_6.0.gemfile.lock +0 -198
- data/gemfiles/rails_5.2.gemfile.lock +0 -188
- data/gemfiles/rails_6.0.gemfile.lock +0 -198
@@ -70,6 +70,47 @@ describe MultiTenant do
|
|
70
70
|
it { expect(@partition_key_not_model_task.non_model_id).to be 77 }
|
71
71
|
end
|
72
72
|
|
73
|
+
describe 'Changes table_name after multi_tenant called' do
|
74
|
+
before do
|
75
|
+
account_klass.has_many(:posts, anonymous_class: post_klass)
|
76
|
+
post_klass.belongs_to(:account, anonymous_class: account_klass)
|
77
|
+
|
78
|
+
@account1 = account_klass.create! name: 'foo'
|
79
|
+
@account2 = account_klass.create! name: 'bar'
|
80
|
+
|
81
|
+
@post1 = @account1.posts.create! name: 'foobar'
|
82
|
+
@post2 = @account2.posts.create! name: 'baz'
|
83
|
+
|
84
|
+
MultiTenant.current_tenant = @account1
|
85
|
+
@posts = post_klass.all
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:account_klass) do
|
89
|
+
Class.new(Account) do
|
90
|
+
def self.name
|
91
|
+
'Account'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
let(:post_klass) do
|
97
|
+
Class.new(ActiveRecord::Base) do
|
98
|
+
self.table_name = 'unknown'
|
99
|
+
|
100
|
+
multi_tenant(:account)
|
101
|
+
|
102
|
+
self.table_name = 'posts'
|
103
|
+
|
104
|
+
def self.name
|
105
|
+
'Post'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it { expect(@posts.length).to eq(1) }
|
111
|
+
it { expect(@posts).to eq([@post1]) }
|
112
|
+
end
|
113
|
+
|
73
114
|
# Scoping models
|
74
115
|
describe 'Project.all should be scoped to the current tenant if set' do
|
75
116
|
before do
|
@@ -144,6 +185,16 @@ describe MultiTenant do
|
|
144
185
|
end
|
145
186
|
end
|
146
187
|
|
188
|
+
it 'handles belongs_to with optional: true' do
|
189
|
+
MultiTenant.with(account) do
|
190
|
+
sub_task
|
191
|
+
end
|
192
|
+
|
193
|
+
record = sub_task.optional_sub_tasks.create!
|
194
|
+
expect(record.reload.sub_task).to eq(sub_task)
|
195
|
+
expect(record.account_id).to eq(nil)
|
196
|
+
end
|
197
|
+
|
147
198
|
it 'handles has_many through' do
|
148
199
|
MultiTenant.with(account) do
|
149
200
|
expect(project.sub_tasks).to eq [sub_task]
|
@@ -348,9 +399,12 @@ describe MultiTenant do
|
|
348
399
|
end
|
349
400
|
|
350
401
|
it "applies the team_id conditions in the where clause" do
|
351
|
-
|
352
|
-
|
353
|
-
|
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
|
354
408
|
|
355
409
|
account1 = Account.create! name: 'Account 1'
|
356
410
|
|
@@ -358,7 +412,7 @@ describe MultiTenant do
|
|
358
412
|
project1 = Project.create! name: 'Project 1'
|
359
413
|
task1 = Task.create! name: 'Task 1', project: project1
|
360
414
|
subtask1 = SubTask.create! task: task1
|
361
|
-
expect(project1.sub_tasks.to_sql).to eq(
|
415
|
+
expect(project1.sub_tasks.to_sql).to eq(option1).or(eq(option2))
|
362
416
|
expect(project1.sub_tasks).to include(subtask1)
|
363
417
|
end
|
364
418
|
|
@@ -373,9 +427,13 @@ describe MultiTenant do
|
|
373
427
|
end
|
374
428
|
|
375
429
|
it "tests joins between distributed and reference table" do
|
376
|
-
|
377
|
-
|
378
|
-
|
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
|
436
|
+
|
379
437
|
account1 = Account.create! name: 'Account 1'
|
380
438
|
category1 = Category.create! name: 'Category 1'
|
381
439
|
|
@@ -383,7 +441,7 @@ describe MultiTenant do
|
|
383
441
|
project1 = Project.create! name: 'Project 1'
|
384
442
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
385
443
|
|
386
|
-
expect(project1.categories.to_sql).to eq(
|
444
|
+
expect(project1.categories.to_sql).to eq(option1).or(eq(option2))
|
387
445
|
expect(project1.categories).to include(category1)
|
388
446
|
expect(project1.project_categories).to include(projectcategory)
|
389
447
|
end
|
@@ -412,21 +470,18 @@ describe MultiTenant do
|
|
412
470
|
account1 = Account.create! name: 'Account 1'
|
413
471
|
category1 = Category.create! name: 'Category 1'
|
414
472
|
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
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
|
422
|
-
sql
|
423
|
-
end
|
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
|
424
479
|
|
425
480
|
MultiTenant.with(account1) do
|
426
481
|
project1 = Project.create! name: 'Project 1'
|
427
482
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
428
483
|
|
429
|
-
expect(Project.eager_load(:categories).to_sql).to eq(
|
484
|
+
expect(Project.eager_load(:categories).to_sql).to eq(option1).or(eq(option2))
|
430
485
|
|
431
486
|
project = Project.eager_load(:categories).first
|
432
487
|
expect(project.categories).to include(category1)
|
@@ -451,21 +506,18 @@ describe MultiTenant do
|
|
451
506
|
category1 = Category.create! name: 'Category 1'
|
452
507
|
|
453
508
|
MultiTenant.with(account1) do
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
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
|
461
|
-
sql
|
462
|
-
end
|
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
|
463
515
|
|
464
516
|
project1 = Project.create! name: 'Project 1'
|
465
517
|
projectcategory = ProjectCategory.create! name: 'project cat 1', project: project1, category: category1
|
466
518
|
|
467
519
|
project1.tasks.create! name: 'baz'
|
468
|
-
expect(Task.joins(:project).joins('LEFT JOIN project_categories pc ON project.category_id = pc.id').to_sql).to eq(
|
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))
|
469
521
|
end
|
470
522
|
|
471
523
|
MultiTenant.without do
|
@@ -485,32 +537,39 @@ describe MultiTenant do
|
|
485
537
|
project2 = Project.create! name: 'Project 2', account: Account.create!(name: 'Account2')
|
486
538
|
|
487
539
|
MultiTenant.with(account) do
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
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
|
549
|
+
|
550
|
+
# 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
|
552
|
+
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])
|
555
|
+
end
|
499
556
|
expect(Project.find(project.id)).to eq(project)
|
500
557
|
end
|
501
558
|
|
502
559
|
MultiTenant.without do
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
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
|
566
|
+
|
567
|
+
# Couldn't make the following line pass for some reason, so came up with an uglier alternative
|
568
|
+
# expect(Project).to receive(:find_by_sql).with(eq(option1).or(eq(option2)), any_args).and_call_original
|
569
|
+
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])
|
572
|
+
end
|
514
573
|
expect(Project.find(project2.id)).to eq(project2)
|
515
574
|
end
|
516
575
|
end
|
@@ -98,4 +98,21 @@ describe "Query Rewriter" do
|
|
98
98
|
}.not_to raise_error
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
context "when joining with a model with a default scope" do
|
103
|
+
let!(:account) { Account.create!(name: "Test Account") }
|
104
|
+
|
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)
|
110
|
+
|
111
|
+
expect(
|
112
|
+
MultiTenant.with(account) do
|
113
|
+
Page.joins(:domain).pluck(:id)
|
114
|
+
end
|
115
|
+
).to eq([page_in_alive_domain.id])
|
116
|
+
end
|
117
|
+
end
|
101
118
|
end
|
@@ -59,4 +59,40 @@ describe MultiTenant, 'Record finding' do
|
|
59
59
|
expect(second_found).to eq(second_record)
|
60
60
|
end
|
61
61
|
end
|
62
|
+
|
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! }
|
66
|
+
|
67
|
+
let(:tenant_2) { Account.create! name: 'Tenant 2' }
|
68
|
+
let(:project_2) { tenant_2.projects.create! }
|
69
|
+
|
70
|
+
let(:category) { Category.create! name: 'Category' }
|
71
|
+
|
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
|
75
|
+
end
|
76
|
+
|
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
|
80
|
+
expect(found_category).to eq(category)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
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
|
87
|
+
expect(found_category).to eq(category)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'can get model without current_tenant' do
|
92
|
+
MultiTenant.without do
|
93
|
+
found_category = Project.find(project_2.id).categories.to_a.first
|
94
|
+
expect(found_category).to eq(category)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
62
98
|
end
|
@@ -5,6 +5,9 @@ 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') }
|
9
|
+
|
10
|
+
before { deleted_acount.destroy! }
|
8
11
|
|
9
12
|
describe 'server middleware' do
|
10
13
|
it 'sets the multitenant context when provided in message' do
|
@@ -15,6 +18,14 @@ describe MultiTenant, 'Sidekiq' do
|
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
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)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
18
29
|
it 'does not set the multitenant context when no tenant provided' do
|
19
30
|
server.call(double, {'bogus' => 'message'}, 'bogus_queue') do
|
20
31
|
expect(MultiTenant.current_tenant).to be_nil
|
data/spec/schema.rb
CHANGED
@@ -34,6 +34,13 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
34
34
|
t.column :type, :string
|
35
35
|
end
|
36
36
|
|
37
|
+
create_table :optional_sub_tasks, force: true do |t|
|
38
|
+
t.references :account, :integer
|
39
|
+
t.column :sub_task_id, :integer
|
40
|
+
t.column :name, :string
|
41
|
+
t.column :type, :string
|
42
|
+
end
|
43
|
+
|
37
44
|
create_table :countries, force: true do |t|
|
38
45
|
t.column :name, :string
|
39
46
|
end
|
@@ -89,10 +96,26 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
89
96
|
t.column :category_id, :integer
|
90
97
|
end
|
91
98
|
|
92
|
-
|
93
99
|
create_table :allowed_places, force: true, id: false do |t|
|
94
|
-
|
95
|
-
|
100
|
+
t.string :account_id, :integer
|
101
|
+
t.string :name, :string
|
102
|
+
end
|
103
|
+
|
104
|
+
create_table :domains, force: true, partition_key: :account_id do |t|
|
105
|
+
t.column :account_id, :integer
|
106
|
+
t.column :name, :string
|
107
|
+
t.column :deleted, :boolean, default: false
|
108
|
+
end
|
109
|
+
|
110
|
+
create_table :pages, force: true, partition_key: :account_id do |t|
|
111
|
+
t.column :account_id, :integer
|
112
|
+
t.column :name, :string
|
113
|
+
t.column :domain_id, :integer
|
114
|
+
end
|
115
|
+
|
116
|
+
create_table :posts, force: true, partition_key: :account_id do |t|
|
117
|
+
t.column :account_id, :integer
|
118
|
+
t.column :name, :string
|
96
119
|
end
|
97
120
|
|
98
121
|
create_distributed_table :accounts, :id
|
@@ -108,6 +131,9 @@ ARGV.grep(/\w+_spec\.rb/).empty? && ActiveRecord::Schema.define(version: 1) do
|
|
108
131
|
create_distributed_table :uuid_records, :organization_id
|
109
132
|
create_distributed_table :project_categories, :account_id
|
110
133
|
create_distributed_table :allowed_places, :account_id
|
134
|
+
create_distributed_table :domains, :account_id
|
135
|
+
create_distributed_table :pages, :account_id
|
136
|
+
create_distributed_table :posts, :account_id
|
111
137
|
create_reference_table :categories
|
112
138
|
end
|
113
139
|
|
@@ -115,6 +141,7 @@ class Account < ActiveRecord::Base
|
|
115
141
|
multi_tenant :account
|
116
142
|
has_many :projects
|
117
143
|
has_one :manager, inverse_of: :account
|
144
|
+
has_many :optional_sub_tasks
|
118
145
|
end
|
119
146
|
|
120
147
|
class Project < ActiveRecord::Base
|
@@ -146,6 +173,15 @@ class SubTask < ActiveRecord::Base
|
|
146
173
|
multi_tenant :account
|
147
174
|
belongs_to :task
|
148
175
|
has_one :project, through: :task
|
176
|
+
has_many :optional_sub_tasks
|
177
|
+
end
|
178
|
+
|
179
|
+
with_belongs_to_required_by_default do
|
180
|
+
class OptionalSubTask < ActiveRecord::Base
|
181
|
+
multi_tenant :account, optional: true
|
182
|
+
belongs_to :account, optional: true
|
183
|
+
belongs_to :sub_task
|
184
|
+
end
|
149
185
|
end
|
150
186
|
|
151
187
|
class StiSubTask < SubTask
|
@@ -185,6 +221,7 @@ class Comment < ActiveRecord::Base
|
|
185
221
|
end
|
186
222
|
|
187
223
|
class Organization < ActiveRecord::Base
|
224
|
+
multi_tenant :organization
|
188
225
|
has_many :uuid_records
|
189
226
|
end
|
190
227
|
|
@@ -204,7 +241,17 @@ class ProjectCategory < ActiveRecord::Base
|
|
204
241
|
belongs_to :account
|
205
242
|
end
|
206
243
|
|
207
|
-
|
208
244
|
class AllowedPlace < ActiveRecord::Base
|
209
245
|
multi_tenant :account
|
210
246
|
end
|
247
|
+
|
248
|
+
class Domain < ActiveRecord::Base
|
249
|
+
multi_tenant :account
|
250
|
+
has_many :pages
|
251
|
+
default_scope { where(deleted: false) }
|
252
|
+
end
|
253
|
+
|
254
|
+
class Page < ActiveRecord::Base
|
255
|
+
multi_tenant :account
|
256
|
+
belongs_to :domain
|
257
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -46,4 +46,11 @@ def uses_prepared_statements?
|
|
46
46
|
ActiveRecord::Base.connection.prepared_statements
|
47
47
|
end
|
48
48
|
|
49
|
+
def with_belongs_to_required_by_default(&block)
|
50
|
+
default_value = ActiveRecord::Base.belongs_to_required_by_default
|
51
|
+
ActiveRecord::Base.belongs_to_required_by_default = true
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
ActiveRecord::Base.belongs_to_required_by_default = default_value
|
55
|
+
end
|
49
56
|
require 'schema'
|
metadata
CHANGED
@@ -1,43 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-multi-tenant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Citus Data
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: request_store
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.0.5
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.0.5
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rails
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
17
|
- - ">="
|
32
18
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
19
|
+
version: '5.2'
|
34
20
|
type: :runtime
|
35
21
|
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
24
|
- - ">="
|
39
25
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
26
|
+
version: '5.2'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rspec
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,27 +142,30 @@ executables: []
|
|
156
142
|
extensions: []
|
157
143
|
extra_rdoc_files: []
|
158
144
|
files:
|
145
|
+
- ".github/workflows/CI.yml"
|
159
146
|
- ".gitignore"
|
160
|
-
- ".
|
147
|
+
- ".rspec"
|
161
148
|
- Appraisals
|
162
149
|
- CHANGELOG.md
|
163
150
|
- Gemfile
|
164
|
-
- Gemfile.lock
|
165
151
|
- LICENSE
|
166
152
|
- README.md
|
167
153
|
- Rakefile
|
168
154
|
- activerecord-multi-tenant.gemspec
|
169
155
|
- docker-compose.yml
|
170
156
|
- gemfiles/.bundle/config
|
157
|
+
- gemfiles/active_record_5.2.3.gemfile
|
171
158
|
- gemfiles/active_record_5.2.gemfile
|
172
|
-
- gemfiles/active_record_5.2.gemfile.lock
|
173
159
|
- gemfiles/active_record_6.0.gemfile
|
174
|
-
- gemfiles/active_record_6.
|
160
|
+
- gemfiles/active_record_6.1.gemfile
|
161
|
+
- gemfiles/active_record_7.0.gemfile
|
162
|
+
- gemfiles/rails_5.2.3.gemfile
|
175
163
|
- gemfiles/rails_5.2.gemfile
|
176
|
-
- gemfiles/rails_5.2.gemfile.lock
|
177
164
|
- gemfiles/rails_6.0.gemfile
|
178
|
-
- gemfiles/rails_6.
|
165
|
+
- gemfiles/rails_6.1.gemfile
|
166
|
+
- gemfiles/rails_7.0.gemfile
|
179
167
|
- lib/activerecord-multi-tenant.rb
|
168
|
+
- lib/activerecord-multi-tenant/arel_visitors_depth_first.rb
|
180
169
|
- lib/activerecord-multi-tenant/controller_extensions.rb
|
181
170
|
- lib/activerecord-multi-tenant/copy_from_client.rb
|
182
171
|
- lib/activerecord-multi-tenant/fast_truncate.rb
|
@@ -221,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
210
|
- !ruby/object:Gem::Version
|
222
211
|
version: '0'
|
223
212
|
requirements: []
|
224
|
-
rubygems_version: 3.
|
213
|
+
rubygems_version: 3.2.32
|
225
214
|
signing_key:
|
226
215
|
specification_version: 4
|
227
216
|
summary: ActiveRecord/Rails integration for multi-tenant databases, in particular
|
data/.travis.yml
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
sudo: required
|
2
|
-
cache: bundler
|
3
|
-
|
4
|
-
language: ruby
|
5
|
-
|
6
|
-
rvm:
|
7
|
-
- 2.5.8
|
8
|
-
- 2.6.4
|
9
|
-
- 2.7.1
|
10
|
-
|
11
|
-
gemfile:
|
12
|
-
- gemfiles/rails_5.2.gemfile
|
13
|
-
- gemfiles/rails_6.0.gemfile
|
14
|
-
- gemfiles/active_record_5.2.gemfile
|
15
|
-
- gemfiles/active_record_6.0.gemfile
|
16
|
-
|
17
|
-
env:
|
18
|
-
- PREPARED_STATEMENTS=0
|
19
|
-
- PREPARED_STATEMENTS=1
|
20
|
-
|
21
|
-
matrix:
|
22
|
-
fast_finish: true
|
23
|
-
|
24
|
-
services:
|
25
|
-
- docker
|
26
|
-
|
27
|
-
before_install:
|
28
|
-
- docker-compose up -d
|
29
|
-
- gem install bundler -v 2.1.4
|
30
|
-
|
31
|
-
script:
|
32
|
-
- bundle exec rake spec
|