Die Programmiersprache Ruby (German Edition)
do |value| # Setter-Methode definieren
instance_variable_set variable, # Instanzvariable auf den
value # Argumentwert setzen
end
end
end
# Diese Methode funktioniert wie attributes, definiert aber stattdessen
# Klassenmethoden, indem sie attributes für die Eigenclass statt für
# self aufruft. Beachten Sie, dass die definierte Methode Klassen-
# instanzvariablen anstelle regulärer Klassenvariablen verwendet.
def class_attrs(hash)
eigenclass = class << self; self; end
eigenclass.class_eval { attributes(hash) }
end
# Beide Methoden sind privat.
private :attributes, :class_attrs
end
Listing 8.7 Attributmethoden mit define_method
8.11 Alias-Verkettung
Wie wir gesehen haben, beinhaltet die Metaprogrammierung in Ruby oft die dynamische Definition von Methoden. Genauso häufig kommt die dynamische Modifikation von Methoden vor. Methoden werden mithilfe eines Verfahrens modifiziert, das wir Alias-Chaining [ 30 ] (zu Deutsch etwa »Alias-Verkettung«) nennen können. Es funktioniert wie folgt:
Zuerst erzeugt man einen Alias für die Methode, die modifiziert werden soll. Dieser Alias stellt einen Namen für die unmodifizierte Version der Methode bereit.
Als Nächstes definiert man eine neue Version der Methode. Diese neue Version sollte die unmodifizierte Version durch den Alias aufrufen, aber bevor oder nachdem sie das tut, kann sie beliebige Funktionalität hinzufügen.
Beachten Sie, dass diese Schritte wiederholt angewendet werden können (solange jedes Mal ein anderer Alias verwendet wird), um eine Kette von Methoden und Aliasen zu erzeugen.
Dieser Abschnitt enthält drei Alias-Verkettung-Beispiele. Das erste führt das Alias-Verkettung statisch durch, das heißt, es verwendet die regulären Anweisungen
alias
und
def
. Das zweite und das dritte Beispiel sind dynamischer; sie führen mithilfe von
alias_method
,
define_method
und
class_eval
Alias-Verkettungs für Methoden mit beliebigem Namen durch.
8.11.1 Das Laden von Dateien und die Definition von Klassen überwachen
Listing 8.8 ist Code, der in einem Programm alle Dateien, die geladen werden, und alle Klassen, die definiert werden, überwacht. Wenn das Programm beendet wird, gibt es einen Bericht aus. Sie können diesen Code verwenden, um ein bestehendes Programm zu »instrumentieren«, damit Sie besser verstehen, was es tut. Eine Möglichkeit, diesen Code zu verwenden, besteht darin, diese Zeile am Beginn des Programms einzufügen:
require 'classtrace'
Eine einfachere Lösung besteht jedoch darin, die Option
-r
Ihres Ruby-Interpreters zu verwenden:
ruby -rclasstrace my_program.rb --traceout /tmp/trace
Die Option
-r
lädt die angegebene Bibliothek, bevor Ruby mit der Ausführung des Programms beginnt. Siehe „10.1 Den Ruby-Interpreter aufrufen“ für weitere Kommandozeilenargumente des Ruby-Interpreters.
Listing 8.8 verwendet statisches Alias-Verkettung, um alle Aufrufe der Methoden
Kernel.require
und
Kernel.load
zu überwachen. Das Programm definiert einen
Object.inherited
-Hook, um die Definition neuer Klassen zu überwachen. Und es verwendet
Kernel.at_exit
, um einen Codeblock auszuführen, wenn das Programm beendet wird. (Die in „5.7 BEGIN und END“ beschriebene Anweisung
END
würde hier ebenfalls funktionieren.)
# Wir definieren dieses Modul, das den globalen Zustand enthält,
# den wir benötigen, so dass wir den globalen Namensraum nicht
# mehr als nötig modifizieren.
module ClassTrace
# Dieses Array enthält unsere Liste von Dateien, die geladen
# werden, und von Klassen, die definiert werden. Jedes Element
# ist ein Unter-Array, das die definierte Klasse oder die geladene
# Datei enthält sowie das aktuelle Stack-Frame, wo sie definiert
# beziehungsweise geladen wird.
T = [] # Array, das die geladenen Dateien enthält
# Nun die Konstante OUT definieren, um anzugeben, wohin die
# Tracing-Ausgabe geht. Ihr Standardwert ist STDOUT, aber sie
# kann auch aus Kommandozeilenargumenten stammen.
if x = ARGV.index("--traceout") # Wenn Argument existiert
OUT = File.open(ARGV[x+1], "w") # Angegebene Datei öffnen
ARGV[x,2] = nil # Und Argumente entfernen
else
OUT = STDERR # Andernfalls Default STDERR
end
end
# Alias-Verkettung Schritt 1: Aliase für Originalmethoden definieren
alias original_require require
alias original_load load
# Aliasverkettung Schritt 2: Neue Versionen der Methoden definieren
def require(file)
ClassTrace::T << [file,caller[0]] # Merken, was wo geladen wurde
original_require(file) # Originalmethode
Weitere Kostenlose Bücher