Jun 15 2006

Ruby Mixin Tutorial

In Java you just have classes (both abstract and concrete) and interfaces. The Ruby language provides classes, modules, and a mix of both. In this post I want to dive into mixins in Ruby.

In the Ruby language a mixin is a class that is mixed with a module. In other words the implementation of the class and module are joined, intertwined, combined, etc. A mixin is a different mechanism to the extend construct used to add concrete implementation to a class. With a mixin you can extend from a module instead of a class. Before we get started with the mixin examples let me first explain what a module is.

I think of a module as a degenerate abstract class. A module can’t be instantiated and no class can directly extend it but a module can fully implement methods. A class can leverage the implementation of a module by including the module’s methods. A module can define methods that can be shared in different and separate classes either at the class or instance level.

Let me define a module, albeit a trivial one, that would convert a numeric integer value to English.

# Convert a integer value to English.
module Stringify
  # Requires an instance variable @value
  def stringify
    if @value == 1
      "One"
    elsif @value == 2
      "Two"
    elsif @value == 3
      "Three"
    end
  end
end

Note that the Stringify module makes use of a @value instance variable. The class that will be mixed with this module needs to define and set a @value instance variable since the Stringify module uses it but does not define it. In addition to instance variables a module could invoke methods defined not in the module itself but in the class that it will be mixed with.

Now let me construct a self contained module that is not dependent on the implementation of any class that it can be mixed with.

# A Math module akin to Java Math class.
module Math
  # Could be called as a class, static, method
  def add(val_one, val_two)
    BigInteger.new(val_one + val_two)
  end
end

The methods in the Math module are intended to be invoked like class methods, also known as static methods. The add method in the Math module accepts two integer values and returns an instance of BigInteger. Let me now define the mixin BigInteger class.

# Base Number class
class Number
  def intValue
    @value
  end
end

# BigInteger extends Number
class BigInteger < Number


  # Add instance methods from Stringify
  include Stringify

  # Add class methods from Math
  extend Math

  # Add a constructor with one parameter
  def initialize(value)
    @value = value
  end
end

Continue reading