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 = do |m|
  log(team, service_id, m,

register b, team.ip, u, p "A registered #{u} / #{p}"
session b, team.ip, u, p do |d|
  do_something d
end "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 Procs 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:

# => 1
# => does logging stuff '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.