Class: PgMultitenantSchemas::Context

Inherits:
Object
  • Object
show all
Defined in:
lib/pg_multitenant_schemas/context.rb

Overview

Thread-safe tenant context management

The Context class manages the current tenant and schema in a thread-safe manner using Thread.current storage. This ensures proper isolation in multi-threaded environments like Rails servers.

Examples:

Get current schema

PgMultitenantSchemas::Context.current_schema #=> "tenant_123"

Switch to tenant context

tenant = Tenant.find(1)
PgMultitenantSchemas::Context.switch_to_tenant(tenant)

Use block-based context

PgMultitenantSchemas::Context.with_tenant(tenant) do
  User.all  # Queries tenant's schema
end

Class Method Summary collapse

Class Method Details

.create_tenant_schema(tenant_or_schema) ⇒ void

This method returns an undefined value.

Create a new PostgreSQL schema for a tenant

Examples:

With tenant object

tenant = Tenant.create!(subdomain: 'acme')
PgMultitenantSchemas::Context.create_tenant_schema(tenant)

With schema name

PgMultitenantSchemas::Context.create_tenant_schema('tenant_123')

Parameters:

  • tenant_or_schema (Object, String)

    The tenant object or schema name

Raises:

  • (ArgumentError)

    if schema name is blank



165
166
167
168
169
170
171
172
# File 'lib/pg_multitenant_schemas/context.rb', line 165

def create_tenant_schema(tenant_or_schema)
  schema_name = if tenant_or_schema.respond_to?(:subdomain)
                  tenant_or_schema.subdomain
                else
                  tenant_or_schema.to_s
                end
  SchemaSwitcher.create_schema(schema_name)
end

.current_schemaString

Get the current tenant schema name

Examples:

PgMultitenantSchemas::Context.current_schema #=> "tenant_123"

Returns:

  • (String)

    The current schema name or default schema if not set



43
44
45
# File 'lib/pg_multitenant_schemas/context.rb', line 43

def current_schema
  Thread.current[:pg_multitenant_current_schema] || PgMultitenantSchemas.configuration.default_schema
end

.current_schema=(schema_name) ⇒ String

Set the current tenant schema name

Parameters:

  • schema_name (String)

    The schema name to set as current

Returns:

  • (String)

    The schema name



51
52
53
# File 'lib/pg_multitenant_schemas/context.rb', line 51

def current_schema=(schema_name)
  Thread.current[:pg_multitenant_current_schema] = schema_name
end

.current_tenantObject?

Get the current tenant object

Returns:

  • (Object, nil)

    The current tenant or nil if no tenant context is set



26
27
28
# File 'lib/pg_multitenant_schemas/context.rb', line 26

def current_tenant
  Thread.current[:pg_multitenant_current_tenant]
end

.current_tenant=(tenant) ⇒ Object

Set the current tenant object

Parameters:

  • tenant (Object)

    The tenant object to set as current

Returns:

  • (Object)

    The tenant object



34
35
36
# File 'lib/pg_multitenant_schemas/context.rb', line 34

def current_tenant=(tenant)
  Thread.current[:pg_multitenant_current_tenant] = tenant
end

.drop_tenant_schema(tenant_or_schema, cascade: true) ⇒ void

This method returns an undefined value.

Drop a PostgreSQL schema for a tenant

Examples:

With tenant object

tenant = Tenant.find(1)
PgMultitenantSchemas::Context.drop_tenant_schema(tenant)

With cascade option

PgMultitenantSchemas::Context.drop_tenant_schema('old_tenant', cascade: true)

Parameters:

  • tenant_or_schema (Object, String)

    The tenant object or schema name

  • cascade (Boolean) (defaults to: true)

    Whether to drop dependent objects (default: true)

Raises:

  • (ArgumentError)

    if schema name is blank



185
186
187
188
189
190
191
192
# File 'lib/pg_multitenant_schemas/context.rb', line 185

def drop_tenant_schema(tenant_or_schema, cascade: true)
  schema_name = if tenant_or_schema.respond_to?(:subdomain)
                  tenant_or_schema.subdomain
                else
                  tenant_or_schema.to_s
                end
  SchemaSwitcher.drop_schema(schema_name, cascade: cascade)
end

.reset!void

This method returns an undefined value.

Reset the current tenant context to default

Clears both tenant and schema context and restores the default schema

Examples:

PgMultitenantSchemas::Context.reset!


62
63
64
65
66
# File 'lib/pg_multitenant_schemas/context.rb', line 62

def reset!
  Thread.current[:pg_multitenant_current_tenant] = nil
  Thread.current[:pg_multitenant_current_schema] = nil
  switch_to_schema(PgMultitenantSchemas.configuration.default_schema)
end

.switch_to_schema(schema_name) ⇒ void

This method returns an undefined value.

Switch to a specific schema

Examples:

PgMultitenantSchemas::Context.switch_to_schema('tenant_123')

Parameters:

  • schema_name (String)

    The schema name to switch to



74
75
76
77
78
# File 'lib/pg_multitenant_schemas/context.rb', line 74

def switch_to_schema(schema_name)
  schema_name = PgMultitenantSchemas.configuration.default_schema if schema_name.blank?
  SchemaSwitcher.switch_schema(schema_name)
  self.current_schema = schema_name
end

.switch_to_tenant(tenant) ⇒ void

This method returns an undefined value.

Switch to a specific tenant's schema

Examples:

With tenant object

tenant = Tenant.find(1)
PgMultitenantSchemas::Context.switch_to_tenant(tenant)

With schema name

PgMultitenantSchemas::Context.switch_to_tenant('tenant_123')

Parameters:

  • tenant (Object, String)

    The tenant object (must respond to :subdomain) or schema name



89
90
91
92
93
94
95
96
97
98
# File 'lib/pg_multitenant_schemas/context.rb', line 89

def switch_to_tenant(tenant)
  if tenant
    schema_name = tenant.respond_to?(:subdomain) ? tenant.subdomain : tenant.to_s
    switch_to_schema(schema_name)
    self.current_tenant = tenant
  else
    switch_to_schema(PgMultitenantSchemas.configuration.default_schema)
    self.current_tenant = nil
  end
end

.with_tenant(tenant_or_schema) { ... } ⇒ Object

Execute a block within a tenant context

All database queries within the block will be executed in the tenant's schema. The previous context is restored after the block completes, even if an error occurs.

Examples:

With tenant object

tenant = Tenant.find(1)
PgMultitenantSchemas::Context.with_tenant(tenant) do
  User.all  # Queries tenant's schema
end

With schema name

PgMultitenantSchemas::Context.with_tenant('tenant_123') do
  Order.create!(status: 'pending')
end

Parameters:

  • tenant_or_schema (Object, String)

    The tenant object or schema name

Yields:

  • Executes the given block in the tenant's schema context

Returns:

  • (Object)

    The return value of the block



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/pg_multitenant_schemas/context.rb', line 117

def with_tenant(tenant_or_schema)
  schema_name, tenant = extract_schema_and_tenant(tenant_or_schema)
  previous_tenant = current_tenant
  previous_schema = current_schema

  begin
    switch_to_schema(schema_name)
    self.current_tenant = tenant
    yield if block_given?
  ensure
    restore_previous_context(previous_tenant, previous_schema)
  end
end