Procs in Ruby for Convenience
Message from 2022
This post is pretty old! Opinions and technical information in it are almost certainly oudated. Commands and configurations will probably not work. Consider the age of the content before putting any of it into practice.
This was pretty raunchy (especially when the call to log()
was repeated every other line in a 50-line script). So when I wrote a similar script, I put most of the redundancy into a Proc
object:
l = Proc.new do |m| log(team, service_id, m, Time.now()) end register b, team.ip, u, p l.call "A registered u} / p}" session b, team.ip, u, p do |d| do_something d end l.call "A did something"
How does this work?
Ruby blocks are one of the most visible and distinctive pieces of syntax in Ruby. Their close friend, the Proc
object, is how you separate the logic of a block from syntax and store it in a variable (or turn it into a method, but that’s something else).
Blocks and Proc
s are similar to closures as seen in other languages, in that they bind to variables in the scope where they’re defined. Above, we note that team
and service_id
aren’t defined within the block’s scope, so Ruby instead looks to the enclosing scope for their values. Since it is able to find them there, those values get attached to the block, so that wherever the block goes, those values for team
and service_id
will follow. Since m
is specified as an argument to the block (the |m|
syntax), Ruby doesn’t go looking for it.
Since the block wasn’t going to be useful just sitting there, I converted it into a Proc
object and stashed it in l
. At this point, the 1-ary (takes one argument) Proc
in l
contains the logic from the block, and the values from the variables above, all encapsulated neatly in a one-letter variable. Since it’s just a normal object, we can call its methods:
l.arity # => 1 l['lol'] # => does logging stuff l.call 'lol' # => does logging stuff
Either calling method (either []
or call
) is smaller than the whole shebang from the first script above, so that’s a big win for DRY.