Ruby-Tutorial

Das Tutorial ist noch im Aufbau, das bedeutet, dass noch die Kapitel noch nicht optimal aufeinander abgestimmt sind. Wir arbeiten daran. Wenn Du dieses Tutorial dennoch lesen möchtest und etwas nicht verstehst, so kannst Du im Forum Fragen stellen, aber auch gerne Kritik üben, wo Du Dinge verbessert sehen möchtest. Deine Fragen und Anregungen helfen mit, dieses Tutorial zu verbessern.

siehe auch: Template, ToDo

Dieses Tutorial ist nicht für Programmier-Anfänger gedacht und setzt Grundkenntnisse in objektorientierter Programmierung voraus. Es richtet sich vor allem an Programmierer, die ihre Fähigkeiten erweitern wollen und keine Angst vor Neuem haben.

Warum Ruby?

Wozu brauchen wir eigentlich noch eine weitere Programmiersprache? C++, Java, C# und Co. sind doch völlig ausreichend und für alle denkbaren Gebiete einsetzbar! Auch Scriptsprachen gibt es genug: Perl und Python sind da bekannte Vertreter.

Das könnte man denken und käme mit dieser Einstellung auch wunderbar durch jeden Programmieralltag.

Zum Glück dachte einer nicht so: Yukihiro “matz” Matsumoto, der Erfinder von Ruby:

„Ich wollte eine Scriptsprache, die mächtiger als Perl und objektorientierter als Python ist.“
- Yukihiro Matsumoto

Matsumoto stellte sich eine Scriptsprache vor, die alle positiven Eigenschaften seiner Lieblingssprachen beinhaltet. Ruby wurde also von zahlreichen Sprachen beeinflusst. Nach der offiziellen Web-Präsenz von Ruby: http://www.ruby-lang.org/de/about sind das Perl, Smalltalk, Eiffel, Ada und Lisp.

Sein Ziel war es eine Programmiersprache zu entwickeln, die natürlich ist:

„Ruby wirkt simpel, aber ist innen sehr komplex, genau wie der menschliche Körper.“
- Yukihiro Matsumoto

Ob ihm dies gelungen ist, und ob Ruby sich unter den oben genannten Programmiersprachen etablieren kann, steht schon lange außer Frage.

Die Community spricht dieselbe eine Sprache: Ruby!

Installation

Debian/Ubuntu

Als root:

apt-get build-dep ruby
cd /opt
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p136.tar.gz
tar xvzf ruby-1.9.2-p136.tar.gz
cd ruby-1.9.2-p136
./configure
make
make install

Windows

Diesen Installer herunter laden und installieren:

http://rubyforge.org/frs/download.php/73722/rubyinstaller-1.9.2-p136.exe

Übersicht

Ruby ist eine objektorientierte Programmiersprache, die Aspekte aus verschiedenen Programmiersprachen beinhaltet. Alles ist ein Objekt – wirklich Alles!

Es gibt keine primitiven Typen wie in Java oder C++. Auch Zahlen und Strings sind Objekte. Das bedeutet, dass man auch auf Zahlen oder Strings Methoden definieren und aufrufen kann. Das wird am besten klar, wenn man folgendes Stück Code betrachtet:

5.times { puts "Ruby is sexy" }

Dieses Code erzeugt erwartungsgemäß folgende Ausgabe:

Ruby is sexy
Ruby is sexy
Ruby is sexy
Ruby is sexy
Ruby is sexy

Objektorientierung lässt sich am besten mit Beispielen aus der Wirklichkeit verdeutlichen. Das soll hier anhand der Kaffeemaschine geschehen.

Zuerst einmal: Die Analyse

Eine Kaffeemaschine kann – natürlich – Kaffee kochen. Sie hat die Eigenschaft Wasserfüllstand, denn ohne Wasser gibt’s keinen Kaffee. Wir gehen erst einmal davon aus, dass sie an einen endlosen Vorrat von frisch gemahlenen Bohnenkaffee angeschlossen ist und uneingeschränkten Zugriff darauf hat.

