Current Attributes
Abstract super class that provides a thread-isolated attributes singleton, which resets automatically before and after each request. This allows you to keep all the per-request attributes easily available to the whole system.
The following full app-like example demonstrates how to use a Current class to facilitate easy access to the global, per-request attributes without passing them deeply around everywhere:
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
  attribute :account, :user
  attribute :request_id, :user_agent, :ip_address
  resets { Time.zone = nil }
  def user=(user)
    super
    self.account = user.account
    Time.zone    = user.time_zone
  end
end
# app/controllers/concerns/authentication.rb
module Authentication
  extend ActiveSupport::Concern
  included do
    before_action :authenticate
  end
  private
    def authenticate
      if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
        Current.user = authenticated_user
      else
        redirect_to new_session_url
      end
    end
end
# app/controllers/concerns/set_current_request_details.rb
module SetCurrentRequestDetails
  extend ActiveSupport::Concern
  included do
    before_action do
      Current.request_id = request.uuid
      Current.user_agent = request.user_agent
      Current.ip_address = request.ip
    end
  end
end
class ApplicationController < ActionController::Base
  include Authentication
  include SetCurrentRequestDetails
end
class MessagesController < ApplicationController
  def create
    Current.account.messages.create(message_params)
  end
end
class Message < ApplicationRecord
  belongs_to :creator, default: -> { Current.user }
  after_create { |message| Event.create(record: message) }
end
class Event < ApplicationRecord
  before_create do
    self.request_id = Current.request_id
    self.user_agent = Current.user_agent
    self.ip_address = Current.ip_address
  end
end
A word of caution: It’s easy to overdo a global singleton like Current and tangle your model as a result. Current should only be used for a few, top-level globals, like account, user, and request details. The attributes stuck in Current should be used by more or less all actions on all requests. If you start sticking controller-specific attributes in there, you’re going to create a mess.
- A
 - B
 - I
 - N
 - R
 - S
 
Attributes
| [RW] | attributes | 
Class Public methods
attribute(*names, default: NOT_SET) Link
Declares one or more attributes that will be given both class and instance accessor methods.
Options
- 
:default- The default value for the attributes. If the value 
is a proc or lambda, it will be called whenever an instance is constructed. Otherwise, the value will be duplicated with #dup. Default values are re-assigned when the attributes are reset.
# File activesupport/lib/active_support/current_attributes.rb, line 114 def attribute(*names, default: NOT_SET) invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES if invalid_attribute_names.any? raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}" end ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner| names.each do |name| owner.define_cached_method(name, namespace: :current_attributes) do |batch| batch << "def #{name}" << "attributes[:#{name}]" << "end" end owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch| batch << "def #{name}=(value)" << "attributes[:#{name}] = value" << "end" end end end Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "") Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value") self.defaults = defaults.merge(names.index_with { default }) end
before_reset(*methods, &block) Link
Calls this callback before reset is called on the instance. Used for resetting external collaborators that depend on current values.
instance() Link
Returns singleton instance for this class in this thread. If none exists, one is created.
new() Link
resets(*methods, &block) Link
Calls this callback after reset is called on the instance. Used for resetting external collaborators, like Time.zone.
Instance Public methods
reset() Link
Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
set(attributes, &block) Link
Expose one or more attributes within a block. Old values are returned after the block concludes. Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
class Chat::PublicationJob < ApplicationJob
  def perform(attributes, room_number, creator)
    Current.set(person: creator) do
      Chat::Publisher.publish(attributes: attributes, room_number: room_number)
    end
  end
end