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) …
You should not use
begin/rescuestatements here, it costs in performance and your intentions aren’t clear. Same withtrywhich is simply arescue nilin 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):