SL Logo

Hooking in to Devise controller actions


Published 2023-10-01

Editor’s note: this article originally appeared on my blog.

Devise is a fantastic authentication gem in the Rails ecosystem that I’m learning more about every day!

I wanted to share how I was able to solve a problem effectively after reading some Devise source code, as it might come in handy for others.

The problem

I was working on a project using the Devise addition invitable and I needed a way to update a user’s profile after they accepted an invite so I could set the slug based on their name. When the user invite is created, we use a generic value as a filler for the name.

I had originally used an ActiveRecord callback, but this felt clunky. Not only was it difficult to understand what it was doing, the side effect of the callback happened on the Profile model! That would be hard to debug if it was your first time working on the app and something was going wrong with the Profile’s slug generation.

I began searching for ways to do what I wanted without callbacks and I decided to take a look at the Devise controllers source code.

The solve

It turns out, Devise anticipates that you might want to access a resource while it’s doing its thing, and so almost every Devise controller action has a line like this:

yield resource if block_given?

This is fantastic! Because we often inherit from the base Devise controllers, what this line does is let you access the resource under operation if you pass in a block:

# sample controller
class Users::RegistrationsController < Devise::RegistrationsController
  def update
    super { |resource| ... }

This was a game changer! I now had a great way to update the profile’s slug with the user’s provided name:

# app/controllers/users/registrations_controller.rb
def update
  super { |user| user.profile.update(slug: user.full_name.parameterize) }

Just like that, I was able to get rid of an ActiveRecord callback! So, if you ever need to access the resource to do something inside a Devise controller, this is your ticket.