Shorthands in Ruby Code Blocks
· 1 min read
Sometimes Ruby, being like a functional language, unobtrusively forces us to use
code blocks within iterators (such as
map
,
each
, etc.) More than offen, these codeblock
are kinda one-liners and woo-
do-|o|-end
magic makes a code looking overdriven:
arr.each do |x|
x.name
end.join(" ")
Well, there is curly-brackets-notation available, hence we may rewrite the code above within one line. But there is still a lot of absolutely unnecessary garbage hiding the core of whatβs being actually done.
The good news is: ruby provides us with a syntactic sugar for that stuff. Letβs look at this:
arr.each (&:name).join(" ")
It is fully equivalent to the
do-|o|-end
codepiece above, but the readability is drastically improved.
How the hell does it work?
The
&obj
is evaluated in ruby in the following way:
-
if object is a
block, it converts the block into a simpleproc. -
if object is a
Proc, it converts the object into a block while preserving thelambda?status of the object. -
if object is not a
Proc, it first calls#to_procon the object and then converts it into a block.
In our case
the method is
#to_proc
on a
Symbol
βs instance (because
:name.class == Symbol
). The
Symbol#to_proc
method
was originally added by
ActiveSupport
but has been integrated into Ruby since 1.8.7.
To enable this shorthand for classes other than
Symbol
, e. g. for an
Array
:
class Array
def to_proc
lambda { |recv| recv.send *self }
end
end
Now we can write:
[ "Apple", "Orange", "Pear" ].map &[ :+, " is a fruit." ]
[ "Apple", "Orange", "Pear" ].map &[ :match, "[a-z]e" ]
yielding:
Β #β ["Apple is a fruit.", "Orange is a fruit.", "Pear is a fruit."]
Β #β [#<MatchData "le">, #<MatchData "ge">, nil]
Methods are being called on array elements (on
String
s in the example above.)
-
Kinda same trick may be done for external methods using
&method
shorthand. Letβs say we have:
arr.each do |x|
get_fullname x
end.join(" ")
Thus, assuming we have the
get_fullname
method defined, we can rewrite it as:
arr.each &method(:get_fullname)
In other words,
%w{ first second third }.map &method(:puts)
will print the array content out (expanding to
{ |s| puts s }
).