Question

I want to check values in a user-input hash (parameters passed to a Rails controller). I want to protect myself against bogus user input. Object#try protects me against missing input, but what about malformed input?

The simple, unsafe case:

  if params[:credentials][:login] …

Obviously, if the user hasn't provided the :credentials value, then my attempt to get :login will fail … Nil doesn't have the method :[]. A very nice solution to this is Object#try:

  if params[:credentials].try(:[], :login) … 

But what if the issue is that the user provided malformed credentials? In particular, one of my users passed an array, rather than a hash. So I still get an exception,

TypeError: can't convert Symbol into Integer

Is there something rather like try(), that turns any bogosity into false/nil?

The closest I could come is

  if begin params[:credentials][:login]; rescue; false; end … 

Which is a bit cluttered -- though, I grant, still more compact and yet more general than explicit paranoia:

  if (params.has_key? :credentials and params[:credentials].is_a? Hash and params[:credentials].has_key? :login) … 
Was it helpful?

Solution

You should not use begin/rescue statements here, it costs in performance and your intentions aren't clear. Same with try which is simply a rescue nil in disguise.

You have to check everything and you check it right. It's just kind of ugly to mix data check and logic.

Since I watched Avdi's Confident Ruby, I changed my way of coding; I suggest you do something like (I just simplified your code a little bit):

def your_action
  checked_params_for_action do
    #safe params here
    #your_controller_code
  end
end

private

def checked_params_for_action(&block)
  if (params[:credentials].is_a?(Hash) && params[:credentials][:login])
    yield
  else
    redirect_to root_path, error: "params malformed"
  end
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top