"try" method - do you need it?
What is "try"?
Have you ever end up with this code?
@person && @person.name
We are doing that since we don't know here if @person
is nil
value. However we can refactor this line with try
:
@person.try(:name)
Which is equivalent to
@person.nil? ? nil : @person.name
So no exception is raised and name
method is called if @person
is nil
. We are happy:)
Arguments
The first argument is the method name, and other arguments are passed as arguments to the called method.
@people.take(5)
can be written as:
@people.try(:take, 5)
Method Chaining
try
method returns always nil
if called on nil
object:
nil.try(:foo) # => nil
Thanks to that we can refactor this:
@person && @person.name && @person.name.downcase == "Kavinsky"
into that:
@person.try(:name).try(:downcase) == "Kavinsky"
You can take it even further to avoid repeating try
:
"Don'tRepeatYourselfs ".try_chain(:underscore, :capitalize, :chop!, :chop!)
Where does try come from?
try
method is defined on Object
class and NilClass
class in Rails and it's not part of Ruby itself. However if you want use it without Rails, you can install activesupport
gem only and then call:
require 'active_support/core_ext/object/try'
Do you really need to use try?
I'm wondering if you ever need to use try. I had a big problem coming up with real example so I just grabbed it from Rails source code. Let's take a look at some examples from another blog post describing try to see if we can avoid try
@manufacturer.products.first.try(:enough_in_stock?, 32) # => "Yes"
Why the heck would you ask this query?
@manufacturer.products.try(:collect) { |p| p.name } # => ["3DS", "Wii"]
Here you can use @manufacturer.products.collect
since products
will return an array which responds to collect
(if you use has_many
association in the model).
What about this one? Is try
really necessary here?
if current_user.try(:is_admin?)
We don't have to use try
if we represent special case as another object (like in Confident Ruby book section 2.2). The problem try
wants to solve is that we don't want to get NoMethodError calling is_admin?
on nil
object. So what we can do is to make the object respond to the desired method is_admin?
by defining the method. The current_user
method can actually return an instance of the following class if the user is not signed in:
class GuestUser
def is_admin?
false
end
end
Now you can use:
if current_user.is_admin?
Conclusion
try
can make your code shorter when you are dealing with potential nil objects. However I would always think about the code redesign first before injecting try
into your code.
Note:
Just for the sake of completeness try method accepts a block.
More on that here http://api.rubyonrails.org/classes/Object.html#method-i-try
2.0.0p195, Rails 3.2
Written by David Hrachový
Related protips
2 Responses
A good read from Thoughtbot on "shallow nils" (alternative to try
): http://robots.thoughtbot.com/swallow-nil
Great example, thanks!