Tenants in Rails

If I had to guess, this should be made into a Gem. Since I'm lacking the time and effort it would take to do this, I am just going to put it up here. Please feel free to read along, I'm going to create a module in lib/ that should be a simple plugin to create a tenant based rails system. The tenant should be abstract and unobtrusive. Can you make improvements? Please feel free to share. This is also assuming a fresh Ruby on Rails app generated as is or close (version 4.2.3 as of this rant).

  1. Create a Tenant model by running the following command:
    rails g tenant name host token
  2. In your lib/ directory, create a file and name it "tenant_scope.rb"

    module TenantScope
      extend self
      class Error < StandardError
      def current
      def current=(tenant)
        threadsafe_storage[:current_tenant] = tenant
      def with(tenant)
        previous_scope = current
    raise Error.new("Tenant can't be nil in #{self.name}.with") if tenant.nil?
    self.current = tenant
    yield(current) if block_given?
    ensure self.current = previous_scope nil end private def threadsafe_storage Thread.current[:tenant_scope] ||= {} end end
  3. Create a directory in lib/ called "tenant_scope" and place the following files & content into this directory

    1. model_mixin.rb

      module TenantScope
        module ModelMixin
          def self.included(base)
            base.belongs_to :tenant
            base.validates_presence_of :tenant_id
            base.send(:default_scope, -> {
              if TenantScope.current
                return base.where("#{base.table_name}.tenant_id" => TenantScope.current.id)
              raise Error.new('Scoped class method called without a tenant present')
    2. rack.rb # TODO: This assumes a host (tld.com) as your tenant, YMMV. An easy first run test could be Tenant.first (just make sure you have a tenant)

      module TenantScope
        class Rack
          attr_reader :request
          def initialize(app)
            @app = app
          def call(env)
            @request = ::Rack::Request.new(env)
            unless tenant = Tenant.find_by(domain: host.split(".").last(2).join("."))
              logger.error "[TenantScope] tenant not found: #{request.host}"
              return [404, {'Content-Type' => 'text/plain', 'Content-Length' => '29' }, ["This tenant does not exist"]]
            logger.debug "[TenantScope] tenant found: #{tenant.name}"
            TenantScope.with(tenant) do
          def logger
  4. Time to wire it up. Add the two following lines to your application.rb

        config.autoload_paths += %W(#{config.root}/lib)
        config.middleware.insert_before "ActionDispatch::Static", "TenantScope::Rack"

Now you should be able to fire up localhost (depending on how you are finding your tenant - as this assumes it's a top level domain). If you're not using POW, then just find the first Tenant until you determine the true pass through algorithms of your tenants.

Now the models that you want to restrict to only a tenant, you will just add this to the file.

include TenantScope::ModelMixin

Now if you really want to console along, all you have to do is set your TenantScope in your console session.

TenantScope.current = Tenant.first

If you have your models all "tenantized" then you should be able to do cool stuff, very simply, the way you're used to.

Model.create(attr: "Whatever")

You will notice that the models properly update and assign things to the stuff that it should be assigning things to (namely the tenant).

Go create some amazing stuff!