Pedro Matiello

Dependency Injection in Ruby as inspired by Scala

15 Mar 2011 dependency-injection ruby scala

A recent discussion in the GOOS’ group has lead me to consider different ways to compose objects in Ruby. Specifically, as module inclusion seems to be the favored approach for adding stuff to classes in Ruby, I’ve became interested in finding a more flexible idiom for this.

The objective, therefore, is to define an instance variable in a module and be able to have it injected in instances of some class. Since I’m not that familiar with Ruby yet, I’m forced to turn to other languages for inspiration. A possible solution in Scala, presented below, is kind of intuitive.

Let’s start defining a simple trait Lightsource.

trait Lightsource {
  def on
  def off
}

The goal is to have a instance of a class implementing this trait injected in every instance of the class Room below.

abstract class Room {
  val lightsource:Lightsource
  def enter = lightsource.on
  def leave = lightsource.off
}

The injection can be performed by mixing-in the Room class with traits designed to provide a Lightsource. For example:

trait FluorescentLamp {
  val lightsource = new Lightsource {
    def on = println("Sad light on")
    def off = println("Sad light off")
  }
}

The actual composition reads nicely:

val sadRoom = new Room with FluorescentLamp

If the fluorescent lamp is unsatisfactory, an alternative implementation can be provided:

trait IncandescentLamp {
  val lightsource = new Lightsource {
    def on = println("Warm light on")
    def off = println("Warm light off")
  }
}
val warmRoom = new Room with IncandescentLamp

And I will stop at this point. Although there is opportunity for improvement, the implementation is satisfactory enough for me.

Having succeeded at the Scala front, we can tackle the same issue using Ruby. Here, we find ourselves both unable and not required to declare interfaces, so we move directly to the definition of our Room class.

class Room

  def enter
    @lightsource.on
  end

  def leave
    @lightsource.off
  end

end

The next step is to define a module responsible for providing an @lightsource object to instances of the Room` class. For instance:

module FluorescentLamp
  class FluorescentLampImpl
    def on
      puts "Sad light on"
    end

    def off
      puts "Sad light off"
    end
  end

  def initialize(*args, &b)
    super
    @lightsource = FluorescentLampImpl.new
  end
end

Alternatively:

module IncandescentLamp
  class IncandescentLampImpl
    def on
      puts "Warm light on"
    end

    def off
      puts "Warm light off"
    end
  end

  def initialize(*args, &b)
    super
    @lightsource = IncandescentLampImpl.new
  end
end

The actual composition does not read so nicely, but surely it can be improved by some sort of DSL. Being lazy, I’ll skip that, though:

sadRoom = Class.new(Room) do
  include FluorescentLamp
end.new

warmRoom = Class.new(Room) do
  include IncandescentLamp
end.new

Now, I’m suppose that this approach is not really within Ruby’s orthodoxy, but I found it interesting nevertheless. Also, it surprised me that the Scala version is both cleaner and smaller than the Ruby version, while still providing the safety advantages of the static typing.