Skip to content

Commit

Permalink
Added Object#try. ( Taken from http://ozmm.org/posts/try.html ) [Chri…
Browse files Browse the repository at this point in the history
…s Wanstrath]
  • Loading branch information
lifo committed Nov 19, 2008
1 parent 51a19ae commit 5173079
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 0 deletions.
2 changes: 2 additions & 0 deletions activesupport/CHANGELOG
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
13 changes: 13 additions & 0 deletions activesupport/lib/active_support/core_ext/object/misc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)

This comment has been minimized.

Copy link
@matthewrudy

matthewrudy Dec 4, 2008

Contributor

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
25 changes: 25 additions & 0 deletions activesupport/test/core_ext/object_and_class_ext_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@fxn fxn commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

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
Copy link
Contributor

@alloy alloy commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

Nice!

@fd
Copy link

@fd fd commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

‘send?’ would be nice.

@rdp
Copy link

@rdp rdp commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

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
Copy link

Choose a reason for hiding this comment

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

+1 for send?

@Aupajo
Copy link
Contributor

@Aupajo Aupajo commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

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

`person.attempt?(:name)`

@henrik
Copy link
Contributor

@henrik henrik commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

+1 for “send?”.

@nakajima
Copy link
Contributor

Choose a reason for hiding this comment

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

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

@alloy
Copy link
Contributor

@alloy alloy commented on 5173079 Nov 19, 2008

Choose a reason for hiding this comment

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

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

@matthewrudy
Copy link
Contributor

Choose a reason for hiding this comment

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

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

@mniessner
Copy link

Choose a reason for hiding this comment

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

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

@josegit
Copy link

Choose a reason for hiding this comment

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

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

@nertzy
Copy link
Contributor

@nertzy nertzy commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

+1 nakajima

@henrik
Copy link
Contributor

@henrik henrik commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

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
Copy link
Contributor

@henrik henrik commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

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

@henrik
Copy link
Contributor

@henrik henrik commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

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

@lifo
Copy link
Member Author

@lifo lifo commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

Please submit patches at Lighthouse and assign to me.

Thanks!

@adkron
Copy link
Contributor

@adkron adkron commented on 5173079 Nov 20, 2008

Choose a reason for hiding this comment

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

this is nice sugar.

@alloy
Copy link
Contributor

@alloy alloy commented on 5173079 Nov 21, 2008

Choose a reason for hiding this comment

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

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
Copy link

Choose a reason for hiding this comment

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

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

@defmthd
Copy link

Choose a reason for hiding this comment

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

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

@rob-at-thewebfellas
Copy link
Contributor

Choose a reason for hiding this comment

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

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

@vitaly
Copy link

@vitaly vitaly commented on 5173079 Nov 25, 2008

Choose a reason for hiding this comment

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

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

@nakajima
Copy link
Contributor

Choose a reason for hiding this comment

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

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
Copy link
Contributor

Choose a reason for hiding this comment

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

+1 nakajima

@josevalim
Copy link
Contributor

Choose a reason for hiding this comment

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

nakajima suggestion is already into core:

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

Please sign in to comment.