In Praise of the Current Pattern

Matthew • December 15, 2022

The "current pattern" or "current context" is a technique that allows you to access some per-request data easily without having to explicitly pass it around everywhere. Recent versions of Rails offer explicit support for it. The current pattern, used judiciously, will help clean up your code and make your life swell!

What is the current pattern?

The current pattern is a singleton class instance you can access anywhere within a web request cycle. You typically initialize this instance at the start of the request and, from there on, read these values.

When you use it in Ruby on Rails, instead of having to pass, for example, a user variable for the current user as a parameter to every method in the entire call stack, instead, you can do this:

if Current.user.manager?
  # Do manager-only stuff...
end

How do I implement the current pattern?

As of Rails 5.2.3, you can use ActiveSupport::CurrentAttributes to implement the pattern.

Before that, you will have to do more work yourself. I'm (unhelpfully!) not going to cover how to do that here except to say that you're going to want to make it threadsafe. (If uncomfortable doing that, spend the time upgrading to a newer Rails version instead!) (Update: Several folks have mentioned that the RequestStore gem provides some of the features and is available in earlier versions of Rails.)

ActiveSupport::CurrentAttributes provides a threadsafe store which is reset at the start of every request cycle. It also offers other helpful features, so be sure to read the documentation.

How do I use the current pattern?

Here's an example from one project where I've employed this pattern.

You'll notice that this looks (very!) similar to the example in the Rails documentation. And that's not a surprise given that this pattern is, well, a pattern!

Here, I'm grabbing the user, if it's present, out of Devise after performing authentication.

class Current < ActiveSupport::CurrentAttributes
  attribute :account 
  attribute :user

  resets do
    Time.zone = nil
  end 

  def user=(user)
    super
    self.account = user.account
    Time.zone = user.time_zone
  end
end


class ApplicationController < ActionController::Base
  before_action :authenticate_user!
  before_action :set_current_user

  def set_current_user
    # current_user comes from Devise
    Current.user = current_user || GuestUser.new
  end
end

Now, Current.user and Current.account are available to me anywhere in my request processing.

Sir, you've described a global variable

Some people might be tempted to call this a global variable, and those people would be absolutely right!

In general, we're all down on global state, but this technique provides more value than pain.

First, yes, it is global state, but just little bit of global state! I suggest you rethink your usage if you have, say, more than ten or so attributes in the current context. If an attributes is not something you're using in just about every request, it probably doesn't belong here.

Second, it's my experience that the values in the current context are usually the same cast of characters: the current user, the current user's time zone, the current account, the current team, and so on. And that's about it for me, usually. So, you have a pretty good idea about when you're likely working with data that is global to the thread.

Third, this state is short-lived, primarily read-only, and consumed only within the context of a single request. So, this further constrains the potential negative consequences.

The combination of these factors weighed against its utility makes it pretty palatable.

It can make testing more painful!

One of the most significant downsides I've experienced using this pattern is that you'll sometime need to set up your instance of Current in some of your tests. I find that off-putting and annoying.

At those times, you'll definitely be asking yourself, "why am I using this stupid global variable?"

The technique I've used to mitigate this is to, where possible, use the Current value to initialize a default method parameter instead of accessing it directly within the method's body. (So, dependency injection.)

So, instead of this:

class Reservation
  def editable?
    Current.user.editor? && recently_created?
  end
end

Do this:

class Reservation
  def editable?(user: Current.user)
    user.editor? && recently_created?
  end
end

I've been satisfied with this approach.

In conclusion

Thanks for reading! I hope you find this useful and that it helps you write some more concise code. Did you find this useful? Let me know! You can find me on Post.news at @mjbellantoni on post.news, the Ruby Job Board page on LinkedIn, or Reddit.

Addendum: A curiously obscure pattern

This technique seems not to be widely known. In fact, in a recent episode of Jason Swett's "Code With Jason" podcast with Jorge Manrubia of 37Signals, Jason didn't seem to know about it!

I do not remember where I learned about it, but there are examples running around in the Rails API documentation in the documentation for ActiveStorage::Variant and ActiveRecord::DelegatedType.

Get the latest sent to your inbox once a week!

Receive a weekly update of technical tips, Rails job market analysis, Rails job listings and more!

We'll never share your email address. See our Privacy & Data Policies for more details.


Matthew Bellantoni is a seasoned technology manager experienced growing teams from 0 people to under 100 people, from $0 to $100M in revenue, at companies in SaaS, eCommerce, marketplace, and enterprise software. His time has typically been at fast-growing VC-backed companies, mainly using Ruby on Rails, where he's been senior management usually reporting to the CEO. (He's also written a Ruby on Rails gem with over a million downloads.) You can contact him at matthew@rubyjobboard.com or at @mjbellantoni.