Project

sublet

0.0
The project is in a healthy, maintained state
Sublet lets you declare a tenant root and subletters that 'sublet from' a parent association—even chaining through multiple levels. Developers set current_tenant themselves in controllers; Sublet scopes AR queries automatically (including admin filters) without adding tenant_id everywhere.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Runtime

 Project Readme

Sublet

Hierarchy-aware, opt-in multi-tenancy for ActiveRecord.

  • Declare a tenant root with sublet.
  • Declare subletters with subletter(:parent_assoc) — the parent can be the tenant or another subletter.
  • You set Sublet.current_tenant yourself (only in the controllers you want).
  • Sublet auto-scopes queries via default_scope so admin filters, counts, etc. follow the tenant.
  • Fails open when tenant chains can't be resolved (no scoping applied).
  • Includes automatic validation to prevent tenant mismatches.

Install

Add this line to your application's Gemfile:

gem "sublet", "~> 0.1.0"

And then execute:

bundle install

Or install it yourself as:

gem install sublet

Requirements

  • Ruby >= 3.1.0
  • ActiveRecord >= 6.1
  • ActiveSupport >= 6.1

Usage

Basic Setup

class Company < ApplicationRecord
  include Sublet::Model
  sublet
end

class Department < ApplicationRecord
  include Sublet::Model
  subletter(:company)
  belongs_to :company
end

class Employee < ApplicationRecord
  include Sublet::Model
  subletter(:department)
  belongs_to :department
  has_one :company, through: :department
end

Controller Integration

In a controller where you want tenant scoping:

class Admin::BaseController < ApplicationController
  before_action do
    Sublet.current_tenant = current_user.company
  end
end

Or use the included controller helper:

class Admin::BaseController < ApplicationController
  before_action do
    set_current_sublet(current_user.company)
  end
end

Temporary Tenant Switching

# Temporarily switch to a different tenant
Sublet.with_tenant(Company.find(1)) do
  Employee.count  # Scoped to Company 1
end

# Temporarily bypass tenant scoping
Sublet.without_tenant do
  Employee.unscoped.count  # All employees across all tenants
end

Advanced Examples

Multi-level Hierarchy

class Account < ApplicationRecord
  include Sublet::Model
  sublet
end

class Company < ApplicationRecord
  include Sublet::Model
  subletter(:account)
  belongs_to :account
end

class Department < ApplicationRecord
  include Sublet::Model
  subletter(:company)
  belongs_to :company
end

class Employee < ApplicationRecord
  include Sublet::Model
  subletter(:department)
  belongs_to :department
end

Tenant Without Scoping

class Company < ApplicationRecord
  include Sublet::Model
  sublet(scope: false)  # Declare as tenant but don't auto-scope
end

Validation

Sublet automatically validates that records belong to the current tenant:

# This will fail validation if the employee doesn't belong to current_tenant
employee = Employee.new(department: wrong_department)
employee.valid?  # => false
employee.errors[:base]  # => ["Tenant mismatch"]

Key Features

  • Hierarchy-aware: Supports multi-level tenant hierarchies (Account → Company → Department → Employee)
  • Opt-in: Only applies scoping where you explicitly set current_tenant
  • Fails open: If tenant chain can't be resolved, no scoping is applied (safe default)
  • Automatic validation: Prevents tenant mismatches during record creation/updates
  • Controller helpers: Includes set_current_sublet helper method
  • Temporary switching: with_tenant and without_tenant for temporary context changes
  • STI-safe: Works correctly with Single Table Inheritance

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/gogrow-dev/sublet. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Notes

  • If a subletter chain can't be resolved to a tenant, Sublet fails open (no scoping).
  • Tenants themselves are not scoped by default (use sublet(scope: true) to enable).
  • The gem automatically includes controller helpers when Rails is present.