Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should we avoid using rescue in its modifier form?

I will define value. But this value may be in value of key of hash. I will use rescue for define value is nil if this keys is not exist. for example

foo = bar[:a][:b][:c] rescue nil

But in practice tell me bad style because me using rescue in its modifier form. I will change logic to use check three condition.

foo = bar[:a][:b][:c] if bar.key?(:a) && bar[:a].key?(:b) && bar[:a][:b].key?(:c)

I really would like to know why should we avoid using rescue in its modifier form?

like image 839
Edin Dzejo' Avatar asked Jan 07 '17 21:01

Edin Dzejo'


2 Answers

Why should we avoid using rescue in its modifier form in rails?

Firstly, because it hides all errors, including the ones you expect and the ones you don't, and a blanket rescue doesn't make it clear to future readers of your code which errors were expected or unexpected. This might not be a problem now, with a simple foo[:a][:b][:c], but at any given point in time somebody might modify that statement to read foo[:a][:b][some_method] and suddenly any errors that should bubble out of some_method are also swallowed.

Secondly, there is usually a better less all-encompassing solution that is more explicitly designed to handle only the error you intend to ignore: A missing index or a nil return value.

In your case, the alternative is not the massive if && && && you're suggesting. For a hash, you can use either dig, which has all the benefits of rescue without swallowing every type of exception that could be raised:

foo = bar.dig(:a, :b, :c)

Similarly, for chained method invocations, you can use try (in Rails) or the safe navigation operator (in Ruby 2.3):

foo = bar.try(:a).try(:b).try(:c)
# or
foo = bar&.a&.b&.c
like image 174
meagar Avatar answered Sep 21 '22 18:09

meagar


The longer form is more safe, I wouldn't use the short version in production unless there is no other way. Whether you use checks or an exeption to avoid or catch errors depends on the situation. Exeptions are costly in processor time, so do your benchmarks for time consuming methods, on the other hand doing a lot of checks and still not being sure is perhaps worse. If the readability of my code would be lost by doing a lot of checks and speed isn't a factor I use begin .. rescue or def .. rescue but in that case you are better to rescue a known exception like this

begin  
  # -  
  raise "another exception"
rescue SyntaxError 
  # -  
rescue => exception
  @errors += 1
  log exception
  log exception.backtrace  
end   

Which gives

another exception
C:/.../test.rb:3:in `<main>'

Always catch the kind of exception and put or better log it, I also use it to raise a variable @errors which is logged for all of my scripts and monitored by a separate tool.

like image 25
peter Avatar answered Sep 23 '22 18:09

peter