Les modules en Crystal remplissent deux rôles :
Le premier est de créer un espace de noms pour éviter les collisions de noms. Cependant, il regroupe aussi le code, ce qui permet de mieux comprendre à quoi il sert.
Le second est de définir des mixins pour partager du code entre les types.
Les modules sont similaires aux classes, mais la principale différence est qu'ils ne peuvent pas être instanciés et n'ont donc pas de variables d'instance.
Pour déclarer un module, on utilise le mot-clé module suivi du nom du module.
module Foo
end
Un espace de noms est une façon de regrouper du code.
Cela évite les collisions de noms et permet de mieux comprendre à quoi sert le code.
Pour accéder à une constante ou à une classe placée dans un espace de noms, on utilise l'opérateur ::.
module Foo
class Bar
end
end
Foo::Bar.new
Cela peut être utile lorsque, par exemple, plusieurs classes ont besoin de la même fonctionnalité « base », ou lorsque du code doit être partagé entre des classes sans lien entre elles. Ou encore lorsqu'on veut partager du code entre des classes qui n'ont aucun lien.
Il y a deux façons différentes d'utiliser un module comme mixin : la première consiste à utiliser le mot-clé include, et la seconde à utiliser le mot-clé extend.
Les deux méthodes rendent les constantes disponibles pour le type qui inclut ou étend le module.
Avec include, toutes les méthodes du module deviennent des méthodes d'instance du type qui inclut le module.
Le mot-clé include doit se trouver en haut du type, suivi du nom du module.
module Foo
def foo
"foo"
end
end
class Bar
include Foo
end
Bar.new.foo # => "foo"
extend fonctionne de manière similaire à include, mais au lieu de rendre les méthodes disponibles en tant que méthodes d'instance, il les rend disponibles en tant que méthodes de classe.
Le mot-clé extend doit être écrit en haut du type, suivi du nom du module.
module Foo
def foo
"foo"
end
end
class Bar
extend Foo
end
Bar.foo # => "foo"
Un motif courant en Crystal consiste à utiliser extend self dans un module.
Cela rend toutes les méthodes du module disponibles en tant que méthodes de classe sur le module lui-même.
Ainsi, tu n'as pas besoin de définir chaque méthode sur le module avec la syntaxe def self.method_name.
module Foo
extend self
def foo
"foo"
end
end
Foo.foo # => "foo"