Last updated on February 12, 2022
Active Support CurrentAttributes adds global per-request attributes to your Rails app. To get started, create a Current class inside your models folder. You can assign any model in your application as an attribute to use as a global variable. We will discuss it later, but you should only use CurrentAttributes for your most important and widely used models within your application, for example, Project or Team.
Check out the commit and pull request created by DHH to learn more about how CurrentAttributes work under the hood. You don’t have to understand how the entire code works, but I find it useful to see an overview of how the “Rails magic” is created.
In the example below, I have assigned my User model to be a CurrentAttribute.
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
attribute :user
end
You can then set your CurrentAttributes simply by assigning them to an object
Current.user = current_user
Once you set this, you will be able to access your user object globally (for example, within views and models) without the need to declare them all the time.
Best way to learn about CurrentAttributes is to create a quick example illustrating a use case for it. For our example, we want to have projects names/slugs as our subdomain. For example project-name.lvh.me:3000
.
Create a Project with a name, subdomain and a user_id fields. Users have many projects and a project belongs to a user.
In order to complete this example, let’s sort out the subdomains. This is not a tutorial on subdomains, so the code is limited to just showcase CurrentAttributes.
# routes.rb
require "subdomain_required"
Rails.application.routes.draw do
#...
constraints(SubdomainRequired) do
resources :projects
# root
root "projects#show", as: :projects_root
end
#
root to: 'home#index'
end
# lib/subdomain_required.rb
class SubdomainRequired
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
To share a devise user session across subdomains:
# initializer/session_store.rb
# replace current_attributes_example with your app name
Rails.application.config.session_store :cookie_store, key: '_current_attributes_example_session'
Create the Current class as the example above and assignprojectas the attribute.
# app/models/current.rb
class Current < ActiveSupport::CurrentAttributes
attribute :project
end
Then we can create a before_action in the ApplicationController where we assign Current.project
to the project depending on the subdomain.
before_action :set_project
private
def set_project
Current.project = current_user.projects.find_by_subdomain(request.subdomain) if request.subdomain.present?
end
You can then use Current.project
within our application and this will be scoped based on our subdomain.
You can then list your user projects and link them to the subdomain
<% current_user.projects.each do |project| %>
<%= link_to project.name, projects_roots_url(subdomain: project.subdomain) %>
<% end %>
You will get redirected to the subdomain and our Current.project will have our relevant project.
<h1>Welcome to <%= Current.project.name %></h1>
Our project is now present globally within our app so we can continue writing our logic using Current.project instead of querying the project in every controller, model and view.
I came across this article which warns of the potential dangers of using CurrentAttributes and global state in general.
Important note it raises that you should be aware of - avoid using CurrentAttributes in background jobs. Instead, pass your CurrentAttributes object’s id as a parameter to your background job and then find the record within. This makes your background jobs explicit and avoids the danger of using the wrong record to perform the job.
# don't pass the object itself
def perform(user)
# user is Current.user
end
# pass the id of Current.user instead
def perform(user_id)
user = User.find user_id
end
DHH recommends using CurrentAttributes for a few, top-level globals, like account and user. Consider using CurrentAttributes if you are going to use this attribute in most of your actions. Otherwise, CurrentAttributes will become anti-productive and introduce problems within your application.
In conclusion, CurrentAttribute can be a great help to your application, but you should use it with caution and you should be aware of the dangers of using a global variable.
Articles, guides and interviews about web development and career progression.
Max 1-2x times per month.