rails-tenantify

Modern row-level multi-tenancy for Rails 7+ / Ruby 3.1+

The RubyGems package is rails-tenantify. The library is required as tenantify (same pattern as rails-personapersona).

CI

Tenantify is a maintained alternative to acts_as_tenant: automatic model scoping, controller resolution, background-job context, bulk-write guards, and test helpers — built for Rails 7 and 8.

Installation

Add to your Gemfile:

gem "rails-tenantify"
bundle install

Create config/initializers/tenantify.rb:

Tenantify.configure do |config|
  config.tenant_model = "Organization"
  config.on_tenant_not_found = :raise   # :raise, :redirect, :null_tenant
  config.audit_overrides = :log         # :log, :raise, :ignore
end

Quick start

Models

class Project < ApplicationRecord
  include Tenantify::Scoped
  belongs_to_tenant :organization
end
  • Adds a default_scope for the current tenant
  • Sets the tenant foreign key on create
  • Validates the tenant cannot change after create
  • Validates associated records belong to the same tenant

Controllers

class ApplicationController < ActionController::Base
  include Tenantify::Controller

  set_tenant_by :subdomain
  # set_tenant_by :header, header: "X-Tenant-ID"
end

Resolvers live under Tenantify::Resolvers (Subdomain, Header). JWT and custom-domain resolvers are planned for upcoming releases.

Background jobs

class ReportJob < ApplicationJob
  def perform
    Tenantify.current_tenant  # restored from enqueue time
  end
end

Tenantify::Job is included automatically for ActiveJob. Sidekiq workers get tenant metadata via middleware when Sidekiq is present.

Switching context

Tenantify.switch_to(organization) do
  Project.all # scoped to organization
end

Tenantify.without_tenant do
  Project.delete_all # bypasses bulk-write protection
end

Tests

RSpec.configure do |config|
  config.include Tenantify::TestHelpers
end

with_tenant(organization) do
  Project.create!(name: "Demo")
end

Bulk-write protection

update_all, delete_all, and destroy_all on tenant-scoped models raise Tenantify::TenantMismatchError unless the relation is already scoped to the current tenant, or you use Tenantify.without_tenant.

Errors

Error When
Tenantify::TenantNotFoundError Resolver cannot find a tenant
Tenantify::TenantMismatchError Unsafe bulk write without tenant scope
Tenantify::TenantOverrideError Unsafe current_tenant= when audit_overrides is :raise

Roadmap

Version Focus
0.1.0 Core scoping, subdomain/header resolvers, ActiveJob, Sidekiq, test helpers
0.2.0 GoodJob, Solid Queue
0.3.0 JWT resolver, API improvements
0.4.0 Custom domains, Active Storage
1.0.0 Stable API, full docs

See CHANGELOG.md for release notes.

Development

bundle install
bundle exec rspec

Contributing

Bug reports and pull requests are welcome at github.com/sghani001/rails-tenantify.

License

MIT — see LICENSE.