Friday, May 30, 2014

"Try not. Do... or do not. There is no try."

At first glance, the try method in Rails is great. It's a quick way to do nil checks in a chain to get at associations and attributes. I've avoided it when I can, partly out of habit and partly because its syntax can be slightly less readable. However, there is another reason to not use try: it can also hide seriously time-wasting bugs caused by misnamed methods, e.g.:


$ rails c
...
2.1.1 :001 > if ''.try(:incorrectly_named_method)
2.1.1 :002?>   # assume method returned nil
2.1.1 :003 > end
 => nil 
2.1.1 :004 > unless ''.incorrectly_named_method
2.1.1 :005?>   # assume method returned nil
2.1.1 :006 > end
NoMethodError: undefined method `incorrectly_named_method' for "":String
 from (irb):4
 ...
 from bin/rails:4:in `require'
 from bin/rails:4:in `
'

That undefined method error on execution can really help. If you avoid it, then if the method name changes, or if you had typed the method name incorrectly, you may not find out about it quickly.

Why would you use try? A few good reasons for using try might be: (1) you are absolutely sure the method name is correct and will not change, and/or (2) you might have used defined? to check that the method existed before attempting to call the method.

Remember that Yoda said, "Try not. Do... or do not. There is no try."

Update: Ruby introduced the ampersand and it beats the try method in two ways: (1) raises NoMethodError if method doesn't exist, (2) handles method missing as probably expected.

[1] pry(main)> class A; def b; puts "ya"; end; end
=> :b
[2] pry(main)> A.new&.b
ya
=> nil
[3] pry(main)> A.new.try(:b)
ya
=> nil
[4] pry(main)> A.new&.c
NoMethodError: undefined method `c' for #
from (pry):4:in `__pry__'
[5] pry(main)> A.new.try(:c)
=> nil
[6] pry(main)> class Dummy; def method_missing(m, *args, &block); puts "yeh"; end; end
=> :method_missing
[7] pry(main)> Dummy.new.try(:a)
=> nil
[8] pry(main)> Dummy.new&.a
yeh
=> nil

No comments: