# Use route-level multitenancy
Multitenancy is not a far-fetched concept, and you might need it when you reach a certain level with your app. Avo is ready to handle that.
This guide will show you one way of achieving that, but if can be changed if you have different needs.
# Prepare the Current model
We will use Rails' Current
(opens new window) model to hold the account.
# app/models/current.rb class Current < ActiveSupport::CurrentAttributes attribute :account end
Copied!
# Add middleware to catch the account param
We're trying to fetch the account number from the params
and see if we have an account with that ID in this middleware. If so, store it in the Current.account
model, where we can use it throughout the app.
## Multitenant Account Middleware # # Included in the Rails engine if enabled. # # Used for setting the Account by the first ID in the URL like Basecamp 3. # This means we don't have to include the Account ID in every URL helper. # From JumpstartRails AccountMiddleware class AccountMiddleware def initialize(app) @app = app end # http://example.com/12345/projects def call(env) request = ActionDispatch::Request.new env # Fetch the account id from the path _, account_id, request_path = request.path.split("/", 3) # Check if the id is a number if /\d+/.match?(account_id) # See if that account is present in the database. if (account = Account.find_by(id: account_id)) # If the account is present, set the Current.account to that Current.account = account else # If not, redirect to the root path return [302, {"Location" => "/"}, []] end request.script_name = "/#{account_id}" request.path_info = "/#{request_path}" end @app.call(request.env) end end
Copied!
# Update the custom tools routes
By default, when generating custom tools, we're adding them to the parent app's routes. Because we're declaring them there, the link helpers don't hold the account id in the params.
Rails.application.routes.draw do scope :avo do get "custom_page", to: "avo/tools#custom_page" end devise_for :users # Your routes authenticate :user, -> user { user.admin? } do mount Avo::Engine => Avo.configuration.root_path end end
Copied!
To fix that, we need to move them as if they were added to Avo's routes.
# config/routes.rb Rails.application.routes.draw do devise_for :users # Your routes authenticate :user, -> user { user.admin? } do mount Avo::Engine => Avo.configuration.root_path end end # Move Avo custom tools routes to Avo engine if defined? ::Avo Avo::Engine.routes.draw do # make sure you don't add the `avo/` prefix to the controller below get 'custom_page', to: "tools#custom_page" end end
Copied!
# app/controllers/avo/tools_controller.rb class Avo::ToolsController < Avo::ApplicationController def custom_page @page_title = "Your custom page" add_breadcrumb "Your custom page" end end
Copied!
# Retrieve and use the account
Throughout your app you can use Current.account
or if you add it to Avo's context
object and use it from there.
# config/initializers/avo.rb Avo.configure do |config| config.set_context do { foo: 'bar', user: current_user, params: request.params, account: Current.account } end end
Copied!
Check out this PR (opens new window) for how to update an app to support multitenancy.