Class: ActiveSupport::CurrentAttributes
- Includes:
- Callbacks
- Defined in:
- activesupport/lib/active_support/current_attributes.rb
Overview
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: .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..create()
end
end
class Message < ApplicationRecord
belongs_to :creator, default: -> { Current.user }
after_create { || Event.create(record: ) }
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.
Direct Known Subclasses
Defined Under Namespace
Modules: TestHelper
Constant Summary collapse
- INVALID_ATTRIBUTE_NAMES =
:nodoc:
[:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all]
Constants included from Callbacks
ActiveSupport::Callbacks::CALLBACK_FILTER_TYPES
Instance Attribute Summary collapse
-
#attributes ⇒ Object
Returns the value of attribute attributes.
Class Method Summary collapse
-
.attribute(*names) ⇒ Object
Declares one or more attributes that will be given both class and instance accessor methods.
-
.before_reset(*methods, &block) ⇒ Object
Calls this callback before #reset is called on the instance.
-
.clear_all ⇒ Object
:nodoc:.
-
.instance ⇒ Object
Returns singleton instance for this class in this thread.
-
.reset_all ⇒ Object
:nodoc:.
-
.resets(*methods, &block) ⇒ Object
(also: after_reset)
Calls this callback after #reset is called on the instance.
Instance Method Summary collapse
-
#initialize ⇒ CurrentAttributes
constructor
A new instance of CurrentAttributes.
-
#reset ⇒ Object
Reset all attributes.
-
#set(set_attributes) ⇒ Object
Expose one or more attributes within a block.
Methods included from Callbacks
Methods included from Concern
#append_features, #class_methods, extended, #included, #prepend_features, #prepended
Constructor Details
#initialize ⇒ CurrentAttributes
Returns a new instance of CurrentAttributes.
195 196 197 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 195 def initialize @attributes = {} end |
Instance Attribute Details
#attributes ⇒ Object
Returns the value of attribute attributes
193 194 195 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 193 def attributes @attributes end |
Class Method Details
.attribute(*names) ⇒ Object
Declares one or more attributes that will be given both class and instance accessor methods.
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 102 def attribute(*names) 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 ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner| names.each do |name| owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch| batch << "def #{name}" << "instance.#{name}" << "end" end owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch| batch << "def #{name}=(value)" << "instance.#{name} = value" << "end" end end end end |
.before_reset(*methods, &block) ⇒ Object
Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
144 145 146 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 144 def before_reset(*methods, &block) set_callback :reset, :before, *methods, &block end |
.clear_all ⇒ Object
:nodoc:
160 161 162 163 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 160 def clear_all # :nodoc: reset_all current_instances.clear end |
.instance ⇒ Object
Returns singleton instance for this class in this thread. If none exists, one is created.
97 98 99 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 97 def instance current_instances[current_instances_key] ||= new end |
.reset_all ⇒ Object
:nodoc:
156 157 158 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 156 def reset_all # :nodoc: current_instances.each_value(&:reset) end |
.resets(*methods, &block) ⇒ Object Also known as: after_reset
Calls this callback after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
149 150 151 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 149 def resets(*methods, &block) set_callback :reset, :after, *methods, &block end |
Instance Method Details
#reset ⇒ Object
Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
218 219 220 221 222 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 218 def reset run_callbacks :reset do self.attributes = {} end end |
#set(set_attributes) ⇒ Object
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
209 210 211 212 213 214 215 |
# File 'activesupport/lib/active_support/current_attributes.rb', line 209 def set(set_attributes) old_attributes = compute_attributes(set_attributes.keys) assign_attributes(set_attributes) yield ensure assign_attributes(old_attributes) end |