I’m new to ruby on rails and I’m a bit stuck with what the best next step is in a multi-tenancy application I’m building.
Basically I want to scope resources by account_id, so I have created a method and helper called current_account in my accounts base_controller.
However, the tutorial I’m following scopes current_account by subdomain which I do not want to do. So I need a way to identify the current user’s account_id so that that I can have a resource variable @contact = current_account.contacts."all".
Do I need to make a new association between the user and account model so that I can use the current_user helper to define the current account id or is there a better way? If so, what is the best way to do this?
Background The first user who signs up becomes the account owner. Account owners can then invite other users to the account. I'm using the devise gem. Resources are scoped by account so that only users linked to an account can see the records belonging to that account.
Base Controller
module Accounts
class BaseController < ApplicationController
def current_account
@current_account ||= ?????
end
helper_method :current_account
def owner?
current_account.owner == current_user
end
helper_method :owner?
end
end
Contacts (my resource) Controller
module Accounts
class ContactsController < Accounts::BaseController
def index
@contact = current_account.contacts.all
end
end
end
Account Model
class Account < ActiveRecord::Base
belongs_to :owner, class_name: "User"
accepts_nested_attributes_for :owner
validates :subdomain, presence: true, uniqueness: true
has_many :contacts
has_many :invitations
has_many :memberships
has_many :users, through: :memberships
end
Invitation Model
class Invitation < ActiveRecord::Base
belongs_to :account
validates :email, presence: true
end
Membership Model
class Membership < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
User Model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Routes
Rails.application.routes.draw do
devise_for :users
scope module: "accounts" do
resources 'dashboard'
resources 'contacts'
resources :invitations, only: [:new, :create] do
member do
get :accept
patch :accepted
end
end
resources :users, only: [:index, :destroy]
end
Schema
ActiveRecord::Schema.define(version: 20170124002015) do
create_table "accounts", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "owner_id"
t.string "subdomain"
end
add_index "accounts", ["subdomain"], name: "index_accounts_on_subdomain"
create_table "contacts", force: true do |t|
t.string "first_name"
t.string "last_name"
t.string "phone"
t.string "email"
t.text "comments"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "account_id"
end
add_index "contacts", ["account_id"], name: "index_contacts_on_account_id"
create_table "invitations", force: true do |t|
t.string "email"
t.integer "account_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
end
add_index "invitations", ["account_id"], name: "index_invitations_on_account_id"
add_index "invitations", ["token"], name: "index_invitations_on_token"
create_table "memberships", force: true do |t|
t.integer "account_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "memberships", ["account_id"], name: "index_memberships_on_account_id"
add_index "memberships", ["user_id"], name: "index_memberships_on_user_id"
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
There are two possible associations between users and accounts:
In the first case, the tenant cannot be set from the current_user, because it's unclear which account should be used as the current tenant. The membership table in the schema.rb indicates this is the approach taken by the tutorial you mentioned. Loading the account by subdomain helps specify which account shall be used as the current tenant.
In the second case, every user has just one account. Users get an account_id, the membership table becomes obsolete, and you can load the current tenant like so:
def current_account
@current_account ||= current_user.account
end
Do I need to make a new association between the user and account model so that I can use the current_user helper to define the current account id or is there a better way? If so, what is the best way to do this?
It seems to me that you want to take the second approach, which requires that an account has_many
users and a user belongs_to
an account.