Skip to content

Commit 9d43772

Browse files
transclaude
andcommitted
ActiveSupport compatibility: fix underscore and try
- Make String#underscore AS-compatible (converts :: to /) - String#snakecase remains pure case conversion without :: handling - Make Kernel#try AS-compatible (add block form and try!) - Facets' Tee form retained when try called with no args and no block - Verified all other AS overlaps (blank?, present?, symbolize_keys, deep_merge, reverse_merge, squish, remove, etc.) are already compatible Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1778d27 commit 9d43772

3 files changed

Lines changed: 54 additions & 33 deletions

File tree

HISTORY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Changes:
3333
* Rewrite `Kernel#callstack`, `#require_all`, `#load_all` to use `caller_locations`
3434
instead of parsing caller strings with regex.
3535
* Switch CI from Travis to GitHub Actions.
36+
* Make `String#underscore` ActiveSupport-compatible (converts `::` to `/`).
37+
Use `String#snakecase` for pure case conversion without namespace handling.
38+
* Make `Kernel#try` ActiveSupport-compatible (supports block form and `try!`).
39+
Facets' Tee/Functor form retained as extra feature.
3640

3741
* Bug Fixes
3842

lib/core/facets/kernel/try.rb

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,36 @@
33
module Kernel
44

55
# Invokes the method identified by the symbol +method+, passing it any
6-
# arguments and/or the block specified, just like the regular Ruby
7-
# <tt>Object#send</tt> does.
6+
# arguments and/or the block specified.
87
#
9-
# *Unlike* that method however, a +NoMethodError+ exception will *not*
10-
# be raised and +nil+ will be returned instead, if the receiving object
11-
# is a +nil+ object or NilClass.
8+
# Unlike regular send, a +NoMethodError+ exception will *not* be raised
9+
# if the receiving object is +nil+ (see NilClass#try below).
1210
#
13-
# For example, without try
11+
# Compatible with ActiveSupport's #try, plus an additional Tee/Functor
12+
# form when called with no arguments and no block.
1413
#
15-
# @example = Struct.new(:name).new("bob")
16-
#
17-
# @example && @example.name
18-
#
19-
# or:
20-
#
21-
# @example ? @example.name : nil
22-
#
23-
# But with try
24-
#
25-
# @example.try(:name) #=> "bob"
26-
#
27-
# or
28-
#
29-
# @example.try.name #=> "bob"
30-
#
31-
# It also accepts arguments and a block, for the method it is trying:
32-
#
33-
# @people.try(:collect){ |p| p.name }
14+
# @example.try(:name) #=> "bob"
15+
# @example.try { |o| o.name } #=> "bob" (ActiveSupport block form)
16+
# @example.try.name #=> "bob" (Facets Tee form)
3417
#
3518
def try(method=nil, *args, &block)
3619
if method
3720
__send__(method, *args, &block)
21+
elsif block_given?
22+
yield self
23+
else
24+
self
25+
end
26+
end
27+
28+
# Like #try, but raises NoMethodError if the method doesn't exist
29+
# (unless receiver is nil). Compatible with ActiveSupport's #try!.
30+
#
31+
def try!(method=nil, *args, &block)
32+
if method
33+
public_send(method, *args, &block)
34+
elsif block_given?
35+
yield self
3836
else
3937
self
4038
end
@@ -46,13 +44,25 @@ def try(method=nil, *args, &block)
4644
class NilClass
4745

4846
# See Kernel#try.
49-
def try(method=nil, *args)
47+
def try(method=nil, *args, &block)
5048
if method
5149
nil
50+
elsif block_given?
51+
nil
5252
else
53-
Functor.new{ nil }
53+
Tee.new { nil }
5454
end
5555
end
5656

57-
end
57+
# See Kernel#try!.
58+
def try!(method=nil, *args, &block)
59+
if method
60+
nil
61+
elsif block_given?
62+
nil
63+
else
64+
Tee.new { nil }
65+
end
66+
end
5867

68+
end

lib/core/facets/string/snakecase.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ class String
99
# "Snake Case".snakecase #=> "snake_case"
1010
# "Snake - Case".snakecase #=> "snake_case"
1111
#
12-
# Note, this method no longer converts `::` to `/`, in that case
13-
# use the {#pathize} method instead.
12+
# Note, unlike #underscore this does not convert `::` to `/`.
13+
# Use {#pathize} for that, or use {#underscore} for ActiveSupport
14+
# compatible behavior.
1415

1516
def snakecase
16-
#gsub(/::/, '/').
1717
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
1818
gsub(/([a-z\d])([A-Z])/,'\1_\2').
1919
tr('-', '_').
@@ -22,10 +22,17 @@ def snakecase
2222
downcase
2323
end
2424

25+
# Like #snakecase but also converts '::' to '/' for
26+
# namespace-to-path conversion. This is compatible with
27+
# ActiveSupport's #underscore.
2528
#
26-
alias_method :underscore, :snakecase
29+
# "SnakeCase".underscore #=> "snake_case"
30+
# "Foo::Bar".underscore #=> "foo/bar"
31+
#
32+
def underscore
33+
gsub(/::/, '/').snakecase
34+
end
2735

2836
# TODO: Add *separators to #snakecase, like camelcase.
2937

3038
end
31-

0 commit comments

Comments
 (0)