Module

Module erfüllen in Ruby zwei Zwecke: Namensräume und Mix-in-Funktionalität.

Ein Namensraum kann verwendet werden, um Code nach Paketen oder Funktionalitäten zu organisieren, was gemeinsame Namen vor Interferenzen durch andere Pakete schützt. Zum Beispiel bietet der IRB-Namensraum Funktionalitäten für irb, die Kollisionen für den gemeinsamen Namen „Context“ verhindern.

Mix-in-Funktionalität ermöglicht das Teilen gemeinsamer Methoden über mehrere Klassen oder Module hinweg. Ruby kommt mit dem Mix-in-Modul Enumerable, das viele Enumerationsmethoden basierend auf der each-Methode bereitstellt, und Comparable ermöglicht den Vergleich von Objekten basierend auf der <=>-Vergleichsmethode.

Beachten Sie, dass es viele Ähnlichkeiten zwischen Modulen und Klassen gibt. Neben der Möglichkeit, ein Modul als Mix-in zu verwenden, gilt die untenstehende Beschreibung von Modulen auch für Klassen.

Moduldefinition

Ein Modul wird mit dem Schlüsselwort module erstellt.

module MyModule
  # ...
end

Ein Modul kann beliebig oft neu geöffnet werden, um Funktionalität hinzuzufügen, zu ändern oder zu entfernen.

module MyModule
  def my_method
  end
end

module MyModule
  alias my_alias my_method
end

module MyModule
  remove_method :my_method
end

Das Neueröffnen von Modulen (oder Klassen) ist eine sehr mächtige Funktion von Ruby, aber es ist am besten, nur Module zu öffnen, die Ihnen gehören. Das Neueröffnen von Modulen, die Ihnen nicht gehören, kann zu Namenskollisionen oder schwer zu diagnostizierenden Fehlern führen.

Verschachtelung

Module können verschachtelt werden.

module Outer
  module Inner
  end
end

Viele Pakete erstellen ein einziges äußerstes Modul (oder Klasse), um einen Namensraum für ihre Funktionalität bereitzustellen.

Sie können auch innere Module mit :: definieren, vorausgesetzt, die äußeren Module (oder Klassen) sind bereits definiert.

module Outer::Inner::GrandChild
end

Beachten Sie, dass dies einen NameError auslöst, wenn Outer und Outer::Inner nicht bereits definiert sind.

Dieser Stil hat den Vorteil, dass der Autor die Einrückung reduzieren kann. Anstelle von 3 Einrückungsebenen ist nur eine notwendig. Der Geltungsbereich der Konstantenauflösung ist jedoch anders, wenn ein Namensraum mit dieser Syntax anstelle der ausführlicheren Syntax erstellt wird.

Scope

self

self bezieht sich auf das Objekt, das den aktuellen Geltungsbereich definiert. self ändert sich beim Betreten einer anderen Methode oder beim Definieren eines neuen Moduls.

Constants

Zugängliche Konstanten sind je nach Modulverschachtelung (welche Syntax zur Definition des Moduls verwendet wurde) unterschiedlich. Im folgenden Beispiel ist die Konstante A::Z von B aus zugänglich, da A Teil der Verschachtelung ist.

module A
  Z = 1

  module B
    p Module.nesting #=> [A::B, A]
    p Z #=> 1
  end
end

Wenn Sie jedoch :: verwenden, um A::B zu definieren, ohne es in A zu verschachteln, wird eine Ausnahme NameError ausgelöst, da die Verschachtelung A nicht einschließt.

module A
  Z = 1
end

module A::B
  p Module.nesting #=> [A::B]
  p Z #=> raises NameError
end

Wenn eine Konstante auf der obersten Ebene definiert ist, können Sie ihr :: voranstellen, um darauf zu verweisen.

Z = 0

module A
  Z = 1

  module B
    p ::Z #=> 0
  end
end

Methoden

Informationen zur Methodendefinition finden Sie in der Syntaxdokumentation für Methoden.

