Inclusión de módulos en Ruby

Mario López

A primera vista, los módulos de ruby parecieran ser simplemente cajas donde uno puede almacenar todo timpo de estructuras. En realidad esta idea no esta lejos de la misma definición de módulo:

Los módulos son colecciones de objetos, métodos y constantes

En el ultimo tiempo he dedicado harto tiempo a estudiar cómo funcionan y como sacarles mayor provecho. Personalmente, me gusta pensar que los módulos tienen un propósito: Ser incluidos o ser extendidos.

Incluir un módulo

Cuando incluimos un módulo dentro de una clase, basicamente estamos compartiendo los métodos del módulo y definiéndolos como si fuesen métodos de instancia de la clase.

module Greetings
  def greet
    "hello"
  end
end

class Foo
  include Greetings
end

Foo.new.greet 
=> "hello"

Lo que realmente hace este método include es modificar la cadena de acestros que pasee la clase, y esa es la razón por la que se pueden utilizar los métodos del módulo como si fuesen métodos de instancia.

Foo.ancestors
=> [Foo, Greetings, Object, Kernel, BasicObject]

Esto es bastante similar a lo que sucede herencia, con la excepción de que en Ruby solo es posible heredar desde una única clase. Una clase Ruby puede incluir muchos módulos.

Extendiendo clases

Si incluir un módulo en una clase se agrega métodos de instancia, ¿Habrá alguna forma de agregar métodos de clase? Por supuesto! Al extender una clase con los métodos de un módulo, se toman los métodos del módulo y se aplican sobre la clase como si fuesen métodos de clase.

module Methods
  def some
    1
  end
end

class Foo
  extend Methods
end

Foo.some
=> 1

Una técnica muy popular de combinar inclusión y extensión se utiliza al redefinir el método included que es llamado por una clase a la hora de incluir un módulo.

module Greetings
  def self.included(base)
    base.extend(ClassMethods)
  end

  def greet
    "Hello"
  end
  
  module ClassMethods
    def some
      1
    end
  end
end

class Foo
  include Greetings
end

Foo.new.greet
=> "hello"

Foo.some
=> 1

Con lo cual ahora el módulo esta preparado para extender la clase en sus métodos de instancia y de clase.