Permalink
Browse files

Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chri…

…s Wanstrath]
  • Loading branch information...
1 parent 51a19ae commit 51730792ca930a896361eb92354a42bc56903de1 @lifo lifo committed Nov 19, 2008
@@ -1,5 +1,7 @@
*2.3.0 [Edge]*
+* Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chris Wanstrath]
+
* Added Enumerable#none? to check that none of the elements match the block #1408 [Damian Janowski]
* TimeZone offset tests: use current_period, to ensure TimeZone#utc_offset is up-to-date [Geoff Buesing]
@@ -71,4 +71,17 @@ def with_options(options)
def acts_like?(duck)
respond_to? "acts_like_#{duck}?"
end
+
+ # Tries to send the method only if object responds to it. Return +nil+ otherwise.
+ #
+ # ==== Example :
+ #
+ # # Without try
+ # @person ? @person.name : nil
+ #
+ # With try
+ # @person.try(:name)
+ def try(method)
+ send(method) if respond_to?(method, true)
@matthewrudy
matthewrudy Dec 4, 2008

noticed a problem.

> > “some string”.try(:gsub, “some”, “this”)
> > => “this string”
> >
> > nil.try(:gsub, “some”, “this”)
> > => TypeError: $_ value need to be String (nil given)
> > from (irb):18:in `gsub’
> > from (irb):18:in`send’
> > from (irb):18
> > from :0

This is because nil defines a private method “gsub”.
Is there really a need for “try” to “try” private methods?
It seems like a less useful case than someone using gsub against an accidental nil.

+ end
end
@@ -247,3 +247,28 @@ def test_instance_exec_nested
[arg] + instance_exec('bar') { |v| [reverse, v] } }
end
end
+
+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
+
+end

27 comments on commit 5173079

@fxn
Member
fxn commented on 5173079 Nov 19, 2008

I like the functionality, but the name doesn’t quite “click” to me either.

Bang methods have different semantics… what about “send?”. Oh, the signature could accept args as well.

@alloy
alloy commented on 5173079 Nov 19, 2008

Nice!

@fd
fd commented on 5173079 Nov 19, 2008

‘send?’ would be nice.

@rdp
rdp commented on 5173079 Nov 19, 2008

take a look at .andand
http://weblog.raganwald.com/2008/01/objectandand-objectme-in-ruby.html
though renaming it
._?
might be nicer # almost equivalent to groovy’s object.?.method

@Roman2K

+1 for send?

@Aupajo
Aupajo commented on 5173079 Nov 19, 2008

How about `attempt?`? Try reminds me of `try… catch`

`person.attempt?(:name)`

@henrik
henrik commented on 5173079 Nov 19, 2008

+1 for “send?”.

@nakajima

I like the name try, though I prefer a version that allows arguments and/or a block: http://gist.github.com/26751

@alloy
alloy commented on 5173079 Nov 19, 2008

Try is fine by me, but +1 for nakajima’s patch.

@matthewrudy

I particularly like the use of the symbol :wee in his examples.

@mniessner

why not use a proxy so you could just do string.try.size ?

@josegit

-5 for send?. Foo? methods, by convention, return boolean values. Try does not.

@nertzy
nertzy commented on 5173079 Nov 20, 2008

+1 nakajima

@henrik
henrik commented on 5173079 Nov 20, 2008

josegit: That’s usually the case, but not always. To quote matz from a recent ruby-talk thread:

By convention, they are predicates, which means the return value can
be considered as boolean value. But at the same time, the values are
not limited to true and false. For example, defined? returns the
string to describe the kind of expression defined, and nil otherwise.

There’s another convention that when a predicate returns non-true
value for represent true, it should return nil for false.

Then again, whether this is a predicate can be discussed. I think I’ll retract my +1 for a ±0.

@henrik
henrik commented on 5173079 Nov 20, 2008

Mangled by textile. First and last paragraphs are mine, middle two are quoted from matz.

@henrik
henrik commented on 5173079 Nov 20, 2008

A better example may be Ruby’s Numeric#nonzero? which returns the number if it’s not zero and returns nil otherwise. send? would be pretty analogous.

@wagenet

I’ll let others hash out the appropriate method name, but I agree that it should at least support arguments like #send.

@lifo
Member
lifo commented on 5173079 Nov 20, 2008

Please submit patches at Lighthouse and assign to me.

Thanks!

@adkron
adkron commented on 5173079 Nov 20, 2008

this is nice sugar.

@alloy
alloy commented on 5173079 Nov 21, 2008

Sumitted patch with Nakajima’s suggestion: http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1425-allow-optional-arguments-andor-block-for-objecttry

@lucianopanaro

Submitted a patch based on alloy’s, adding the option of doing person.try_name(arg1, arg2, …) { … }

http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1425-allow-optional-arguments-andor-block-for-objecttry

@peanut
peanut commented on 5173079 Nov 21, 2008

Thank you very-very much. I think it will be very useful!

@rob-at-thewebfellas

Surely with nakajima’s version it should be called do_or_do_not – remember “there is no try”…

@vitaly
vitaly commented on 5173079 Nov 25, 2008

The problem is nil.respond_to?(:id) is true. and so you can’t use it as for @object.try.id

@nakajima

vitaly: That’s an instance in which you’d probably want to rethink using #try anyway, and instead perhaps structure your interface in such a way that doesn’t return nil objects.

I know it’s ridiculous for me to say this, since I was the one who recommended an “improved” version, but I actually think that nine times out of ten, using #try is a code smell.

@carlosbrando

+1 nakajima

@josevalim
Member

nakajima suggestion is already into core:

http://github.com/rails/rails/commit/823b623fe2de8846c37aa13250010809ac940b57

Please sign in to comment.