Gut aufpassen, jetzt folgen die Grundlagen:

Klassen, Objekte, Variablen

Was muss eine Kaffeemaschine können? Man kann sie einschalten, und die gewünschte Anzahl an Tassen Kaffee kochen. Entwickeln wir dazu erst folgendes HauptProgramm, so wie wir unsere Kaffeemaschine gerne bedienen würden:

Wir legen Ordner für jede Version der Kaffemaschine an. Wir starten mit dem Ordner m0.

main.rb:

require_relative 'm0/kaffee_maschine'
 
km = KaffeeMaschine.new
 
km.einschalten
km.koche_kaffee 3


Dann bilden wir das reale Konstrukt Kaffemaschine auf eine Ruby-Klasse ab.

m0/kaffee_maschine.rb:

class KaffeeMaschine
  def einschalten
    puts "Maschine ist an"
  end
 
  def koche_kaffee anzahl
    print "Koche Kaffee"
    3.times do
      print "."
      sleep 1
    end
    puts "gekocht"
 
    anzahl.times do
      print "c[_] "
    end
    puts "\nWohl bekommt's!"
  end
end


Wir starten das Programm, indem wir die Datei main.rb mit Hilfe des Ruby-Interpreters ausführen lässen:

ruby main.rb


Das Ergebnis ist:

Maschine ist an
Koche Kaffe.
..gekocht
c[_] c[_] c[_] 
Wohl bekommt's!

Klassen

Klassendefinitionen werden mit dem Schlüsselwort

class

eingeleitet.

Es ist Konvention Klassennamen mit einem Großbuchstaben zu beginnen. Bei zusammengesetzten Wörter wie Kaffee und Maschine wird jeder Anfangsbuchstabe nach CamelCase-Manier groß geschrieben. Bsp.:

class KaffeeMaschine 
end

Vererbung

Um die Vererbung in Ruby zu demonstrieren soll unsere Kaffeemaschine nun eine Spezialisierung einer allgemeinen Maschine werden.

Diese allgemeine Maschine soll lediglich die Ein-/Ausschaltfunktion besitzen. Alle speziellen Maschinen sollen die Methode einschalten erben.

Dazu erstellen wir zunächst ein neues Verzeichnis m1 eine neue Klasse Maschine:
m1/maschine.rb:

class Maschine
  def einschalten
    puts "Maschine ist an"
  end
end


Die Klasse KaffeeMaschine sieht nun entsprechend aus:
m1/kaffee_maschinen.rb:

require_relative "maschine"
 
class KaffeeMaschine < Maschine
  def koche_kaffee anzahl
    print "Koche Kaffe"
    3.times do
      print "."
      sleep 1
    end
    puts "gekocht"
 
    anzahl.times do
      print "c[_] "
    end
    puts "\nWohl bekommt's!"
  end
end


Die Datei main.rb muss leicht verändert werden:
main.rb:

require_relative 'm1/kaffee_maschine'
 
km = KaffeeMaschine.new
 
km.einschalten
km.koche_kaffee 3

Objekte

In Ruby hat jedes neu erzeugte Objekte eine eigene ID, die mit der Methode object_id heraus gefunden werden kann.

Ein Test dafür könnte so aussehen:

main.rb:

require_relative 'm1/kaffee_maschine'
 
km = KaffeeMaschine.new
puts "Object-ID: #{km.object_id}"


Die Ausgabe könnte z.B. folgendermaßen aussehen:

Object-ID: 6292400

Konstruktor

Der Konstruktor einer Klasse heisst in Ruby initialize und wird wie eine normale Methode definiert. In unserem Beispiel könnte man ihn dazu benutzen um den Füllstand der Maschine in einer Instanzvariable @fuellstand zu speichern und mit 0 zu initialisieren.

