Skip to content
PLAY VIDEO PLAY VIDEO PLAY VIDEO
By Jonathan Miles

Reducing Developer Friction With Rails 6’s Actionable Errors

store-not-found-error

One of Rails 6’s new features is ActionableErrors, which allows you to add buttons to Rails’ default error page to perform certain actions. Out-of-the-box Rails 6 includes one for the common “pending migration” error, where you can hit a button on the page and run the migrations without having to jump back to a terminal.

Creating Custom ActionableErrors

To create your own “actionable error” is quite simple. You create an error class that includes ActiveSupport::ActionableError, then you can use the action method to create the button and its behaviour. Here’s a simple example from Rails itself:

class PendingMigrationError < MigrationError 
  include ActiveSupport::ActionableError 

  action Run pending migrations do 
    ActiveRecord::Tasks::DatabaseTasks.migrate 
  end
end

The string here (“Run pending migrations”) is what will be shown on the button. The block is what will be executed when the button is clicked. This is normal Rails code, so you have access to all your models and other objects.

When To Use Actionable Errors

ActionableErrors are great, but they require a custom error object. This means you probably only want to use them for errors that your code is raising. Further, you have to weigh the cost/benefit of maintaining an additional piece of code against the productivity gain of having a button there to fix an issue.

We recently added an ActionableError to an internal gem. In our case, we support a form of multi-tenancy by selecting a Store based on the requests’ domain (a very common way to handle multi-tenancy scoping). If we can’t find the store, we raise an exception.

This works, but causes a minor headache every time a developer pulls down a new database dump from staging to run locally. We also don’t enforce how a developer sets up their machine so they could be using localhost, or lvh.me, or pow, or manually editing their hosts file, etc, etc.

This made an ideal candidate for an ActionableError — it is an error we are raising within our code, it has a simple and repeatable fix, and it is something that our developers encounter often.

Dynamic Actionable Errors

The ActionableError code in Rails is quite simple and I’d encourage anyone interested to read through it. One thing it does not have obvious support for is dynamic actions (at least at the time of writing).

In our Store case above we really want to generate the buttons dynamically — we don’t know how many Store objects are in the database, nor do we know which domain the request is coming from until runtime.

To get around this, we’re shadowing the _actions class-level variable with a method that returns our dynamic list of actions:

    def initialize(msg, domain)
      # actions are class-level methods so we need to keep domain accessable
      @@domain = domain || "nil"
      super(msg)
    end

    # ActionableError expects actions to have been defined at the class-level, but
    # we want to declare them dynamically, so we shadow the _actions variable and
    # create them on-the-fly here
    def self._actions
     Store.all.map do |store|
        ["Change Store #{store.name} (#{store.url}) url to: #{@@domain}", -> { store.update!(url: @@domain) }]
      end.to_h
    end

You can see here we just iterate through all the Store objects and create a hash mapping <text to show on button> to a lambda that will be called when that button is clicked.

Conclusion

ActionableErrors are a great quality-of-life improvement for Rails developers. Going forward I’d love to see Rails provide an official way to define custom actions, but our work-around here solves that problem for now and seems unlikely to break in future Rails versions.

I wouldn’t try to force this onto a lot of errors, but for those times when you’ve said to yourself “Argh I always forget to <simple action> after I’ve <common procedure>”, ActionableErrors could save you that extra bit of time and sanity.

Latest Articles by Our Team

Our expert team of designers and developers love what the do and enjoy sharing their knowledge with the world.

We Hire Only the Best

reinteractive is Australia’s largest dedicated Ruby on Rails development company. We don’t cut corners and we know what we are doing.

We are an organisation made up of amazing individuals and we take pride in our team. We are 100% remote work enabling us to choose the best talent no matter which part of the country they live in. reinteractive is dedicated to making it a great place for any developer to work.

Free Community Workshops

We created the Ruby on Rails InstallFest and Ruby on Rails Development Hub to help introduce new people to software development and to help existing developers hone their skills. These workshops provide invaluable mentorship to train developers, addressing key skills shortages in the industry. Software development is a great career choice for all ages and these events help you get started and skilled up.

  • Webinars

    Webinars

    Webinars are our online portal for tips, tricks and lessons learned in everything we do. Make the most of this free resource to help you become a better developer.

    Learn more about webinars

  • Installfest

    Installfest

    The Ruby on Rails Installfest includes a full setup of your development environment and step-by-step instructions on how to build your first app hosted on Heroku. Over 1,800 attendees to date and counting.

    Learn more about Installfest

  • Development Hub

    Development Hub

    The Ruby on Rails Development Hub is a monthly event where you will get the chance to spend time with our team and others in the community to improve and hone your Ruby on Rails skills.

    Learn more about Development Hub

Get the “reinteractive Review” Monthly Email