Tired of writing things like (user.name if user) and all it's variants?
Almost a year back now, I saw the try() method suggested. The intention of the method is to guard you against calling methods on a nil object. If you try to call a method on a nil object it should return nil, otherwise it should call the method. Here's the implementation:
class Object
##
# @person ? @person.name : nil
# vs
# @person.try(:name)
def try(method)
send method if respond_to? method
end
end
The implementation is a one liner. Nothing to get excited about, looks simple enough, what could be wrong? And so I've been happily using try's sparsely in my code ever since, and with me I suspect a lot of others, and was a happy camper.
But then today I started to write a few unit tests for that core extension. Hardly looked like it warranted unit tests, but for consistency sake in unit testing my apps core extensions I did it anyways. I created these 4 tests:
test "succeed when trying method" do
"hello".try(:length).should == 5
end
test "fail when trying method" do
assert_raises(NoMethodError) { "hello".try(:does_not_exist) }
end
test "not fail when trying method" do
nil.try(:does_not_exist).should == nil
end
test "return nil when calling any method on nil" do
nil.try(:to_s).should == nil
end
And so there we had it: 2 out of the 4 tests failed. Excuse me?!
So I took a closer look at this seemingly innocent little method. Apparently there are some pitfalls. For example:
>> "hello".try(:does_not_exist)
=> nil
While I expect nil.try(:does_not_exist) to return nil, I certainly do not expect nil when the receiver object is not nil. I expected a NoMethodError. Obviously, I'd say. How about this one:
>> nil.try(:to_s)
=> ""
I do expect nil.to_s to return an empty string, but not here. The intention is to be guarded against nil, and only if the receiver object is not nil should the method be called.
There is another pitfall, which I haven't created a test for, but something you might want to be aware of. If a class implements methods through a method_missing method then try might not recognize a valid method call because respond_to? will return false, and therefor try will return nil instead of the value that method_missing would return.
All pesky little details, but they could have gnarly consequences.
So I had a look around the internetz, and came across this. Don't like the ability to provide a default, instead of person.try(:name, :default => default) I'd rather do the usual person.try(:name) || default. Do like that you can do user.try(:manager, :name) instead of having to do user.try(:manager).try(:name). My example however immediately points out a flaw in it's implementation: if user.manager returns nil then the receiver object doesn't get changed and in the next iteration of the loop it returns the value of user.name. Easy to fix of course, so let's keep this feature in mind.
Then I came across a few other alternatives that were entertaining the idea of using method_missing, but let's not..
So maybe it's turtles to the rescue! Well, the author considers the code dumb, and I think he's right
Still, an entertaining idea.
And so I ended up at the door steps of the Master Guardian of Ruby Land: Ragan Wald. In his last blog post from summer last year he claims he's retired from hacking on Ruby. But nothing is further from the truth. This guy is continuously rethinking how to protect his code with his various slave guards, otherwise known as his little monads doing his dirty washings. He has even come up with some new slaves in the form of Rewrite and a few days ago with RewriteRails. See also his rather obscurely located 'blog post' announcement about this. It does appear like he is on to something very dangerous! Definitely worthwhile to keep an eye on.
Meanwhile, I'm thinking in more simple terms. I just want a very simple implementation of try with no fuss. After downloading more of the internetz I found one other person who argues that the try method isn't doing what it says it's doing. He came up with this instead:
class Object
##
# @person.name unless @person.nil?
# vs
# @person.try(:name)
def try(method)
self.send(method) unless self.nil?
end
end
Let's run this through the tests, see what happens:
4 tests, 4 assertions, 0 failures, 0 errors
Now that's what I'm talking about! This is the implementation that I'll use. For now. I'll accept the danger that anyone might actually contribute a different try method in some class.
UPDATE: And then I read this: And I for one welcome our new insect overlords.
Dear me! The try method has been added to rails 2.3/3.0 last month.
How wrong could I be when just a few minutes ago I wrote, with apparent misplaced confidence, that I'd accept the danger of anyone actually contributing a different try method. They might as well deliver me over to the mad doctor of blood island right now! No point in postponing the horrors that are my fate.
Incidentally, 3 days ago tap has been added to rails as well. But let's stay with try here for the moment. What is it's implementation?
# Tries to send the method only if object responds to it. Return +nil+ otherwise.
# It will also forward any arguments and/or block like Object#send does.
#
# ==== Example :
#
# # Without try
# @person ? @person.name : nil
#
# With try
# @person.try(:name)
#
# # try also accepts arguments/blocks for the method it is trying
# Person.try(:find, 1)
# @people.try(:map) {|p| p.name}
def try(method, *args, &block)
send(method, *args, &block) if respond_to?(method, true)
end
There you go. Even worse. Allowings arguments and a block, and bypassing the privacy security of your methods! It's like the IRS: they come in whenever they like and they take whatever they like. Sure, ruby is open, so if anyone wanted to break into my object and pry around my private stuff, fine, but do we have to promote that kind of behaviour?
No Mr Horse, I think this should be kidnapped, drugged, and chained deep down a mine shaft behind the bras of any Coober Pedy resident, preferably of those with long dusty zz-top beards.
(disclaimer, before anyone gets upset by my suggestion: I generally do not condone hiding things behind bras. Ever.)
The other issue remains: the example is misleading. Consider this instead:
# # Without try
# x ? x.to_i : nil
#
# With try
# x.try(:to_i)
When x = 123 you get 123, when x = nil you get 0 with try, nil without try.
Which tests were written? :
class ObjectTryTest < Test::Unit::TestCase
def setup
@string = "Hello"
end
def test_nonexisting_method
method = :undefined_method
assert !@string.respond_to?(method)
assert_nil @string.try(method)
end
def test_valid_method
assert_equal 5, @string.try(:size)
end
def test_valid_private_method
class << @string
private :size
end
assert_equal 5, @string.try(:size)
end
def test_argument_forwarding
assert_equal 'Hey', @string.try(:sub, 'llo', 'y')
end
def test_block_forwarding
assert_equal 'Hey', @string.try(:sub, 'llo') { |match| 'y' }
end
end
Okay, so again, why would anyone want to try an :undefined_method on a non-nil receiver? Doesn't make sense to me.
And for what purpose did the rails team add this try method? :

