Search code examples
ruby-on-railspostgresqlapartment-gem

Creating New Tenant Apartment Gem - Always restart?


I have a rails 4.2 multi-tenant app using the Apartment gem which has been awesome.

Each company has their own subdomain. I'm using a custom "elevator" which looks at the full request host to determine which "Tenant" should be loaded.

When I create a new company I have an after_create hook to create the new tenant with the proper request host.

This always seems to require a restart of the server both in development and production otherwise I get a Tenant Not Found error.

It's using sqlite in development and postgres in production.

Do I really have to restart the server each time I create a new tenant? Is there an automated way to do this? Maybe just reloading the initializer will work, but I'm not sure how to do that/if that's possible?

I have been messing around with this for a month and haven't been able to find a solution that works. Please help!

initializers/apartment.rb

require 'apartment/elevators/host_hash'

config.tenant_names = lambda { Company.pluck :request_host }

Rails.application.config.middleware.use 'Apartment::Elevators::HostHash', Company.full_hosts_hash

initializers/host_hash.rb

require 'apartment/elevators/generic'

module Apartment
    module Elevators
    class HostHash < Generic
      def initialize(app, hash = {}, processor = nil)
        super app, processor
        @hash = hash
      end

      def parse_tenant_name(request)
        if request.host.split('.').first == "www"
            nil
        else
            raise TenantNotFound,
              "Cannot find tenant for host #{request.host}" unless @hash.has_key?(request.host)
            @hash[request.host]
        end
      end
    end
    end
end 

Company Model

after_create :create_tenant


def self.full_hosts_hash
    Company.all.inject(Hash.new) do |hash, company|
      hash[company.request_host] = company.request_host
      hash
    end
end

private

    def create_tenant
        Apartment::Tenant.create(request_host)
    end

What ended up working

I changed the elevator configuration to get away from the HostHash one that's in the apartment gem and used a completely custom one. Mostly based off of an issue on the apartment gem github: https://github.com/influitive/apartment/issues/280

initializers/apartment.rb

Rails.application.config.middleware.use 'BaseSite::BaseElevator'

app/middleware/base_site.rb

require 'apartment/elevators/generic'

module BaseSite
    class BaseElevator < Apartment::Elevators::Generic
        def parse_tenant_name(request)
          company = Company.find_by_request_host(request.host)

          return company.request_host unless company.nil?

          fail StandardError, "No website found at #{request.host} not found"
        end
    end
end

Solution

  • I think the problem could be that your host_hash.rb lives in the initializers directory. Shouldn't it be in a folder called "middleware"?, as per the Apartment gem ReadME you referenced in your comment. In that example they used app/middleware/my_custom_elevator.rb. Perhaps yours might look like app/middleware/host_hash.rb?

    Right now the file is in initializers, so it's loading from there. But your apartment.rb references it by Rails.application.config.middleware.use. Just a hunch but in addition to loading it initially, it may be looking for it in a nonexistent middleware folder. I'd go ahead and create app/middleware, put the file in there instead, and see what happens. Not sure but you might need to alter require paths too.

    Let us know if that helps.