# Assigning variables in a block

It's common pattern when you need to assign a variable inside a block

def query(sql)
logger.debug "Db: Executing query #{sql}"
result = nil
ts = Benchmark.realtime do
result = @db.exec sql
end
logger.debug "Db: Query completed in #{ts}"
result
end


Is there a more elegant way to rewrite this snippet?

-

I am afraid your code cannot be simplified further due to the imperative nature of Benchmark.realtime (it would have been better if it also returned the result of the block). Anyway, I think the best solution is to abstract it and write a wrapper method. For example:

def benchmark(msg)
logger.debug(msg)
result = nil
ts = Benchmark.realtime(&block) { result = yield }
logger.debug("Completed in #{ts} seconds")
result
end

def query(sql)
benchmark("DB: Executing query #{sql}") do
@db.exec(sql)
end
end


Now, if your question was on a more generic plane, let's say, that you have lots of methods that work like realtime which force you write a lot of boilerplate, I'd propose this generic Object#capture wrapper:

class Object
def capture(method, *args)
result2 = nil
result1 = send(method, *args) { |*bargs| result2 = yield(*bargs) }
[result1, result2]
end
end

realtime_output, block_output = Benchmark.capture(:realtime) do
"my output"
end #=> [7.263e-06, "my output"]

-
Yeah, I was asking about the generic pattern, the Benchmark.realtime is of course wrapped –  synapse Oct 12 '12 at 13:39
@synapse: I updated the answer, see the second snippet. –  tokland Oct 17 '12 at 14:37