2

I have a user controller that, if I want to, I can rescue the error if no user is found and redirect them to the appropriate page. But I would like to only use the rescue_from method only on certain routes instead of all the routes. So, pretty much something similar to

rescue_from ActiveRecord::RecordNotFound, with: :record_not_found, except: [:new, :edit]

Is there a way to do this? Help is appreciated!

class UserController < ApplicationController

  before_action :get_user

  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

  def show
  end

  def new
  end

  def create
  end

  def edit
  end

  def update
  end

  private
    def get_user
      User.find(params[:id])
    end

    def record_not_found
      redirect_to user_path, error: "Sorry, no user found."
    end 
end
sclem72
  • 466
  • 6
  • 16

4 Answers4

0

--UPDATE --

Possibly put it into

private

def problem
  begin
    @user = User.find(params[:id])
  rescue ActiveRecord::RecordNotFound
    redirect_to user_path, error: "Sorry, no user found."
  end
end

and have a before_action

before_action :problem, except: [:new, :edit]

This question might also help rescue from ActiveRecord::RecordNotFound in Rails

Community
  • 1
  • 1
Jon
  • 1,954
  • 1
  • 15
  • 13
0

Lets start by only calling the callback when its needed:

before_action :get_user, only: [:show, :edit, :update, :destroy]

However the queried record is not even being assigned to a variable so it can be used later. Lets fix that:

class UserController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # ...

  private 

    def set_user
       @user = User.find(params[:id])
    end
end

While we could use rescue_from ActiveRecord::RecordNotFound, with: :record_not_found - We don't even know that it was actually the query for a user that failed! It could be some other callback defined upstream in ApplicationController for example.

Also when a resource cannot be found your application should be telling the client that by sending a 404 - NOT FOUND or 410 - GONE response. Not by redirecting as that sends a 3xx response code which indicate that a resource has moved temporarily.

You could rescue the exception directly in the callback instead:

    def set_user
       begin 
         @user = User.find(params[:id])
       rescue ActiveRecord::RecordNotFound
         @users = User.all
         flash.now[:error] = "User not found"
         # note that we render - not redirect!
         render :index, status: 404
       end
    end

Although in most cases it is better to leave it to default handler rather than bungle up the REST interface of your app and add a bunch of complexity.

max
  • 96,212
  • 14
  • 104
  • 165
0

Solution A, use a common parent controller that define this before_filter, like:

class RescueNotRoundRecordController < ApplicationController
  rescue_from ActiveRecord::RecordNotFound, with: :record_not_found

  private
  def record_not_found
    redirect_to user_path, error: "Sorry, no user found."
  end 
end

class UserController < RescueNotRoundRecordController
  before_action :get_user
end

Solution B, use module to do that, which I think is the better way to go:

module RescueNotRoundRecord
  def self.included(mod)
    mod.class_eval do
      rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
    end
  end

  private
  def record_not_found
    redirect_to user_path, error: "Sorry, no user found."
  end 
end

class UserController < ApplicationController
  include RescueNotRoundRecord
end
lei liu
  • 2,735
  • 1
  • 16
  • 18
0

I'm seeing the most obvious two variants to solve problem of rendering the error with a filter by action name:

rescue_from Exception, with: ->(e) { %w(new edit).include?(action_name) && method(:record_not_found)[e] }

or

rescue_from Exception do |e|
   %w(new edit).include?(action_name) && method(:record_not_found)[e]
end
Малъ Скрылевъ
  • 16,187
  • 5
  • 56
  • 69