Klassenmethoden können direkt aufgerufen werden. (Dies ist etwas verwirrend, aber eine Methode eines Moduls wird oft als „Klassenmethode“ anstelle von „Modulmethode“ bezeichnet. Sehen Sie auch Module#module_function, das eine Instanzmethode in eine Klassenmethode umwandeln kann.)

Wenn eine Klassenmethode eine Konstante referenziert, verwendet sie dieselben Regeln wie die Referenzierung außerhalb der Methode, da der Geltungsbereich derselbe ist.

Instanzmethoden, die in einem Modul definiert sind, sind nur aufrufbar, wenn sie inkludiert werden. Diese Methoden haben über die Ahnenliste Zugriff auf die Konstanten, die zum Zeitpunkt ihrer Inklusion definiert wurden.

module A
  Z = 1

  def z
    Z
  end
end

include A

p self.class.ancestors #=> [Object, A, Kernel, BasicObject]
p z #=> 1

Sichtbarkeit

Ruby hat drei Arten der Sichtbarkeit. Der Standard ist public. Eine öffentliche Methode kann von jedem anderen Objekt aufgerufen werden.

Die zweite Sichtbarkeit ist protected. Beim Aufruf einer geschützten Methode muss der Sender die Klasse oder das Modul erben, das die Methode definiert. Andernfalls wird ein NoMethodError ausgelöst.

Geschützte Sichtbarkeit wird am häufigsten verwendet, um == und andere Vergleichsmethoden zu definieren, bei denen der Autor den Zustand eines Objekts nicht für jeden Aufrufer preisgeben möchte und ihn nur auf geerbte Klassen beschränken möchte.

Hier ist ein Beispiel

class A
  def n(other)
    other.m
  end
end

class B < A
  def m
    1
  end

  protected :m

end

class C < B
end

a = A.new
b = B.new
c = C.new

c.n b #=> 1 -- C is a subclass of B
b.n b #=> 1 -- m called on defining class
a.n b # raises NoMethodError A is not a subclass of B

Die dritte Sichtbarkeit ist private. Eine private Methode kann nur innerhalb der besitzenden Klasse ohne Empfänger oder mit einem literalen self als Empfänger aufgerufen werden. Wenn eine private Methode mit einem anderen Empfänger als einem literalen self aufgerufen wird, wird eine NoMethodError ausgelöst.

class A
  def without
    m
  end

  def with_self
    self.m
  end

  def with_other
    A.new.m
  end

  def with_renamed
    copy = self
    copy.m
  end

  def m
    1
  end

  private :m
end

a = A.new
a.without      #=> 1
a.with_self    #=> 1
a.with_other   # NoMethodError (private method `m' called for #<A:0x0000559c287f27d0>)
a.with_renamed # NoMethodError (private method `m' called for #<A:0x0000559c285f8330>)

alias und undef

Sie können Methoden auch aliasen oder undefinieren, aber diese Operationen sind nicht auf Module oder Klassen beschränkt. Informationen dazu finden Sie im Abschnitt über sonstige Syntax.

Klassen

Jede Klasse ist auch ein Modul, aber im Gegensatz zu Modulen kann eine Klasse nicht in ein anderes Modul (oder eine andere Klasse) als Mix-in übernommen werden. Wie ein Modul kann eine Klasse als Namensraum verwendet werden. Eine Klasse erbt auch Methoden und Konstanten von ihrer Oberklasse.

Definition einer Klasse

Verwenden Sie das Schlüsselwort class, um eine Klasse zu erstellen.

class MyClass
  # ...
end

Wenn Sie keine Oberklasse angeben, erbt Ihre neue Klasse von Object. Sie können von einer anderen Klasse erben, indem Sie < gefolgt von einem Klassennamen verwenden.

class MySubclass < MyClass
  # ...
end

Es gibt eine spezielle Klasse BasicObject, die als leere Klasse konzipiert ist und ein Minimum an integrierten Methoden enthält. Sie können BasicObject verwenden, um eine unabhängige Vererbungsstruktur zu erstellen. Weitere Details finden Sie in der Dokumentation zu BasicObject.

Genau wie Module können auch Klassen neu geöffnet werden. Beim Neueröffnen einer Klasse können Sie deren Oberklasse weglassen. Die Angabe einer anderen Oberklasse als in der vorherigen Definition führt zu einem Fehler.

class C
end

class D < C
end

# OK
class D < C
end

# OK
class D
end

# TypeError: superclass mismatch for class D
class D < String
end

Vererbung

Jede Methode, die in einer Klasse definiert ist, kann von ihrer Unterklasse aufgerufen werden.

class A
  Z = 1

  def z
    Z
  end
end

class B < A
end

p B.new.z #=> 1

Dasselbe gilt für Konstanten.

class A
  Z = 1
end

class B < A
  def z
    Z
  end
end

p B.new.z #=> 1

Sie können die Funktionalität einer Methode der Oberklasse überschreiben, indem Sie die Methode neu definieren.

class A
  def m
    1
  end
end

class B < A
  def m
    2
  end
end

p B.new.m #=> 2

Wenn Sie die Funktionalität der Oberklasse aus einer Methode aufrufen möchten, verwenden Sie super.

class A
  def m
    1
  end
end

class B < A
  def m
    2 + super
  end
end

p B.new.m #=> 3

Wenn super ohne Argumente verwendet wird, werden die Argumente verwendet, die an die Methode der Unterklasse übergeben wurden. Um keine Argumente an die Methode der Oberklasse zu senden, verwenden Sie super(). Um spezifische Argumente an die Methode der Oberklasse zu senden, übergeben Sie diese manuell, z. B. super(2).

super kann beliebig oft in der Methode der Unterklasse aufgerufen werden.

Singleton-Klassen

Die Singleton-Klasse (auch bekannt als Metaklasse oder Eigenklasse) eines Objekts ist eine Klasse, die Methoden nur für diese Instanz enthält. Sie können auf die Singleton-Klasse eines Objekts zugreifen, indem Sie class << object wie folgt verwenden.

class C
end

class << C
  # self is the singleton class here
end

Am häufigsten wird die Singleton-Klasse wie folgt aufgerufen.

class C
  class << self
    # ...
  end
end

Dies ermöglicht die Definition von Methoden und Attributen für eine Klasse (oder ein Modul), ohne def self.my_method schreiben zu müssen.

Da Sie die Singleton-Klasse jedes Objekts öffnen können, bedeutet dies, dass dieser Codeblock

o = Object.new

def o.my_method
  1 + 1
end

diesem Codeblock entspricht.

o = Object.new

class << o
  def my_method
    1 + 1
  end
end

Beide Objekte haben eine my_method, die 2 zurückgibt.