Ruby proporciona una construcción llamada Module
que es como una Clase delgada. Al principio, los desarrolladores de Ruby pueden mirar Módulos como un «cajón de basura» de código que se lanza y se usa para organizar archivos, pero hay un método para esta locura. La clase Ruby Class
hereda de Module
y agrega cosas como instanciación, propiedades, etc., todas las cosas que normalmente pensarías que tendría una clase. Debido a que Module
es literalmente un antepasado de Class
, esto significa que los módulos pueden tratarse como clases de alguna manera.
Lo más importante para entender es cómo funciona la cadena de ancestros de Ruby. Como todos los lenguajes de programación orientada a objetos, Ruby admite la herencia. Cuando heredas de una clase, la superclase se agrega a tu cadena de ancestros. Usted puede ver los antepasados de cualquier clase llamando ancestors
en él:
class Foo < BarendFoo.ancestors#=>
Como se ha mencionado, usted puede encontrar Module
en el array Class.ancestors
.
Cuando include
un módulo en su clase, el módulo se agrega a la cadena de ancestros de su clase, al igual que una clase. Esto hace que include
sea solo una forma de herencia, no tiene nada de especial.
module Barendclass Foo include BarendFoo.ancestors#=>
Todos los métodos en el Bar
se agregan a Foo
como métodos de instancia. Debido a que Bar está en la cadena, incluso puede llamar a super
desde él para llamar a los métodos por encima de él en la cadena, ya sea que estén definidos en Módulos o Clases.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
aunque Bar
es un módulo, super
llama a la cadena y así Baz#hello
también es llamado. Vale la pena señalar que Bar
se agrega a la cadena de ancestros delante de Baz. Cuando se incluye un módulo, siempre se agrega directamente encima de su clase de inclusión. Esto puede ser confuso al agregar varios módulos, ya que se incluyen en orden «inverso» :
class Foo include A include BendFoo.ancestors#=>
Cuando A
incluido, que se inserta directamente encima de Foo
. Pero cuando se incluye B
, también se inserta directamente encima de Foo
, por lo que termina aterrizando antes de A
.
include vs extend
include
es bastante fácil de entender, agrega los métodos del módulo como métodos de instancia a su clase incluida. Puedes pensar en extend
haciendo lo mismo, pero agregando los métodos como métodos de clase.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
anteponer
además De incluir/ampliar, Ruby 2.0+ añade la etiqueta prepend
método. prepend
es similar a include
, pero en su lugar inserta el módulo antes de la clase de inclusión en la cadena de herencia.
class Foo include Bar prepend BazendFoo.ancestors#=>
En Ruby puede volver a abrir cualquier clase y redefinir los métodos en ella – no parece útil anular los métodos en una clase utilizando un módulo antepuesto en lugar de redefinirlos. Donde prepend
es útil es cuando todavía necesita la implementación original del método, porque los módulos antepuestos pueden anular los métodos pero aún usan super
para llamar a la implementación original.