Recently I had a project where I wanted to make a simple “centralized logger” that could be called from anywhere in a small application. I decided to set up a simple class that I could call from anywhere in the app, re-using the Ruby Logger class’s various write methods (#debug, #warn, and so on).

I experimented with a lot of different solutions for forwarding or delegating a Ruby class method. My first stop was at the Forwardable module, but I couldn’t figure out a way to get it to delegate a class method. I eventually rigged up a somewhat hacky solution with define_method, but I wasn’t very happy with it.

Finally, a co-worker pointed me to SingleForwardable, which can be used to set up delegations for individual objects, classes, or modules. Perfect!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'logger'
require 'forwardable'

class SpecialLogger
  extend SingleForwardable

  @logger = Logger.new(STDOUT)
  @logger.level = Logger::INFO

  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
end

SpecialLogger.warn("Oh no!")
# W, [2014-12-29T15:07:39.704559 #41608]  WARN -- : Oh no!

Works perfectly, and in just a few quick, extremely readable, lines of code.