Skip to content

Commit 1368c9f

Browse files
committed
Tenant.while_tenanted and .current_tenant
current_tenant wraps current_shard, and we use a sentinel value to indicate when the class is untenanted. Also restructured the test scenarios a bit, dropped the scenario from the migrations paths, and expanded scenarios to cover a primary database named both "primary" and something custom.
1 parent baaa948 commit 1368c9f

File tree

15 files changed

+163
-11
lines changed

15 files changed

+163
-11
lines changed

lib/active_record/tenanted.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88

99
module ActiveRecord
1010
module Tenanted
11+
# Base exception class for the library.
1112
class Error < StandardError; end
13+
14+
# Raised when database access is attempted without a current tenant having been set.
15+
class NoTenantError < Error; end
1216
end
1317
end
1418

lib/active_record/tenanted/tenant.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,34 @@ module ActiveRecord
44
module Tenanted
55
module Tenant
66
extend ActiveSupport::Concern
7+
8+
# This is a sentinel value used to indicate that the class is not currently tenanted.
9+
#
10+
# It's the default value returned by `current_shard` when the class is not tenanted. The
11+
# `current_tenant` method's job is to recognizes that sentinel value and return `nil`, because
12+
# Active Record itself does not recognize `nil` as a valid shard value.
13+
UNTENANTED_SENTINEL = Object.new # :nodoc:
14+
15+
included do
16+
connecting_to(shard: UNTENANTED_SENTINEL, role: ActiveRecord.writing_role)
17+
end
18+
19+
class_methods do
20+
def current_tenant
21+
shard = current_shard
22+
shard != UNTENANTED_SENTINEL ? shard.to_s : nil
23+
end
24+
25+
def while_tenanted(tenant_name, &block)
26+
connected_to(shard: tenant_name, role: ActiveRecord.writing_role) do
27+
prohibit_shard_swapping(true, &block)
28+
end
29+
end
30+
31+
def connection_pool
32+
raise NoTenantError unless current_tenant
33+
end
34+
end
735
end
836
end
937
end
Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
#
2+
# this test config has a primary database named "primary"
3+
#
14
test:
2-
tenanted:
5+
primary:
36
tenanted: true
47
adapter: "sqlite3"
5-
database: "%{storage}/test/tenanted/%%{tenant}/main.sqlite3"
6-
migrations_paths: "%{__dir__}/%{scenario}/tenanted_migrations"
8+
database: "%{storage}/test/primary/%%{tenant}/main.sqlite3"
9+
migrations_paths: "%{__dir__}/primary_migrations"
710
shared:
811
adapter: "sqlite3"
9-
database: "%{storage}/test/shared/%%{tenant}/main.sqlite3"
10-
migrations_paths: "%{__dir__}/%{scenario}/shared_migrations"
12+
database: "%{storage}/test/shared.sqlite3"
13+
migrations_paths: "%{__dir__}/shared_migrations"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateUsers < ActiveRecord::Migration[8.1]
2+
def change
3+
create_table :users do |t|
4+
t.string :email
5+
6+
t.timestamps
7+
end
8+
end
9+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateAnnouncements < ActiveRecord::Migration[8.1]
2+
def change
3+
create_table :announcements do |t|
4+
t.string :message
5+
6+
t.timestamps
7+
end
8+
end
9+
end

test/scenarios/vanilla/tenanted_secondary.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
class TenantedApplicationRecord < ActiveRecord::Base
44
self.abstract_class = true
55

6-
tenanted "tenanted"
6+
tenanted
77
end
88

99
class User < TenantedApplicationRecord
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#
2+
# this test config has a primary database named "tenanted"
3+
#
4+
test:
5+
tenanted:
6+
tenanted: true
7+
adapter: "sqlite3"
8+
database: "%{storage}/test/tenanted/%%{tenant}/main.sqlite3"
9+
migrations_paths: "%{__dir__}/tenanted_migrations"
10+
shared:
11+
adapter: "sqlite3"
12+
database: "%{storage}/test/shared.sqlite3"
13+
migrations_paths: "%{__dir__}/shared_migrations"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateAnnouncements < ActiveRecord::Migration[8.1]
2+
def change
3+
create_table :announcements do |t|
4+
t.string :message
5+
6+
t.timestamps
7+
end
8+
end
9+
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class CreateUsers < ActiveRecord::Migration[8.1]
2+
def change
3+
create_table :users do |t|
4+
t.string :email
5+
6+
t.timestamps
7+
end
8+
end
9+
end
File renamed without changes.

0 commit comments

Comments
 (0)