Los módulos en Crystal tienen dos propósitos:
El primer propósito es crear un espacio de nombres para evitar colisiones de nombres. Sin embargo, también agrupa el código, lo que facilita entender para qué sirve.
El segundo propósito es definir mixin para compartir código con los tipos.
Los módulos son similares a las clases, pero la principal diferencia es que no se pueden instanciar y, por lo tanto, no tienen variables de instancia.
Para declarar un módulo, usas la palabra clave module seguida del nombre del módulo.
module Foo
end
Un espacio de nombres es una forma de agrupar código.
Esto evita colisiones de nombres y facilita entender para qué sirve el código.
Cuando quieres acceder a una constante o a una clase que se ha colocado dentro de un espacio de nombres, usas el operador ::.
module Foo
class Bar
end
end
Foo::Bar.new
Esto puede ser útil cuando, por ejemplo, varias clases necesitan la misma funcionalidad «base» o cuando se necesita compartir código entre clases no relacionadas. O cuando quieres compartir código entre clases que no están relacionadas.
Hay dos formas diferentes de usar un módulo como mixin: la primera es usar la palabra clave include y la segunda es usar la palabra clave extend.
Ambos métodos harán que las constantes estén disponibles para el tipo que incluye o extiende el módulo.
Include hará que todos los métodos del módulo estén disponibles como métodos de instancia en el tipo que incluye el módulo.
La palabra clave include debe ir al principio del tipo, seguida del nombre del módulo.
module Foo
def foo
"foo"
end
end
class Bar
include Foo
end
Bar.new.foo # => "foo"
Extend funciona de forma similar a include, pero en lugar de hacer que los métodos estén disponibles como métodos de instancia, los hace disponibles como métodos de clase.
La palabra clave extend debe escribirse al principio del tipo, seguida del nombre del módulo.
module Foo
def foo
"foo"
end
end
class Bar
extend Foo
end
Bar.foo # => "foo"
Un patrón típico en Crystal usa el patrón extend self en un módulo.
Esto hará que todos los métodos del módulo estén disponibles como métodos de clase en el propio módulo.
Esto significa que no tienes que asignar cada método al módulo usando la sintaxis def self.method_name.
module Foo
extend self
def foo
"foo"
end
end
Foo.foo # => "foo"