Sagen wir, eine Tasse fasst 300ml Wasser. Also ziehen wir für jede gekochte Tasse Wasser 0.3l Wasser vom Füllstand ab. Außerdem müssen wir eine Überprüfung einbauen, die eine Fehlermeldung ausgibt, falls nicht genügend Wasser im Gerät ist.

Es wird dafür eine neue Programmversion in Ordner m2 angelegt: m2/kaffee_maschine.rb:

require_relative 'maschine'
 
class KaffeeMaschine < Maschine
  def initialize
    @fuellstand = 0
  end
 
  def koche_kaffee anzahl
 
    # Füllstandprüfung
    print "Ueberpruefe Fuellstand..."
    if @fuellstand < 0.3 * anzahl
      print "Bitte Wasser nachfuellen!\n"
      return
    else
      print "ok\n"
    end
 
    print "Koche Kaffee"
    3.times do
      print "."
      sleep 1
    end
    puts "gekocht"
 
    @fuellstand -= 0.3 * anzahl
 
    anzahl.times do
      print "c[_] "
    end
    puts "\nWohl bekommt's!"
  end
end

Variablen

Damit kommen wir zum nächsten Abschnitt: Variablen. Es gibt 4 verschiedene Arten von Variablen mit unterschiedlichen Sichtbarkeitsbereichen in Ruby:

  1. Globale Variablen
  2. Klassenvariablen
  3. Instanzvariablen
  4. Lokale Variablen

Für jeden Variablentyp werden wie die Kaffeemaschine einen Schritt erweitern.

Globale Variablen

Um die Kaffeemaschine wartbar zu halten definieren wir das Volumen einer Tasse an einer zentralen Stelle. Zu diesem Zweck definieren wir einen globale Variable. Für jede gekochte Tasse Kaffee wird dieser Wert vom Füllstand abgezogen. Außerdem brauchen wir eine neue Methode um die Kaffeemaschine wieder mit Wasser zu befüllen.

Globale Variablen werden mit einem $ notiert.

m3/kaffee_maschine.rb:

require_relative 'maschine'
 
$kaffee_tasse_volumen = 0.3
 
class KaffeeMaschine < Maschine
  def initialize
    @fuellstand = 0
  end
 
  def nachfuellen anzahl
    print "Fuelle #{anzahl}l Wasser nach!\n"
    @fuellstand += anzahl
  end
 
  def koche_kaffee anzahl
 
    # Füllstandprüfung
    print "Ueberpruefe Fuellstand..."
    if @fuellstand < $kaffee_tasse_volumen * anzahl
      print "Bitte Wasser nachfuellen!\n"
      return
    else
      print "ok\n"
    end
 
    print "Koche Kaffee"
    3.times do
      print "."
      sleep 1
    end
    puts "gekocht"
 
    @fuellstand -= $kaffee_tasse_volumen * anzahl
 
    anzahl.times do
      print "c[_] "
    end
    puts "\nWohl bekommt's!"
  end
end

Um die neuen Funktionen testen zu können, wird die main.rb etwas abgeändert:

main.rb:

require_relative 'm3/kaffee_maschine'
 
km = KaffeeMaschine.new
 
km.einschalten
km.koche_kaffee 3
km.nachfuellen 1
km.koche_kaffee 3

Das sollte folgende Ausgabe erzeugen:

Maschine ist an
Ueberpruefe Fuellstand...Bitte Wasser nachfuellen!
Fuelle 1l Wasser nach!
Ueberpruefe Fuellstand...ok
Koche Kaffee...gekocht
c[_] c[_] c[_] 
Wohl bekommt's!

Klassenvariablen

Instanzvariablen

Lokale Variablen

Eingabe und Ausgabe

Symbole

Container

Arrays

Arrayfunktionen

Hashes

Hashfunktionen

Module

Module als Bibliotheken

Module als Mixins

Verschachtelte Module

Zufällige Auswahl aus einem Array

Blöcke

Blöcke mit Parametern