Einblicke in Ruby

Christian Wenz <christian@wenz.org>
http://www.hauser-wenz.de/

Abstract
Die Skriptsprache Ruby hat ihre Wurzeln in Japan, ist aber vor einiger Zeit in die USA und auch nach Europa hinübergeschwappt. Die Sprache bietet eine ganze Reihe von innovativen und überraschenden Features, es lohnt sich also ein näherer Blick. Es ist natürlich klar, dass in einem halbstündigen Vortrag weder eine komplette Spracheinführung gegeben, noch eine vollständige übersicht über die Features von Ruby geboten werden kann. Stattdessen zeige ich Ihnen exemplarisch, was alles mit Ruby machbar ist. Ziel soll es also nicht sein, nach Ende des Vortrags den Raum als fortgeschrittener Ruby-Programmierer zu verlassen, vielmehr möchte ich Ihr Interesse für eine faszinierende neue Sprache wecken.

Warum eigentlich Ruby?

Während ein Großteil der populären Skriptsprachen amerikanischen Ursprungs sind, hat Ruby seine Wurzeln auf einem ganz anderen Kontinent. Der Erschaffer von Ruby, Yukihiro Matsumoto, lebt und arbeitet in Japan. So ist es auch zu erklären, dass Ruby insbesondere in Japan eine große Anwenderbasis hat; neueren Untersuchungen zufolge ist es dort sogar populärer als Pyton.

Die Sprache wurde im Jahr 1993 das erste Mal der Öffentlichkeit zugänglich gemacht und hat dann langsam aber sicher immer weitere Kreise gezogen. Dennoch dauerte es sieben weitere Jahre, bis 2000 Ruby auch in USA Fuß fassen konnte; der Sprung über den großen Teich nach Europa war und ist der nächste logische Schritt. Ruby-Vater Matsumoto bevorzugt es sowieso, mit seinem auch für Nicht-Asiaten einfach zu merken und auszusprechenden Namen "Matz" angesprochen zu werden. Matz übrigens ist nicht nur sehr sympathisch, er ist sich auch nicht zu schade sich in Usenet-Diskussionen selbst einzuschalten und Fragen persönlich zu beantworten (wenn nicht ein Mitglied aus der engagierten Ruby-Community schneller war). Im Vergleich zu manch anderer Programmiersprache ist das ein wahrer Segen.

Die ursprüngliche Intention von Ruby war, eine Alternative zu Perl zu entwickeln. Während Perl-Anhänger ihre Lieblingsskriptsprache als Schweizer Taschenmesser ansehen, bezeichnen Kritiker sie als "Schweizer Kettensäge". Ruby hat sich einige der Annehmlichkeiten von Perl geborgt und diese in die Sprachsyntax übernommen. Auch Python, das ja wie gesagt in Japan schon überholt wurde, diente als Meßlatte die es zu überwinden galt.

Was viele Anhänger von OOP (Objektorientierter Programmierung) freuen wird: Alles in Ruby ist ein Objekt. Bei aller berechtigten Kritik an dem übermäßigen Einsatz von OOP, die Vorteile liegen auf der Hand; ein sehr strukturiertes Programmieren ist möglich. Die daraus resultierenden Nachteile sind jedoch auch nahe liegend, insbesondere in Sachen Geschwindigkeit; mit Perl und Python hält Ruby zwar mit, gegen kompilierte Programme kommt Ruby jedoch nicht an.

Syntax

In Ruby werden Variablennamen ohne Dollarzeichen oder einen anderen Identifikator eingeleitet:

i = 0
x, y = 3, 4

Die letzte Anweisung ist eine parallele Zuweisung; x wird der Wert 3, y der Wert 4 zugewiesen.

Mit puts wird in der Konsole ein Text ausgegeben:

puts Math.sqrt(x*x+y*y)

Als Ausgabe erscheint 5.0.

Als Kontrollstruktur stehen while-Schleifen und if-Bedingungen zur Verfügung. Folgender Code gibt drei Mal das Wort "Linuxtag" aus:

i=0
while i<3
  puts "Linuxtag\n"
  i = i + 1
end

Und hier ein Beispiel für if:

os = "Win32"
if os == "Linux"
  puts "Linuxfreund"
else
  puts "Linuxfeind"
end

Codeblöcke und Objektorientierung

Codeblöcke werden entweder durch geschweifte Klammern abgegeben, oder es werden dem Sprachschatz von BASIC ähnliche Schlüsselwörter verwendet; ein end beendet den Block. Das sieht dann ungefähr so aus:

def LinuxBlock
  yield
end

Das Kommando yield führt ein anzugebendes Kommando aus. Der obige Block kann durch Angabe des Blocknamens (hinter def) aufgerufen werden; als Parameter wird innerhalb von runden Klammern ein oder mehrere Befehle angegeben. Mit puts beispielsweise wird ein beliebiger Text ausgegeben:

