DEV Community

Augusts Bautra
Augusts Bautra

Posted on • Edited on

3

Stop abusing before_action

In this short rant I will show you how to change most before_action calls into plain Ruby.

Does this make your eyes bleed?
complex controller with many before_action calls

source

# bad, hook approach
class InstructionsController < ApplicationController
  before_action :load_instruction, only: %i[show edit]  

  def show
    authorize @instruction
  end

  def edit
    authorize @instruction
  end

  private

  def load_instruction
    @instruction = Instruction.find(params[:id])    
  end
Enter fullscreen mode Exit fullscreen mode

This is a weird antipattern. Hooks are like guard clauses, intended to step in before getting to action itself (authentification, redirects).

Consider Grug's advice - keep code pertaining to a thing in one place. Let's get rid of before_action here and have everyting an action needs in it.

# good, simple memoisation, no verbs in getters
class InstructionsController < ApplicationController 
  def show
    authorize instruction
  end

  def edit
    authorize instruction
  end

  private

  def instruction
    @instruction ||= Instruction.find(params[:id])
  end
end
Enter fullscreen mode Exit fullscreen mode

Discussion

Pros:

  1. Side-stepped the need to parse all hooks when trying to understand what hooks a new action should get.
  2. Eliminated any order-dependence on @instruction being available. Just use the memoising private getter, same as in other parts of Rails code.
  3. Reduced the risk of double-auth (in hook and in action itself), it happens.
  4. Less Rails™, more Ruby ❤️.

Con:

  1. Admittedly we've not done much about unclear source of @instruction. It's still loaded as a side-effect, but there's a cure for that also - stop using ivars in controllers and pass all needed variables to template explicitly.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more

Top comments (3)

Collapse
 
ben profile image
Ben Halpern

I am a serial abuser of before action

Collapse
 
epigene profile image
Augusts Bautra

Ben, no, please, no. :)

Collapse
 
david_rawk_3370f41db75fc8 profile image
david rawk

My only thought here is that, this is not what the rails team suggest.

The core rails team suggest using the before actions. I know this because when you run generate scaffold blarg your controller will have set_blarg as a before action. It comes in the box.

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More