September 5, 2016
This blog is part of our Rails 5 series.
Rails already provides methods for creating class level and module level variables in the form of cattr** and mattr** suite of methods.
In Rails 5, we can go a step further and create thread specific class or module level variables.
Here is an example which demonstrates an example on how to use it.
module CurrentScope
thread_mattr_accessor :user_permissions
end
class ApplicationController < ActionController::Base
before_action :set_permissions
def set_permissions
user = User.find(params[:user_id])
CurrentScope.user_permissions = user.permissions
end
end
Now CurrentScope.user_permissions
will be available till the lifetime of
currently executing thread and all the code after this point can use this
variable.
For example, we can access this variable in any of the models without explicitly
passing current_user
from the controller.
class BookingsController < ApplicationController
def create
Booking.create(booking_params)
end
end
class Booking < ApplicationRecord
validate :check_permissions
private
def check_permissions
unless CurrentScope.user_permissions.include?(:create_booking)
self.errors.add(:base, "Not permitted to allow creation of booking")
end
end
end
It internally uses
Thread.current#[]=
method, so all the variables are scoped to the thread currently executing. It
will also take care of namespacing these variables per class or module so that
CurrentScope.user_permissions
and RequestScope.user_permissions
will not
conflict with each other.
If you have used
PerThreadRegistry
before for managing global variables, thread_mattr_*
& thread_cattr_*
methods can be used in place of it starting from Rails 5.
Globals are generally bad and should be avoided but this change provides nicer API if you want to fiddle with them anyway!
If this blog was helpful, check out our full blog archive.