Deliberately calling code that may raise an error is ugly. Just look at it:
person.name.gsub(/foo/, 'bar') rescue nil
As person.name
returns nil
if no name has been set, person.name.gsub
would raise a NoMethodError
in the event of a nameless person. nil
has no gsub
method. Because we know this, we rescue
. So, we deliberately call code that may raise an error. Which is ugly.
Instead, you should determine wether or not the code you call can raise an error, and then just not call it.
person.name.gsub(/foo/, 'bar') if person.name
This is also sort of odd, though. If getting a persons name takes 2 seconds, the additional call to person.name
sucks. Etc.
Introducing
class Object
def try(*args, &block)
return if self.nil?
block_given? ? yield(self) : self.__send__(args.shift, *args)
end
end
Yes! Now you can do this instead:
person.name.try(:gsub, /foo/, 'bar')
An actual working sample:
class Person
attr_reader :name
def initialize(name = nil)
@name = name
end
end
p = Person.new() # name is nil!
p.name.upcase
# => NoMethodError: undefined method ‘upcase’ for nil:NilClass
p.name.try(:upcase)
# => nil
p.name.try(:gsub, /foo/, 'bar')
# => nil
p.name.try {|n| n.upcase.reverse }
# => nil
Every call returns nil, because name
is nil. The point is that no errors were raised.
Here's how it looks when name
isn't nil:
p2 = Person.new('Joe')
p2.name.try(:upcase)
# => "JOE"
p2.name.try(:gsub, /J/, 'A')
# => 'Aoe'
p2.name.try {|n| n.upcase.reverse }
# => "EOJ"