Die Programmiersprache Ruby (German Edition)
Als String ausgeben
end
@out << "#{tagname}>" # Das Tag schließen
else
# Andernfalls ist dies ein leeres Tag, also einfach schließen.
@out << '/>'
end
nil # Tags geben sich selbst aus, geben also nichts zurück.
end
# Der Code unten ist das, was dies von einer gewöhnlichen Klasse in
# eine DSL umwandelt. Erstens: Jede unbekannte Methode wird als
# Name eines Tags behandelt.
alias method_missing tag
# Zweitens: einen Block in einer neuen Instanz der Klasse ausführen
def self.generate(out, &block)
XML.new(out).instance_eval(&block)
end
end
Listing 8.11 Eine einfache DSL zur Erzeugung einer XML-Ausgabe
8.12.2 Validierte XML-Ausgabe durch Methodenerzeugung
Die Klasse
XML
in Listing 8.11 ist nützlich zur Ausgabe von wohlgeformtem XML, aber sie führt keine Fehlerprüfung durch, um sicherzustellen, dass die Ausgabe gemäß einer bestimmten XML-Grammatik valide ist. Im nächsten Beispiel, Listing 8.12 , fügen wir ein wenig einfache Fehlerüberprüfung hinzu (wenn auch nicht annähernd genug, um vollständige Validität zu gewährleisten – das würde ein erheblich längeres Beispiel erfordern). Dieses Beispiel besteht eigentlich aus zwei DSLs in einer. Die erste ist eine DSL zur Definition einer XML-Grammatik: ein Satz von Tags und die erlaubten Attribute für jedes Tag.
class HTML Form < XMLGrammar
element :form, :action => REQ,
:method => "GET",
:enctype => "application/x-www-form-urlencoded",
:name => OPT
element :input, :type => "text", :name => OPT, :value => OPT,
:maxlength => OPT, :size => OPT, :src => OPT,
:checked => BOOL, :disabled => BOOL, :readonly => BOOL
element :textarea, :rows => REQ, :cols => REQ, :name => OPT,
:disabled => BOOL, :readonly => BOOL
element :button, :name => OPT, :value => OPT,
:type => "submit", :disabled => OPT
end
Die erste DSL wird durch die Klassenmethode
XMLGrammar.element
definiert. Sie verwenden Sie, indem Sie eine Unterklasse von
XMLGrammar
bilden. Die Methode
element
erwartet den Namen eines Tags als erstes Argument und einen Hash gültiger Attribute als zweites Argument. Die Schlüssel des Hash sind Attributnamen. Diesen Namen werden Standardwerte des Attributs, die Konstante
REQ
für benötigte Attribute oder die Konstante
OPT
für optionale Attribute zugeordnet. Ein Aufruf von
element
erzeugt in der Unterklasse, die Sie erzeugen, eine Methode mit dem angegebenen Namen.
Die Unterklasse von
XMLGrammar
, die Sie definieren, ist die zweite DSL, und Sie können sie verwenden, um eine XML-Ausgabe zu generieren, die gemäß den angegebenen Regeln gültig ist. Die Klasse
XMLGrammar
besitzt keine
method_missing
-Methode, so dass sie nicht erlaubt, ein Tag zu verwenden, das kein Teil der Grammatik ist. Und die Methode
tag
zur Ausgabe von Tags führt Fehlerüberprüfungen für Ihre Attribute durch. Verwenden Sie die erzeugte Grammatik-Unterklasse genau wie die Klasse
XML
in Listing 8.11 :
HTMLForm.generate(STDOUT) do
comment "Ein einfaches HTML-Formular"
form :name => "Registrierung",
:action => "http://www.example.com/register.cgi" do
content "Name:"
input :name => "name"
content "Adresse:"
textarea :name => "address", :rows=>6, :cols=>40 do
"Bitte hier Ihre E-Mail-Adresse eingeben"
end
button { "Absenden" }
end
end
Listing 8.12 zeigt die Implementierung der Klasse
XMLGrammar
.
class XMLGrammar
# Eine Instanz dieser Klasse erzeugen, unter Angabe eines Stream
# oder eines Objekts für die Ausgabe. Das kann jedes Objekt sein,
# das auf <<(String) reagiert.
def initialize(out)
@out = out # Merken, wohin wir unsere Ausgabe senden
end
# Den Block in einer Instanz aufrufen, die in den angegebenen
# Ausgabedatenstrom schreibt
def self.generate(out, &block)
new(out).instance_eval(&block)
end
# Ein erlaubtes Element (oder Tag) in der Grammatik definieren.
# Diese Klassenmethode ist die Grammatik-Spezifikations-DSL
# und definiert die Methoden, die die XML-Ausgabe-DSL
# konstituieren.
def self.element(tagname, attributes={})
@allowed_attributes ||= {}
@allowed_attributes[tagname] = attributes
class_eval %Q{
def #{tagname}(attributes={}, &block)
tag(:#{tagname},attributes,&block)
end
}
end
# Dies sind Konstanten zur Definition von Attributwerten.
OPT = :opt # für optionale Attribute
REQ = :req # für benötigte Attribute
BOOL = :bool # für Attribute, deren Wert ihr eigener Name ist
def self.allowed_attributes
@allowed_attributes
end
# Objekt als CDATA ausgeben, nil zurückgeben
def
Weitere Kostenlose Bücher