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"]