Reputation: 1437
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) …
Upvotes: 2
Views: 714
Reputation: 115541
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
Upvotes: 4