开发者

How can I deal with invalid IDs in before_filter?

Let's say my controller looks like this:

class MyController < ApplicationController
  before_filter :find_user,:only => [:index,:create,:update,:destroy]
  def index
     @some_objects = @user.objects.all
  end

  ...

  private
  def find_user
    @user = User.find(params[:user_id])
  end

end

If the user_id param does not exist, @user will be nil.I think this is not the way:

def index
   if @user
      @some_objects = @user.objects.all
   else
      # ?
   end
end

The code would look uglier having all these checks in my controller ... not to mention I'd have to duplica开发者_运维问答te a lot of the logic if other controllers are similar to this one. How do you deal with these cases?


If the user_id param does not exist, then find method throw ActiveRecord::RecordNotFound exception. This exception is caught in a before_filter and rendered error. Аll subsequent filters and the index action will not be called.

class MyController < ApplicationController
  before_filter :find_user,:only => [:index,:create,:update,:destroy]
  def index
     @some_objects = @user.objects.all
  end

  private
  def find_user
    @user = User.find(params[:user_id])
  rescue ActiveRecord::RecordNotFound
    # render or redirect_to error
  end

end


The best way to make it perfectly dry is to use a dedicated gem: inherited_resources

It will basically handle everything for you, preloading as expected your resources according to the context.

Of course you can add your desired specific scopes, see tutorial.


I think it should be placed in the same filter:

private
def find_user
  @user = User.find_by_id(params[:user_id])
  redirect_to where_you_want_to_go_when_no_user_url unless @user #for example login page
end

If you want to render your controller actions even without a @user and you always need the @some_objects (and you don't want the variable to be null), you can have another before_filter:

def get_some_objects
  @some_objects = @user.present? ? @user.objects.all : []
end

or combining both options (redirect or set some_objects variable):

def set_variables
  @user = User.find_by_id(params[:user_id])
  if @user
    @some_objects = @user.objects.all
  else
    redirect_to where_you_want_to_go_when_no_user_url
  end
end

I hope this helps.

EDIT: change 'find' to 'find_by_id' to avoid errors, when id is null or user for given id does not exist.


When you ask for a specific id and AR can't find it, it throws a RecordNotFound error. You would have to catch that with something like:

irb(main):025:0> begin
irb(main):026:1* Location.find 100
irb(main):027:1> rescue ActiveRecord::RecordNotFound => e
irb(main):028:1> puts "Oops: #{e.message}"
irb(main):029:1> end
Oops: Couldn't find Location with ID=100
=> nil

If you are wanting to apply something to all controllers you should probably think about adding your method to ApplicationController...

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