My question
How do I create a default_scope for Users such that it limits visibility to only those users who have a tenants_users record for the current tenant (tenants_users.tenant_id == Tenant.current_id) in the join table for the has_many :through association?? I think this is ultimately a syntax question, but it may be deeper than that.
What I'm doing
I'm implementing multitenancy using Multitenancy with Scopes (subscription required) as a guide. Ryan uses a default_scope to limit available rows based on the client's current tenant. He basically adds a tenant_id column to all tables that contain tenant-specific data, then includes a default_scope in each model:
default_scope { where(tenant_id: Tenant.current_id) }
[Tenant.current_id (a cattr_accessor) is set when a user logs in (Tenant.current_id = the tenant_id for the Tenant the user selects).]
This works great for me, except I want a many-to-many relationship for Users and Tenants (as opposed to many-user-to-one-tenant as the Railscast assumes): A Tenant can have multiple users, AND I want Users to have access to Multiple tenants. So, instead of a tenant_id column on my Users table, I have a tenants_users join table that has a tenant_id and user_id for each row.
A different description of the result I'm looking for
If I fire the up the Rails Console and set:
Tenant.current_id = 2
...then say...
User.all
...I want to see ONLY those users who have a row in the tenants_users table such that tenant_id == Tenant.current_id.
If I say:
User.unscoped.all
Then I want to see all the users, regardless of what Tenant.current_id is.
Code
tenant.rb
class Tenant < ActiveRecord::Base
cattr_accessor :current_id
has_many :users, :through => :tenants_users
has_many :tenants_users
def self.current_id=(id)
Thread.current[:tenant_id] = id
end
def self.current_id
Thread.current[:tenant_id]
end
end
user.rb
class User < ActiveRecord::Base
# This was the default_scope before I moved tenant_id to the tenants_users table
# default_scope { where(tenant_id: Tenant.current_id) }
# What should it be now?
default_scope ???
has_many :tenants_users
has_many :tenants, :through => :tenants_users
end
tenants_user.rb
class TenantsUser < ActiveRecord::Base
belongs_to :tenant
belongs_to :user
end
This basically joins the
tenants_users
table and puts the condition on thetenant_id
attribute: