Skip to content

Commit

Permalink
Allow optional arguments and/or block for Object#try like Object#send…
Browse files Browse the repository at this point in the history
… does. [#1425 state:resolved]

Original suggestion by Pat Nakajima.

Signed-off-by: Pratik Naik <pratiknaik@gmail.com>
  • Loading branch information
alloy authored and lifo committed Nov 24, 2008
1 parent fffb1da commit 823b623
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 2 deletions.
9 changes: 7 additions & 2 deletions activesupport/lib/active_support/core_ext/object/misc.rb
Expand Up @@ -73,6 +73,7 @@ def acts_like?(duck)
end

# 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 :
#
Expand All @@ -81,7 +82,11 @@ def acts_like?(duck)
#
# With try
# @person.try(:name)
def try(method)
send(method) if respond_to?(method, true)
#
# # 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
end
7 changes: 7 additions & 0 deletions activesupport/test/core_ext/object_and_class_ext_test.rb
Expand Up @@ -271,4 +271,11 @@ class << @string
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

4 comments on commit 823b623

@rdp
Copy link

@rdp rdp commented on 823b623 Feb 21, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definition of try would seriously benefit from being implemented like andand, at least from my side of the fence.
Cheers!
-=r

@bitti
Copy link

@bitti bitti commented on 823b623 Mar 3, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rogerdpack is right. The current semantic has many problems.

Firstly the major usecase for try is to catch the case when the receiver is nil, not so much if the method is defined on the receiver. There are also lots of methods on nil, so e.g. object.try(:id) will not archive anything besides subtle errors.

Secondly, because catching the first missing method is not enough, one often sees cascades of two ore more tries:

User.admins.first.try(:address).try(:reset)

but (apart from the hard to read syntax) it can hide many problems, because if address is defined one expects to have a reset method on it and otherwise get an exception. So like in andand it would be better to have something like

User.admin.try { |u| u.address.reset }

I guess this form can even be implemented without breaking compatibility with the old form (because the current implementation expects a “method” parameter)

@bitti
Copy link

@bitti bitti commented on 823b623 Mar 3, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry it should’ve read

User.admins.first.try { |u| u.address.reset }

@mlanett
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like the behavior one really wants is
x != nil ? x.send(*args) : nil

which is rather different than the implementation.

Please sign in to comment.