Skip to Content
Course content

85: Action Controller Filters

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're running a high-end members-only club. You've got a DJ inside playing great music—that's your controller action. Now, you could have the DJ stop the music every single time someone walks through the door to check their membership card, ask for their name, and verify their age. But that would be an absolute disaster. The music would never flow, and the DJ would spend more time doing paperwork than playing records.

Instead, you hire a bouncer. The bouncer stands at the door and handles all the "pre-flight" checks. If the guest isn't on the list, the bouncer turns them away before they ever set foot in the club. If they are cleared, the bouncer steps aside and lets them in to enjoy the music. Once the guest leaves, maybe a staff member does a quick sweep of the room to make sure no glasses were broken. That's a post-visit check.

In Rails, Action Controller Filters are your bouncers. They allow you to extract repetitive logic—like authentication, finding a record in the database, or checking permissions—out of your individual actions and into a centralized place.

The Gatekeeper Pattern

The most common filter you'll use is before_action. I've seen way too many beginners manually call current_user or find_post at the start of every single method in their controller. It's tedious and makes your code noisy. Here is how we actually handle that in a professional codebase:

class PostsController < ApplicationController
  before_action :set_post, only: [:show, :edit, :update, :destroy]
  before_action :ensure_logged_in, only: [:new, :create, :edit, :update, :destroy]

  def show
    # @post is already set by the filter!
  end

  def edit
    # @post is already set and we know the user is logged in.
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end

  def ensure_logged_in
    unless current_user
      redirect_to login_path, alert: "You must be logged in to do that."
    end
  end
end

Notice the only: option. This is vital. You don't want the set_post filter running on the index action, because there is no params[:id] when you're looking at a list of all posts. If you tried that, Rails would throw an error and your app would crash. You can also use except: if it's easier to list the few actions that shouldn't be filtered.

Wrapping Logic with around_action

Sometimes a before_action and an after_action aren't enough because they don't share the same local state. That's where around_action comes in. Think of this as a "wrapper." It's like an escort who walks a guest into the club, stays with them the whole time, and then walks them out.

I typically use these for things like performance timing or database transactions. The key is the yield keyword. If you don't call yield, the actual controller action will never execute—the guest never gets into the club.

class ReportsController < ApplicationController
  around_action :track_performance

  def heavy_export
    # Some complex logic that takes 5 seconds
    render plain: "Export Complete"
  end

  private

  def track_performance
    start_time = Time.now
    yield # This is where the 'heavy_export' action actually runs
    end_time = Time.now
    Rails.logger.info "Report took #{end_time - start_time} seconds to generate."
  end
end

The Order of Operations

One thing that trips people up is the execution order. Filters are run in the order they are defined in the class. If you have three before_action calls, Rails hits them one by one. If any of those filters performs a redirect_to or render, the chain is halted immediately. The subsequent filters and the actual action are skipped entirely. This is exactly why our ensure_logged_in filter works—it stops the "unauthorized" guest right at the door.




📋 Practical Task

Building a Premium Subscription Guard

You are building a blog where some articles are free and some are "Premium." You need to ensure that users cannot access premium articles unless their account has a premium boolean set to true.

Your Task:

  • In the ArticlesController, create a before_action called verify_premium_access.
  • This filter should only run on the show action.
  • Inside the filter, find the article by ID. If the article is marked as premium: true and the current_user is either not logged in or not a premium member, redirect them to the pricing_path with a flash message saying "This content requires a premium subscription."
  • Ensure the show action remains clean, relying on the filter to have already handled the authorization and set the @article instance variable.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.