CurrentAttributes
Rails’ ActiveSupport provides an abstraction for setting thread isolated global state that is set on a per web request basis. After each request, the state is automatically reset.
See also https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
The canonical example is setting Current.user
for an authenticated web request.
class Current < ActiveSupport::CurrentAttributes attribute :user end
def set_current_user Current.user = User.find_by(id: cookies.encrypted[:user_id]) end
See also https://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
# dry-effects Alternative
⚠️ I would only use something like CurrentAttributes sparingly if at all. If something like global state makes sense for a certain use cases, consider a safer alternative https://github.com/dry-rb/dry-effects (they say it isn’t global state, but it is just with more explicit code and errors that get raised to enforce using it ’safely’).
Reader
for example, allows passing a value down the stack. It’s strictly for
reading, so if writing state down the stack is needed, use the State
effect.
class ApplicationController < ActionController::Base include Dry::Effects::Handler.Reader(:current_user) around_action :provide_current_user def provide_current_user(&block) user = User.find_by(id: cookies.encrypted[:user_id]) with_current_user(user, &block) end end
class ThingThatDoesStuff include Dry::Effects.Reader(:current_user, default: nil) def call current_user.things end end
Why is the better than CurrentAttributes?
- Must explicitly declare (via module inclusion) which classes can read or set the global state
- Accessing global state before it is set raises an error