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-persona → persona).
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_scopefor 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.