I moduli in Crystal hanno due scopi:
Il primo è creare un namespace per evitare collisioni di nomi. Tuttavia, raggruppano anche il codice, rendendo più facile capire a cosa serve.
Il secondo è definire dei mixin per condividere codice tra i tipi.
I moduli sono simili alle classi, ma la differenza principale è che non possono essere istanziati e quindi non hanno variabili di istanza.
Per dichiarare un modulo, si usa la parola chiave module seguita dal nome del modulo.
module Foo
end
Un namespace è un modo per raggruppare il codice.
Questo evita conflitti di nomi e rende più facile capire a cosa serve il codice.
Per accedere a una costante o a una classe inserita in un namespace, si usa l'operatore ::.
module Foo
class Bar
end
end
Foo::Bar.new
Questo può essere utile quando, per esempio, più classi hanno bisogno della stessa funzionalità di «base» o quando il codice deve essere condiviso tra classi non correlate. O quando si vuole condividere codice tra classi che non hanno alcuna relazione tra loro.
Ci sono due modi diversi per usare un modulo come mixin: il primo è usare la parola chiave include, il secondo è usare la parola chiave extend.
Entrambi i metodi rendono le costanti disponibili al tipo che include o estende il modulo.
Include rende tutti i metodi del modulo disponibili come metodi di istanza sul tipo che include il modulo.
La parola chiave include va inserita all'inizio del tipo, seguita dal nome del modulo.
module Foo
def foo
"foo"
end
end
class Bar
include Foo
end
Bar.new.foo # => "foo"
Extend funziona in modo simile a include, ma invece di rendere i metodi disponibili come metodi di istanza, li rende disponibili come metodi di classe.
La parola chiave extend va scritta all'inizio del tipo, seguita dal nome del modulo.
module Foo
def foo
"foo"
end
end
class Bar
extend Foo
end
Bar.foo # => "foo"
Un pattern tipico in Crystal è usare il pattern extend self in un modulo.
Questo rende tutti i metodi del modulo disponibili come metodi di classe sul modulo stesso.
Vuol dire che non devi assegnare ogni metodo al modulo usando la sintassi def self.method_name.
module Foo
extend self
def foo
"foo"
end
end
Foo.foo # => "foo"