
Rake ist ein Build-Tool für Ruby, vergleichbar zu Ant und Phing. Es gibt aber einen entscheidenden Unterschied zwischen Rake und den anderen. Im Gegensatz zu den anderen Tools arbeitet Rake nicht mit einer externen domänenspezifischen Sprache (DSL) wie beispielweise XML für Ant. Stattdessen werden alle Aufgaben in reinem Ruby verfasst. Das gibt dir die volle Flexibilität und du kannst so von einigen guten Ruby-Funktionen profitieren.
Was sind Build-Tools?
Hast du je versucht Software von der Quelle in das Linux oder Unix zu installieren? Dann hast du wahrscheinlich schon Bekanntschaft mit make gemacht. Der Installationsprozess sieht normalerweise ähnlich aus. Zuerst wechselst du vom aktuellen Ordner zum Ordner mit dem unkomprimierten Quellcode und dann gibst du den folgenden Befehl ein:
./configure
make
make install
Die zweite und dritte Linie sind einfach nur make-Programmaufrufe. Nach dem Start sucht make zuerst nach dem Makefile. Diese Datei enthält Informationen über die Quelldatei und deren Abhängigkeiten untereinander. make sortiert die Abhängigkeiten topologisch und versucht diese in der angemessenen Reihenfolge zu lösen. Also im Wesentlichen bedeutet das: Softwareentwickler legen Abhängigkeiten fest und das Build-Tool ist dann für die Verarbeitung verantwortlich. make spart auch Zeit. Wenn sich der Quellcode seit der letzten Zusammenstellung nicht verändert hat, wird dieser nicht erneut verarbeitet, da das reine Zeitverschwendung wäre.
Build-Tools können nicht für die Zusammenstellung von Quellcode verwendet werden, aber wurden ursprünglich dafür entwickelt. Im Allgemeinen werden Build-Tools genutzt um leidige und sich wiederholende Aufgaben zu automatisieren.
Was unterscheidet Rake von anderen Build-Tools?
Wie ich bereits erwähnt habe, gibt es einen wesentlichen Unterschied zwischen Rake und anderen Build-Tools. Anstatt einen Makefile für make oder build.xml für Ant und Phing zu schreiben, gibst du einfach einige Zeilen in Ruby ein. Du benötigst keine neue komplizierte Build-Tool-Syntax, die du nicht mehr verwenden kannst, wenn du das Build-Tool mal wechselst.
Aber genug der Theorie – nur Übung macht den Meister!
Wie wird Rake installiert?
Die Installation von Rake ist so einfach wie 1, 2, 3 – vorausgesetzt du hast bereits RubyGems auf deinem System laufen. Die Installation von Rake erfolgt mit den folgenden Befehlen:
gem install rake
Meistens benötigst du dafür Root-Rechte, du musst entweder zum root wechseln oder den angegebenen Befehlen „sudo“ voranstellen:sudo gem install rake
Wenn RakeGems installiert ist, kannst du deine erste einfache Rake-Aufgabe schreiben.Erste Rake-Aufgabe
Der einfachste Weg eine Rake-Aufgabe zu definieren ist der folgende Ruby-Code:
task :default do
puts "Hello World!"
end
Rake-Aufgaben sollten sich immer in einer Datei mit dem Namen rakefile, Rakefile, rakefile.rb oder Rakefile.rb befinden. Die ersten beiden Namen werden dabei am häufigsten verwendet.Wenn die Rake-Datei gespeichert ist, solltest du vom aktuellen Verzeichnis zum Ordner wechseln, der diese Datei enthält und den folgenden Rake-Befehl ausführen:
$ rake
(in /home/username/ruby/rake_examples)
Hello World!
Und deine erste Rake-Aufgabe läuft!
So, was ist genau passiert? Nun, wenn man Rake eine Rake-Datei vorgibt, sucht es nach Aufgaben, die einfache Aufrufe von Aufgaben sind. Eine Rake-Datei kann viele Aufgaben enthalten. Wenn Rake von der Kommandozeile ausgeführt wird, kannst du festlegen, welche Aufgaben ausgeführt werden sollen. Sobald keine Aufgabe ausgewählt wurde, sucht Rake nach der Standardaufgabe. Aus diesem Grund funktionierte der obere Rake-Aufruf ohne zusätzliche Parameter.
Was passiert, wenn man mehrere Aufgaben in eine Rake-Datei packt:
task :ring do
puts "Bell is ringing."
end
task :enter do
puts "Entering home!"
end
Wenn du nun versuchst Rake ohne Parameter zu starten, wir eine Fehlermeldung angezeigt:$ rake
(in /home/username/ruby/rake_examples)
rake aborted!
Don't know how to build task 'default'
(See full trace by running task with --trace)
Da in dieser Rake-Datei keine Standardaufgabe definiert ist, musst du die Aufgabe beim Start eindeutig festlegen:$ rake enter
(in /home/username/rake_examples)
Entering home!
$ rake ring
(in /home/username/rake_examples)
Bell is ringing.
Auch wenn die neue Rake-Datei funktionieren zu scheint, irgendetwas stimmt nicht. Du kannst nicht einfach die Tür deines Nachbarn öffnen ohne vorher zu klingeln (zumindest gehört sich das nicht). Und hier kommen die Abhängigkeiten ins Spiel.Abhängigkeiten festlegen
Hier eine leicht veränderte Rake-Datei:
task :ring do
puts "Bell is ringing."
end
task :enter => :ring do
puts "Entering home!"
end
Wenn wir nun ins Haus gehen wollen, haben wir zuerst geklingelt:$ rake enter
(in /home/username/ruby/rake_examples)
Bell is ringing.
Entering home!
Nun ist Rake dafür verantwortlich, die Klingel zu betätigen bevor du ins Haus gehst. Du kannst viel komplexere Abhängigkeiten definieren und überlässt dann Rake die Lösung dieser. Du sagst einfach was du willst und das Build-Tool erledigt die Schwerarbeit.Die gute Nachricht: Abhängigkeiten können nicht nur während der Definition der Aufgaben, sondern abhängig von Laufzeitzuständen auch später festgelegt werden. Schliesslich arbeitest du mit Ruby und nicht mit einer statischen XML-Datei. Schon vergessen?
Um den gleichen Effekt mit läutender Klingel zu erreichen, kannst du folgendes eingeben:
task :ring do
puts "Bell is ringing."
end
task :enter do
puts "Entering home!"
end
task :enter => :ring
Der Effekt ist derselbe.Aufgaben beschreiben
Jede Aufgabe, die du schreibst, kannst du mit einigen einfachen Worten beschreiben. Beschreibungen dienen nicht nur als Kommentare, sondern werden auch in der Liste der verfügbaren Aufgaben angezeigt. So fügst du Beschreibungen hinzu:
desc 'Ring the bell'
task :ring do
puts "Bell is ringing."
end
desc 'Enter home'
task :enter => :ring do
puts "Entering home!"
end
Für eine Auflistung aller verfügbaren Aufgaben, starte Rake einfach mit angehängtem -T oder --tasks:$ rake -T
(in /home/username/ruby/rake_examples)
rake enter # Enter home
rake ring # Ring the bell
Ich persönlich bevorzuge die kürzere Variante (-T), da spart man ein paar Tastenanschläge, aber du darfst dich natürlich anders entscheiden.Dateiaufgaben
Wie bereits vorgenannt dienen Build-Tools der Vereinfachung von Zusammenstellung von Projekten. Zwischen bestimmten Dateien und Build-Tools, die diese unterstützen, existieren viele Abhängigkeiten. Das gleiche gilt für Rake. Du kannst einen speziellen Aufgabentyp definieren – die Dateiaufgabe:
file 'products.sql' => 'products.xml' do
# build SQL INSERT clause and save it in products.sql file,
# basing on products.xml datafile
end
Eine Dateiaufgabe unterscheidet sich – im Allgemeinen — nicht von einer normalen Aufgabe. Der Unterschied: Wenn die Eingabedatei (in diesem Fall products.xml) nicht vorhanden ist, wird die Aufgabe nicht ausgeführt. Sie wird ebenfalls nicht ausgeführt, wenn die Ausgabedatei (products.sql) nicht älter als die Eingabedatei ist. Wenn du dieses Verhalten nicht akzeptierst, z.B. benötigst du die Ausgabedatei jedes Mal, wenn die Aufgabe ausgeführt wird, dann definierst du am besten eine normale Aufgabe.FileUtils
Rake enthält das FileUtils-Modul, das dir einen Reihe von Unix-ähnlichen Dateifunktionen einschliesslich mkdir, rmdir, cp, mv, chmod und touch zur Verfügung stellt.
Da das FileUtils-Modul bereits integriert ist, kannst du die Methoden direkt ohne den Scope-Operator nutzen:
task :manipulate_files do
mkdir 'new_dir'
mv 'new_dir', 'test'
chmod 0777, 'test'
touch 'test/wrobel.txt'
rm_rf 'test'
end
Wenn du dich bereits mit Linux/Unix-Befehlen auskennst, erlernst du den Umgang mit dem FileUtils-Modul im Handumdrehen.Apropos, kannst du dich noch an das Verhalten der Dateiaufgabe erinnern? Wenn die Ausgabedatei nicht älter als die Eingabedatei ist (d.h. Datei ist aktuell), dann wird die Dateiaufgabe nicht ausgeführt. Wenn du diese Überprüfung in eine normale Aufgabe integrieren willst, kannst du dafür eine FileUtils-Funktion mit dem (überaschenden!) Namen uptodate? nutzen.
task :check do
# ...
unless uptodate?(output_file, input_file)
# regenerate output_file
end
end
FileList
Stell dir eine Dateiaufgabe vor, bei der viele Eingabedateien kombiniert werden und das Ergebnis nur eine Ausgabedatei ist. Die offensichtlichste Lösung dafür ist wohl die Definition von Dateiabhängigkeiten:
one_file_to_rule_them_all = 'database.sql'
tables_sql = ['orders.sql', 'payments.sql', 'categories.sql']
file one_file_to_rule_them_all => tables_sql
Funktioniert. Was aber, wenn du eine neue Eingabe-SQL-Datei erstellst? Nun, dann darfst du nicht vergessen diese manuell zu den tables_sql-Variablen hinzuzufügen.Ich weiss nicht, wie es dir geht, aber ich bin ein wenig verwirrt. Hatte ich nicht gesagt, dass Build-Tools uns langweilige und wiederholende Aufgaben abnehmen. Und das manuelle Einfügen von Dateien in die Liste IST langweilig!
Glücklicherweise wussten das auch die Macher von Rake. Deswegen gibt es genau für solche Situationen die FileList-Funktion. Und so wird sie eingesetzt:
one_file_to_rule_them_all = 'database.sql'
FileList['*.sql'].each {|table| file one_file_to_rule_them_all => table}
Also kein langweiliges Hinzufügen jeder einzelnen SQL-Datei zu einer Liste. Rake erledigt das für dich.Zusammenfassung
Viele weitere Rake-Funktionen, einschliesslich clean, clobber, rdoc und gem tasks, pathmap, rules und namespaces sind in diesem Tutorial nicht erwähnt. Auf jeden Fall werde ich darauf zu einem späteren Zeitpunkt eingehen.
Ich hoffe dieser Artikel gibt dir einen ersten Überblick und hat dir Lust auf Rake gemacht. Rake hat sich bereits bewährt und ist in der Ruby-Welt weit verbreitet, somit ein Muss. Viel Spass mit deinen eigenen Rake-Experimenten!


Kommentare
Endlich mal ein Beitrag mit Niveau und zu einem interessanten Thema! Für mich persönlich zwar nichts wirklich Neues, aber viele Ruby Anfänger werden bestimmt daraus viel entnehmen können.
Ich wünschte mir es würde hier auf pc.de mehr solcher Artikel rund um Programmierung geben…
Kommentieren