Skip to content

Commit 292a9a8

Browse files
authored
Merge pull request #332 from rubyworks/claude/github-connector-capabilities-4qcre6
Make Kernel#try! the lenient one; keep #try strict
2 parents 580ab94 + 1bb2395 commit 292a9a8

3 files changed

Lines changed: 74 additions & 14 deletions

File tree

‎HISTORY.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
Changes:
66

7+
* Breaking
8+
9+
* `Kernel#try!` now returns `nil` when the receiver doesn't respond to the
10+
method (or it is private), instead of raising `NoMethodError`. `#try`
11+
stays strict. This is deliberately the opposite of ActiveSupport, which
12+
since Rails 4.0 makes `#try` lenient and `#try!` strict. In Ruby the bang
13+
marks the more dangerous method, and quietly ignoring a missing method is
14+
the dangerous behavior. The docs no longer claim ActiveSupport
15+
compatibility.
16+
* `Kernel#try` no longer calls private methods. It now behaves exactly like
17+
a normal call or `&.`, raising `NoMethodError` for them.
18+
719
* Enhancements
820

921
* New `lib/rails` library for Rails-compatible methods that Facets carries

‎lib/core/facets/kernel/try.rb‎

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,46 @@
33
module Kernel
44

55
# Invokes the method identified by the symbol +method+, passing it any
6-
# arguments and/or the block specified.
7-
#
8-
# Unlike regular send, a +NoMethodError+ exception will *not* be raised
9-
# if the receiving object is +nil+ (see NilClass#try below).
10-
#
11-
# Compatible with ActiveSupport's #try, plus an additional Functor
12-
# form when called with no arguments and no block.
6+
# arguments and/or the block specified. If the receiver is +nil+, it
7+
# returns +nil+ instead (see NilClass#try below). Otherwise it behaves
8+
# exactly like a normal call (or &.): a method the receiver doesn't have,
9+
# or a private one, raises NoMethodError.
1310
#
1411
# @example.try(:name) #=> "bob"
15-
# @example.try { |o| o.name } #=> "bob" (ActiveSupport block form)
16-
# @example.try.name #=> "bob" (Facets Functor form)
12+
# @example.try { |o| o.name } #=> "bob"
13+
# @example.try.name #=> "bob" (Functor form)
14+
# nil.try.name #=> nil
15+
#
16+
# NOTE: THIS IS NOT ACTIVESUPPORT'S #try. SINCE RAILS 4.0, ACTIVESUPPORT'S
17+
# #try RETURNS NIL FOR A METHOD THE RECEIVER DOESN'T HAVE, SO A TYPO LIKE
18+
# user.try(:nmae) QUIETLY BECOMES NIL AND HIDES THE BUG. FACETS KEEPS #try
19+
# STRICT AND PUTS THE LENIENT BEHAVIOR IN #try!, WHERE IT BELONGS: IN RUBY,
20+
# THE BANG MARKS THE MORE DANGEROUS VERSION OF A METHOD, AND SWALLOWING A
21+
# MISSING METHOD IS THE DANGEROUS ONE. RAILS HAS THE TWO BACKWARD.
1722
#
1823
def try(method=nil, *args, &block)
1924
if method
20-
__send__(method, *args, &block)
25+
public_send(method, *args, &block)
2126
elsif block_given?
2227
yield self
2328
else
2429
self
2530
end
2631
end
2732

28-
# Like #try, but raises NoMethodError if the method doesn't exist
29-
# (unless receiver is nil). Compatible with ActiveSupport's #try!.
33+
# Like #try, but also returns +nil+ if the receiver doesn't respond to
34+
# +method+ (or it is private), instead of raising NoMethodError.
35+
#
36+
# @example.try!(:name) #=> "bob"
37+
# @example.try!(:nmae) #=> nil
38+
#
39+
# NOTE: THIS IS THE OPPOSITE OF ACTIVESUPPORT'S #try!, WHICH RAISES. THE
40+
# BANG IS HERE BECAUSE QUIETLY IGNORING A MISSING METHOD IS THE DANGEROUS
41+
# BEHAVIOR; SEE #try ABOVE.
3042
#
3143
def try!(method=nil, *args, &block)
3244
if method
33-
public_send(method, *args, &block)
45+
public_send(method, *args, &block) if respond_to?(method)
3446
elsif block_given?
3547
yield self
3648
else
@@ -40,7 +52,6 @@ def try!(method=nil, *args, &block)
4052

4153
end
4254

43-
4455
class NilClass
4556

4657
# See Kernel#try.

‎test/core/kernel/test_try.rb‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,35 @@
1414
example.try.name.assert == "bob"
1515
end
1616

17+
test "raises for a missing method" do
18+
example = Struct.new(:name).new("bob")
19+
NoMethodError.assert.raised? { example.try(:nmae) }
20+
end
21+
22+
test "raises for a private method, like a normal call" do
23+
example = Class.new { private def secret; :s; end }.new
24+
NoMethodError.assert.raised? { example.try(:secret) }
25+
end
26+
27+
end
28+
29+
method :try! do
30+
31+
test do
32+
example = Struct.new(:name).new("bob")
33+
example.try!(:name).assert == "bob"
34+
end
35+
36+
test "returns nil for a missing method" do
37+
example = Struct.new(:name).new("bob")
38+
example.try!(:nmae).assert == nil
39+
end
40+
41+
test "returns nil for a private method" do
42+
example = Class.new { private def secret; :s; end }.new
43+
example.try!(:secret).assert == nil
44+
end
45+
1746
end
1847

1948
end
@@ -32,4 +61,12 @@
3261

3362
end
3463

64+
method :try! do
65+
66+
test do
67+
nil.try!(:name).assert == nil
68+
end
69+
70+
end
71+
3572
end

0 commit comments

Comments
 (0)