Clearly none whatsoever. Throughout the thousands of lines of code of rails it's used exactly once. No arguments, no block usage. So what's the point? Where's the value? Why do we get this?
I'd consider the reddit possibly more useful.

I added the try() method and nowhere has it been claimed to be the best/perfect implementation
Please feel free to submit patches and assign to me, or hit me up on irc.
— Pratik on January 12, 2009 at 7:38 pm #
Isn't the purpose of try() to make sure an object responds to the method you're trying to call, not protect you against calls on nil?
I think the best solution would be this:
class Object
def try(method, *args, &blk)
if respond_to? method
send(method, *args, &blk)
else
self
end
end
end
This way you try to apply a method. If you fail, you return the object itself. I guess you could also implement protection against nils in this as well if you wish.
— Fredrik W on January 12, 2009 at 7:46 pm #
[edit]
try is for all the people who've had @thing.method if @thing; else nil littering their code.
— asdf on January 13, 2009 at 5:35 am #
@pratik nice talking to you on irc earlier today. Now the implementation has changed to a version similar to what I'm currently using, which makes me happy... I guess (I still maintain it would be better to remove the method entirely). Others in the rails community that are using a different version, like Chris's version or the version of facets, should be aware of the incompatibility.
@fredrik that's not how it's advertised. It's advertised starting with "@person ?", which literally translates into: @person nil or not? Whether an object responds to a method or not was the technical implementation.
Furthermore, reread the post about the pitfall where classes implement methods via method_missing. You need to know the underlying implementation of the receiver's class in order to know how try was going to respond. That's a bad thing.
Your solution would misguide you on user.try(:manager).try(:name), if user does not respond to method :manager, but does respond to method :name.
@asdf indeed, litter, some restraint in the usage of #try would be good. As linked in Ragan's insect overlord post: Pervasive null tests are a code smell.
— Lawrence Pit on January 13, 2009 at 6:57 am #
Nice post. IMO, both responds_to? and unless nil are legitimate interpretations of such a method, they just mean different things. Which one should "try" mean? I have no idea, that's why andand is named andand, we thought it was self-documenting. I later wrote something with responds_to? semantics, and I called it "please."
I take your point about objects that do things with method_missing, but to be honest that isn't Pratik's fault. AFAIC, it is a bug if an object handles method m but returns false to responds_to?(:m).
I know that a very popular framework is littered with this bug, and that's a shame. But perhaps some of the contributors could take a moment to fix these issues up. I would consider your post a huge shout-out to people who like to toy with method_missing: don't forget to also implement responds_to?, there is no reason to settle for the default implementation.
Thanks again for a great post and the links. Much appreciated!
— Reg Braithwaite on January 13, 2009 at 7:18 am #
[...] Try() as you might [...]
— Ennuyer.net » Blog Archive » 2009-01-13 - Today’s Ruby/Rails Reading on January 13, 2009 at 6:35 pm #
An interesting exploration. Reg's assertion that 'both responds_to? and unless nil are legitimate interpretations of' "try" underlies the difficulty in establishing semantics for new idioms.
By way of comparison, consider an ORM that offers save and save! methods with the later raising an exception on a failed invocation, while the former merely returns false. Should myobj.save raise an exception if a validation fails, or should it merely log the exception and return false? IMO both are valid interpretations, the downside being that such potential for semantic ambiguity gives rise to a caveat emptor style of library development.
— Paul Carey on January 13, 2009 at 11:37 pm #
@Reg wrote:
> I know that a very popular framework is littered with this bug
Which one are you referring to? Merb?
Try a search for method_missing in the merb codebase. There are dozens of classes with a method_missing without a corresponding respond_to?. Doing a quick search through the code of some of the other major frameworks and gems I have not found a single one that does not have this "bug", so it appears to be the norm rather than the exception.
When using the respond_to? version confidence level in using it would be very low, because regardless of how the worlds should turn, reality is they're turning any way their gods fancy. At least with the unless x.nil? version there is a clear outcome, which can only be thwarted if a class re-implements the nil? method in an unexpected way, which, dare I make this possible mistake again of saying it, I estimate the risk of that to be negligible.
— Lawrence Pit on January 14, 2009 at 1:24 am #
I really prefer the andand syntax, it seems more natural. But I also like the 'try' keyword. Could we have the best of both worlds where try would work like andand if you don't pass it a method?
Compare,
obj.try(:something, arg1, arg2)
versus,
obj.try.something(arg1, arg2)
— Tom A on January 26, 2009 at 7:40 am #