LinuxBlock
{ puts "Linuxtag 2002" }

Funktionsblöcke können auch die Funktion von den Funktions-Sprachelementen anderer Sprachen übernehmen, beispielsweise hier zur Berechnung der Fakultät einer Zahl:

def Fak(n)
  if n<0
    return "nix da!"
  else
    produkt = 1
    while n>1
      produkt = produkt * n
      n = n - 1
    end
    return produkt
  end
end

puts(Fak(6))

Wie zu sehen, wird mit return ein Rückgabewert angegeben und der Codeblock verlassen. Als Ergebnis wird wie erwartet 720 ausgegeben.

Die kurze übersicht über Sprachspezifika soll mit einem kleinen Ausflug in die objektorientierte Welt beendet werden. Klassen werden mit dem Schlüsselwort class eingeleitet, die einzelnen Klassenmethoden mit dem Schlüsselwort def bestimmt. Dabei kann eine Methode als Konstruktur eingesetzt werden, wenn sie den Namen initialize trägt. Eigenschaften der Klasse erhalten als Präfix den Klammeraffen - genauer gesagt wird damit ausgedrückt, dass die Variable nur lokal sichtbar ist:

class Steuer
  def initialize(steuersatz)
    @steuersatz = steuersatz
  end
  
  def brutto(betrag)
    return betrag * (1+@steuersatz)
  end
  
  def netto(betrag)
    return betrag / (1 + @steuersatz)
  end
end

st = Steuer.new(0.16)
puts st.brutto(125)
puts st.netto(290)

Die Ausgabewerte sind 145.0 und 250.

Ein- und Ausgabe

Ein Muss für jede fortschrittliche Programmiersprache sind entweder mächtige Funktionen zur Behandlung von Strings oder gleich reguläre Ausdrücke. Ruby bietet insbesondere im letzteren Bereich Funktionalität, hier ein kleines Beispiel:

name = gets
/(\w+), (\w+)/ =~ name
puts "Sie heißen #{$2} #{$1}!"

Bei der Eingabe "Matsumoto, Yukihiro" würde also "Sie heißen Yukihiro Matsumoto" ausgegeben würden.

Dateizugriff mit Ruby ist ebenfalls einfach. Durch File.new() wird eine Verbindung zu einer Datei geöffnet; als zweiter Parameter wird der Zugriffsmodus angegeben (beispielsweise "r" zum Lesen, "w" zum Schreiben):

zaehler = 0
handle = File.new("dateiname", "r")
handle.each_line do |zeile|
  zaehler = zaehler+1
  puts zaehler.to_s + ": " + zeile
end

Die angegebene Datei wird zeilenweise eingelesen und zusammen mit der Zeilennummer ausgegeben. Die Methode to_s() einer Zahl wandelt diese übrigens in einen String um.

Fortgeschrittene Programmierung

Ruby ist multithreadfähig, was an einem etwas fortgeschrittenen Beispiel erklärt werden soll. Im Modul net/http, das mit require "net/http" eingebunden werden kann, sind Funktionalitäten zum Laden von Daten über eine HTTP-Verbindung angegeben. Das folgende Beispiel holt nun mehrere Webseiten, und zwar parallel. Dies wird realisiert, indem verschiedene Threads eingerichtet werden:

require "net/http"

urls = ["http://www.microsoft.com", "http://www.linuxtag.org"]
threads = []

for el in urls
  threads.push(Thread.new(el)) { |url|
    stream = Net::HTTP.new(url, 80) #Port 80
    puts "Hole #{url} ..."
    status, html = h.get("/", nil)
    puts "#{url} empfangen!"
  }
end

threads.each { |t| t.join }

Wie in der Ausgabe zu sehen ist (zumindest in den meisten Fällen), wird zwar die Homepage von Microsoft als erstes abgerufen, zuerst empfangen wird jedoch die des Linuxtag e.V., entweder aufgrund der geringeren Dateigröße oder infolge einer besseren Anbindung.

Mit minimalen Anpassungen können Ruby-Programme auch als Shell-Skripten benutzt werden; der Sprachumfang des japanischen Newcomers übertrifft freilich den vieler Shells bei weitem. Durch das Einfügen des korrekten Pfads zum Ruby-Interpreter nach dem She-Bang wird das Skript direkt von Ruby ausgeführt, zumindest falls der Benutzer Ausführungsrechte für die Datei hat. Standard-Dateiendung für Ruby-Skripte ist .rb.

#!/usr/bin/env ruby
puts "Ich bin ein Shell-Skript"

Ruby dient aber nicht nur zum Konsolen-Modus. Mit der Tk-Komponente (der letzte Teil in Tcl/Tk) können Benutzeroberflächen gestaltet werden. Diese sind dann zwar plattformunabhängig, die Geschwindigkeit nativer Oberflächen kann damit jedoch natürlich nicht erreicht werden. Hier ein kleines Beispiel, das ein einfaches Fenster samt Schaltfläche erzeugt. Ein Klick auf die Schaltfläche beendet das Ruby-Skript.

require "tk"

form = TkRoot.new { title "GUI mit Ruby" }
zeile1 = TkFrame.new(form)
zeile2 = TkFrame.new(form)
ausgabe = TkLabel.new(zeile1) do
  text "Linuxtag 2002"
  pack
end
button = TkButton.new(zeile2) do
  text "Ende"
  command { proc exit }
  pack
end
zeile1.pack
zeile2.pack

Tk.mainloop

Das mit Ruby/Tk erzeugte Fenster

Außer Tk können noch andere Graphical Toolkits eingesetzt werden, beispielsweise Gtk.

Ruby goes WWW

Zu guter Letzt wollen wir noch einen kurzen Blick auf den Web-Einsatz von Ruby werfen. Im CGI-Modus lässt sich beispielsweise die Dateiendung .rb mit dem Ruby-Interpreter verknüpfen. Sie müssen jedoch den kompletten HTTP-Header selbst erzeugen, also insbesondere den MIME-Typ von Hand erzeugen. Hier ein minimales Beispiel:

#!/usr/bin/env ruby

print "Content-type: text/html\n\n"

print "<html>"
print "<title>Ruby goes WWW</title>"
print "<body>Ruby goes WWW!</body>"
print "</html>"

Auf dieser Basis können nun etwas komplexere Skripten erstellt werden. Beispielsweise können Formulardaten eingelesen und ausgewertet werden. Dazu geben wir ein einfaches HTML-Formular aus, das per GET verschickt wird:

#!/usr/bin/env ruby

print "Content-type: text/html\n\n"

print "<html>"
print "<title>Ruby goes WWW</title>"
print "<body><form>"
print "<input type=\"text\" name=\"Name\" />"
print "<input type=\"submit\" />"
if ENV["QUERY_STRING"]
  ENV["QUERY_STRING"].grep(/(.*)=(.*)/)
  if $2
    print "<br />" + $2.to_s
  end
end
print "</body></html>"

Die Querystring (also alles, was hin der einem Fragezeichen an den URL angehängt worden ist, wird in einen Teil links von einem Gleichheitszeichen und rechts von einem Gleichheitszeichen getrennt. Rechts davon steht der Wert, der in das Textfeld eingegeben worden ist.

Der im Formular eingegebene Wert wird ausgelesen

Wem das ganze zu umständlich ist, wird eventuell mit dem CGI-Modul von Ruby glücklich, das in etwa Lincoln Steins CGI.pm für Perl entspricht. Damit lassen sich sehr bequem HTML-Seiten erstellen. Nachfolgendes Listing entspricht in etwa dem Vorgängerskript, ist jedoch deutlich bequemer:

#!/usr/bin/env ruby

require "CGI"

ausgabe = CGI.new("html3")

ausgabe.out {
  ausgabe.html {
    ausgabe.head {
      ausgabe.title { "Ruby goes WWW" }
    } +
    ausgabe.body {
      ausgabe.form {
        ausgabe.text_field("Name") +
        ausgabe.submit("Abschicken")
      } +
      ausgabe.br + ausgabe["Name"].first.to_s
    }
  }
}

Fazit

In diesem Vortrag konnten die Möglichkeiten von Ruby nur angeschnitten werden; die umfangreiche Modulbibliothek bietet weitaus mehr. Die Frage ist, welche Chancen Ruby in Zukunft hat. Zumindest im Webbereich haben die etablierten Konkurrenten, allen voran PHP, noch die Nase vorn; einige der Funktionalitäten sind für Ruby noch nicht implementiert worden. Das soll Sie aber nicht davon abhalten, selbst erste Schritte in der Sprache zu unternehmen. Wer weiß, vielleicht erstellen Sie in Zukunft all Ihre Shellskripten in Ruby, oder Sie setzen Matz' Sprache in der Tat für Webapplikationen ein. Das erklärte Ziel des Vortrags, Interesse für die Materie zu wecken, ist hoffentlich gelungen.

Quellen


Über den Autor:
Christian Wenz ist Autor von über zwei Dutzend Computerbüchern mit dem Schwerpunkt Webprogrammierung, Verfasser von Artikeln in Fachzeitschriften (u.a. c't, Internet World) sowie regelmäßig Speaker auf Konferenzen. Zu seinen erfolgreichsten Publikationen gehören unter anderem das "Web Publishing Kompendium" (Markt + Technik) sowie diverse Veröffentlichungen zu PHP (Addison-Wesley, Markt+Technik).