Ruby (Programmiersprache)

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche
Ruby
Logo
Offizielles Logo
Basisdaten
Paradigmen: multiparadigmatisch, tief integrierte Objektorientierung
Erscheinungsjahr: 1995
Entwickler: Yukihiro Matsumoto und andere
Aktuelle Version: 2.5.1  (28. März 2018[1])
Typisierung: stark, dynamisch („Duck-Typing“)
Wichtige Implementierungen: MRI / CRuby (YARV), JRuby, Rubinius (siehe unten)
Beeinflusst von: Smalltalk, Perl, Python, Lisp, CLU, Eiffel, Ada, Dylan, JavaScript
Beeinflusste: Crystal, D,[2] Elixir, Groovy, Rust, Swift
Betriebssystem: plattformübergreifend
Lizenz: 2-clause BSD und eigene (siehe Nutzungsbedingungen)
ruby-lang.org

Ruby (englisch für Rubin) ist eine höhere Programmiersprache, die Mitte der 1990er Jahre vom Japaner Yukihiro Matsumoto entworfen wurde.

Ruby ist objektorientiert, unterstützt aber mehrere weitere Programmierparadigmen (unter anderem prozedurale und funktionale Programmierung sowie Nebenläufigkeit), bietet dynamische Typisierung, Reflexion und automatische Speicherbereinigung. Ein Programm in Ruby wird zur Laufzeit interpretiert. 2012 wurde die Ruby-Spezifikation als internationale Norm ISO/IEC 30170 standardisiert.[3]

Entstehung und Geschichte[Bearbeiten | Quelltext bearbeiten]

Schöpfer von Ruby, Yukihiro Matsumoto

Yukihiro „Matz“ Matsumoto begann 1993 an einer eigenen Sprache zu arbeiten und gab am 21. Dezember 1995 die erste Version von Ruby, 0.95, frei.[4] Den Namen, hergeleitet vom Edelstein Rubin, wählte er als Anspielung auf die Programmiersprache Perl.[5] Matsumotos Ziel bei der Erschaffung der neuen Sprache war eine Synthese aus Elementen der von ihm geschätzten Programmiersprachen Perl, Smalltalk, Eiffel, Ada und Lisp zu bilden und funktionale und imperative Programmierparadigmen in Einklang zu bringen. Darüber hinaus sollte die Sprache objektorientierter als Python und zugleich mächtiger als Perl sein. Auch Flexibilität, Ausdrucksstärke und Einfachheit spielten bereits eine gewichtige Rolle.[6]

Besonders wichtig, neben den technischen Eigenschaften, war Matsumoto an Ruby auch die emotionale Wirkung auf Anwender, Mitentwickler und sich selbst. Matsumotos Auffassung dazu ist folgende:

„Natürlich unterscheidet sich Ruby von Perl oder Python an vielen Stellen, das ist schließlich Rubys Daseinsberechtigung. Rubys wesentliches Ziel ist „Freude“. Meines Wissens gibt es keine andere Sprache, die sich so sehr auf die Freude konzentriert. Rubys eigentliches Ziel ist es zu erfreuen – Sprachdesigner, Anwender, Sprachlerner, jeden. Freude allein ist jedoch nicht alles. Ruby bietet auch viele Verwendungsmöglichkeiten. Könnte man sie nicht einsetzen, würde sie doch auch keine Freude bereiten.“

Yukihiro Matsumoto: Vorwort des Sprachschöpfers in „Rubyプログラミング入門“ (übersetzt)

In Japan erlangte Ruby nach Erscheinen rasch an Bekanntheit. Im Westen verhalfen der Programmiersprache das Buch Programming Ruby (2000) (bekannt als Pickaxe book als Anspielung auf die Illustration auf dem Buchdeckel) und das Webframework Ruby on Rails (2004) zum Durchbruch. In den folgenden Jahren mehrte sich auch die englisch- und deutschsprachige Dokumentation sowie Literatur. Die Verkehrssprache der Kern-Entwickler wechselte von Japanisch zu Englisch. Heute wird die Sprache als Open-Source-Projekt weitergepflegt und ist Gegenstand diverser Publikationen und Kurse. Veröffentlichungen mit neuen Funktionen geschehen üblicherweise im Jahresrhythmus zur Weihnachtszeit.

Ein Meilenstein in der Entwicklung von Ruby war Version 1.9 im Jahr 2007, welche mit vielen Änderungen ziemlich inkompatibel zu den Vorgängerversionen wurde. Der Interpreter wurde komplett neu geschrieben, womit Rubyprogramme wesentlich schneller wurden. Zeichenketten bekamen separate Zeichenkodierungen, Variablen in Blöcken wurden block-lokal, IPv6-Unterstützung und einige syntaktische Erleichterungen fanden Einzug.

Mit Ruby 2.0 wurden Schlüsselwortparameter eingeführt (bspw.: 0.step(by: 5, to: 20)), das Vorhängen (prepend) von Modulen und Bedarfsauswertung (lazy-evaluation) von Iteratoren eingeführt. Außerdem wurde die Standardzeichenkodierung auf UTF-8 festgelegt. Ruby 2.1 führte Refinements (eine Methode um Überschreibungen von Klassen lokal zu beschränken) ein. In Ruby 2.2 wurde der Garbage-Collector durch einen Inkrementellen ersetzt. Mit Ruby 2.3 ist es möglich alle Zeichenketten automatisch schreibgeschützt zu erstellen und ein neuer Operator &. (Safe-Navigation-Operator) zum Umgehen von Nil-Überprüfungen wurde eingeführt. In Ruby 2.4 wurden die Klassen Fixnum und Bignum vereinheitlicht. Seit Ruby 2.5 können Exceptions in do-end-Blöcken ohne separate Unterblöcke gefangen werden. Jede neue Ruby-Version ab 1.9 brachte zudem Geschwindigkeitsoptimierungen verschiedenen Ausmaßes mit sich. Zusätzlich erweiterte jede Version die Standardbibliothek um nützliche Methoden. Es wurde stets auf Abwärtskompatibilität geachtet, allerdings nicht immer zu hundert Prozent erreicht.

Merkmale[Bearbeiten | Quelltext bearbeiten]

Alles ist ein Objekt[Bearbeiten | Quelltext bearbeiten]

Ruby ist eine Programmiersprache die, obwohl sie auch viele andere Programmierparadigmen unterstützt, von Grund auf objektorientiert ist. Das bedeutet konkret, dass in Ruby ausnahmslos jeder Wert ein Objekt und jede Funktion eine Methode ist (d. h. einer Klasse zugeordnet ist). Ausnahmen für primitive Datentypen, wie in vielen anderen objektorientierten Programmiersprachen, gibt es nicht. Auch Klassen sind Objekte. Objekte speichern Instanzvariablen und haben eine Klasse, Klassen sind Objekte, die Methoden speichern und eine Vererbungshierarchie besitzen. Auf Instanzvariablen kann ausschließlich über Methoden zugegriffen werden.

class TestKlasse
end
test_objekt = TestKlasse.new

puts 1.class            # => Integer
puts "text".class       # => String
puts test_object.class  # => TestKlasse
puts TestKlasse.class   # => Class
puts Class.class        # => Class

Werden in Ruby „Funktionen“ definiert, so sind das in Wirklichkeit Methoden, die dem Objekt Object hinzugefügt werden. In Ruby erben alle Objekte implizit von Object, weswegen so definierte „Funktionen“ in jedem Objekt und damit allgegenwärtig verfügbar sind. Allerdings werden diese Funktion als private markiert, das heißt, sie können nicht von außen auf einem Objekt aufgerufen werden.

# Definiere „Funktion“
def meine_funktion
  puts "Hier bin ich"
end

# Kann in andern Objekten wie eine Funktion benutzt werden
class ABC
  def gib_meine_funktion_aus
    meine_funktion
  end
end
mein_abc = ABC.new
mein_abc.gib_meine_funktion_aus # => Hier bin ich
mein_abc.meine_funktion # => Fehlermeldung, private method `meine_funktion' called

Da in Ruby Objekte dynamisch sind, kann man mit Ruby auch prototypbasiert programmieren. Das bedeutet grob, dass Objekte eigene Methoden haben können (außerhalb der von der Klasse vorgegebenen) und kopiert und verändert werden können (da nur Klassen Methoden beinhalten können, wird im Hintergrund eine versteckte neue Klasse nur für das eine Objekt angelegt).

auto1 = Object.new
def auto1.beschleunigen
  puts "brumm brumm brumm"
end

auto1.beschleunigen # => "brumm brumm brumm"

auto2 = auto1.clone
def auto2.bremsen
  puts "quietsch"
end

auto2.beschleunigen # => "brumm brumm brumm"
auto2.bremsen       # => "quietsch"

Blöcke[Bearbeiten | Quelltext bearbeiten]

In den meisten höheren Programmiersprachen ist es möglich, Funktionen in irgendeiner Form zusätzlich als Parameter Logik zu übergeben, sei es durch First-Class-Funktionen oder First-Class-Objekte (deren Methoden dann die Logik bereitstellen). Das ist in Ruby nicht anders, allerdings hat Ruby den Spezialfall, dass genau eine Funktion übergeben wird, syntaktisch (und auch von der Rechenleistung) stark optimiert. Dieser Spezialfall wird Block genannt, gemeint als ein Block Programmierlogik, den die Funktion zu benutzen hat.

Blöcke werden Funktionen als separate Parameter übergeben und folgen als letztes Argument, eingegrenzt durch geschwungene Klammern oder die Schlüsselworte do und end. Im Folgenden wird die Methode times des Objekts 10 aufgerufen und ein Block übergeben. Beide Aufrufe sind identisch.

10.times {
  puts "Hallo Welt!"
}
10.times do
  puts "Hallo Welt!"
end

Zusätzlich können Blöcken Parameter übergeben werden und sie haben auch einen Rückgabewert. Zum Aufrufen des übergebenen Blocks wird innerhalb der aufgerufenen Methode das Schlüsselwort yield verwendet. Werden yield Parameter angegeben, so werden diese dem Block übergeben, der sie zu Beginn als lokale Variablen deklarieren oder ignorieren kann. Blöcke (ebenso wie Methoden) geben automatisch den letzten Ausdruck des Blockes als Rückgabewert zurück (mittels break und next kann aber auch an anderen Stellen zurückgesprungen werden).

def methode_die_block_aufruft(übergebener_parameter)
  eigene_variable = "Hallo"
  rückgabe = yield eigene_variable, übergebener_parameter
  if rückgabe == "ok"
    puts "☺"
  end
end

# Aufruf
methode_die_block_aufruft("aus Ruby") do |p1,p2| # die Blockargumente werden innerhalb || in block-lokale Variablen umgewandelt
  puts p1 + " " + p2 + "!"
  "ok"
end
# Zuerst wird im Block „Hallo aus Ruby!“ ausgegeben,
# dann in der Methode ☺, da der Rückgabewert „ok“ war

Blöcke können auch in Funktionsobjekte umgewandelt werden. Wird innerhalb der Parameterliste einer Methode vor den letzten Parameter ein & geschrieben, wandelt Ruby den übergebenen Block in ein Proc (ein Funktions-Objekt) um. Alternativ können Procs auch manuell durch die Schlüsselworte proc, lambda und -> angelegt werden. Aufgerufen werde diese Objekte durch die Methoden call, [] oder .(). Da Ruby runde Klammern lediglich zum Gruppieren nutzt, können Procs nicht (wie in anderen Programmiersprachen üblich) mit proc_name() aufgerufen werden.

def mache_block_zu_proc &block
  block # ist der Rückgabewert, da letzter Ausdruck
end
a = mache_block_zu_proc{|a,b| a + b}
b = proc {|a,b| a - b} # ein return innerhalb des Blocks verlässt die beinhaltende Methode, break nur den Block
c = lambda {|a,b| a * b} # wie proc, aber return innerhalb des lamdas verlässt nur diesen Block
d = -> (a,b) {a / b} # neue Schreibweise für lambda, runde Klammern sind optional
a.call(1,2) # => 3
b[1,2]      # => -1
c.(1,2)     # => 2

# Beispiel für funktionale Programmierung
e = d.curry.(8) # neues Proc das den (oder die) ersten Parameter (den Dividenden hier) als 8 setzt
e.(2)       # => 4
# mit & können Proc-Objekte (eigentlich jede Klasse die eine call Methode hat) wieder in Blöcke umgewandelt werden
[8,4,2,1].map(&e) # => [1, 2, 4, 8]

Alle Blöcke sind übrigens Closures, sie speichern also z. B. den Zustand lokaler Variablen, wenn sie innerhalb des Blocks dauerhaft benötigt werden.

Mixins[Bearbeiten | Quelltext bearbeiten]

Ruby beherrscht bewusst keine Mehrfachvererbung, bietet stattdessen aber ein Konzept namens Mixins (deutsch: Beimischungen). Mixins sind Sammlungen von Methoden, die beliebigen Klassen beigemischt werden können. Eine Klasse kann beliebig viele Mixins beinhalten. Mixins werden in der Vererbungshierarchie zwischen Klasse und Superklasse in der Reihenfolge eingehängt, in der sie geschrieben wurden. Alternativ ist es auch möglich, Mixins vor die eigentliche Klasse zu hängen (u. a. hilfreich für Aspektorientierte Programmierung). Um Mixins zu definieren, werden in Ruby Module benutzt, das sind quasi Klassen (d. h. Sammlungen von Methoden) die nicht instantizert werden können und Namensräume in einem.

class Tier
  def sagt
    puts "#{self.class} sagt nichts" # "text#{logik}text" ist Rubys Textinterpolation
  end
end

module KannSchwimmen # Module, d. h. KannSchwimmen.new geht nicht
  def schwimmt
    puts "#{self.class} schwimmt" # self gibt die Instanz zurück,
  end                             #  jedes Objekt hat eine Methode .class die das Klassenobjekt zurück gibt
end

module KannNichtSchwimmen
  def schwimmt
    puts "#{self.class} geht unter" 
  end
end

class Fisch < Tier # Vererbung wird durch den Kleiner-Als-Operator gekennzeichnet
  include KannSchwimmen # füge KannSchwimmen zwischen Fisch und Tier ein
end

class Vogel < Tier
  include KannNichtSchwimmen
  def sagt # übliches Vererben, überdecke sagt-Methode von Tier
    puts "#{self.class}: Piep"
  end
end

class Mensch < Tier
  include KannSchwimmen
  def sagt
    puts "#{self.class}: Ich kann mich besser ausdrücken"
  end
end

class NichtSchwimmer < Mensch
  prepend KannNichtSchwimmen # hänge KannNichtSchwimmen vor NichtSchwimmer ein,
end                          # dh, überdecke die schwimmt-Methode

fisch = Fisch.new
mensch = Mensch.new
vogel = Vogel.new
nicht_schwimmer = NichtSchwimmer.new

fisch.sagt                # => Fisch sagt nichts
vogel.sagt                # => Vogel: Piep
mensch.sagt               # => Mensch: Ich kann mich besser ausdrücken
nicht_schwimmer.sagt      # => NichtSchwimmer: Ich kann mich besser ausdrücken
puts
fisch.schwimmt            # => Fisch schwimmt
vogel.schwimmt            # => Vogel geht unter
mensch.schwimmt           # => Mensch schwimmt
nicht_schwimmer.schwimmt  # => NichtSchwimmer geht unter

Offene Klassen[Bearbeiten | Quelltext bearbeiten]

In Ruby sind alle Klassen offen, das heißt sämtliche Methoden können im späteren Programmverlauf ausgetauscht werden. Das gilt auch für alle ruby-internen Klassen. Um Methoden zu überschreiben, muss lediglich eine neue Klasse mit demselben Namen wie die zu überschreibende Klasse angelegt werden. Ruby tauscht dann die neu definierten Methoden aus oder fügt sie hinzu. Diese Technik ist sehr mächtig, ermöglicht aber auch schwer auffindbare Fehler, besonders in größeren Projekten. Aus diesem Grund wird sie auch leicht abwertend Monkey-Patching genannt. Etwas Abhilfe zu den auftretenden Problemen schaffen Refinements, eine Möglichkeit die Überschreibungen lokal einzuschränken. Die zuvor vorgestellte Methode, Module den gewünschten Klassen vorzuhängen (prepend), ist meist die sichere Variante (und bietet außerdem die Möglichkeit die überdeckte Methode direkt aufzurufen).

class Numeric # ruby-interne Klasse
  def inverse # neue Methode, berechne den Kehrwert
    1.0 / self
  end
end

5.inverse # => 0.2

Domänenspezifische Sprache[Bearbeiten | Quelltext bearbeiten]

Ruby wird gerne benutzt zum Erzeugen von domänenspezifischen Sprachen. Das sind Textbausteine, die weniger wie Programmcode aussehen, sondern mehr wie eine Auszeichnungssprache, in Wirklichkeit normaler Ruby-Code sind. Möglich macht das zum einen die flexible Schreibweise von Ruby, bspw. sind Klammern hinter Methodenaufrufen oder Semikolons am Ende der Zeile optional. Zum anderen bietet Ruby viele Möglichkeiten zur Metaprogrammierung, die es möglich machen redundante Ausdrücke verschwinden zu lassen und hinter den Kulissen das Programm zu verändern. Hier als Beispiel die Konfiguration einer Testfabrik für FactoryBot:

FactoryBot.define do
  factory :user do
    first_name "Max"
    last_name  "Mustermann"
    admin false
  end
  factory :admin, class: User do
    first_name "Admin"
    last_name  "Root"
    admin true
  end
end

Mächtige Standardbibliothek[Bearbeiten | Quelltext bearbeiten]

Die Kern-Klassen (String, Integer, Float, Array, Hash, Range, Regexp) bringen eine Fülle an Methoden mit, die in jedem Rubyprogramm sofort verwendet werden können. Insbesondere durch die häufige Nutzung von Blöcken können mit diesen Methoden lange Programme auf wenige Zeilen reduziert werden, weshalb sich Ruby gut zum Zeigen von Proof of Concepts eignet. Besonders hervor hebt sich das Modul Enumerable, welches viele Konzepte der funktionalen Programmierung direkt in den Ruby-Kern bringt. Enumerable kann in jedes Objekt eingebunden werden, welches einen iterierbaren Container darstellt und die Methode each implementiert, bspw. die Klassen Array, Hash und Range. For-Schleifen sind in Ruby deshalb meist unnötig. Die im Folgenden gezeigten Methoden reduce, count, sort_by, each_slice und map sind alles Methoden aus Enumerable und können somit selbst erstellten Klassen leicht hinzugefügt werden.

# summiere die zahlen 1 bis 100 (ohne Summenformel), reduce entspricht fold aus der funktionalen Programmierung
(1..100).reduce{|counter,number| counter + number} # (1..100).sum geht auch
# Array aus den Zahlen von 0 bis 30 in 5er Schritten
(0..30).step(5).to_a
# Anzahl der Zeilen einer Datei die leer sind (ohne die Datei komplett in den Arbeitsspeicher zu laden)
IO.foreach("Dateiname").count{|line| line =~ /^\s*$/}
# Sortiere Array nach Länge der Nachnamen
["Max Mustermann", "John Doe", "Tarou Yamada"].sort_by{|name| name.split.last.length}
# Schreibe jedes dritte Wort im Satz in Großbuchstaben
"Franz jagt im komplett verwahrlosten Taxi quer durch Bayern.".split.each_slice(3).map{|drei_wörter| drei_wörter.last.upcase!; drei_wörter}.flatten.join(" ")

Zusätzlich von den integrierten Modulen wird Ruby standardmäßig mit vielen Modulen ausgeliefert. Bspw. sind Unterstützung für JSON, YAML, HTTP, Benchmarks, Primzahlen, sichere Zufallszahlen, OpenSSL und Logging sofort vorhanden.

Metaprogrammierung[Bearbeiten | Quelltext bearbeiten]

Ruby bietet weitreichende Möglichkeiten zu Metaprogrammierung an. Damit ist es z. B. möglich Methoden zu generieren, Instanzvariablen auszutauschen, die Vererbungshierachie zu verändern oder Konstanten zu bearbeiten. Die Syntax zu verändern oder weitere Operatoren hinzuzufügen geht allerdings nicht. Hier sei als Beispiel eine Methode aufgeführt Setter und Getter automatisch zu erzeugen (unter den Namen attr_reader, attr_writer, attr_accesor bereits im Standard enthalten).

class Object # Mokey-Patching aller Klassen
  def self.getter *args # self ist hier Object, es wird eine Klassenmethode erzeugt
    args.each do |arg| # es wird durch alle Parameter iteriert
      define_method arg do # define_method(arg){block} erzeugt eine neue Methode des Names arg mit dem Inhalt block
        instance_variable_get("@#{arg}".to_sym) # instance_variable get gibt den Wert der Instanzvariablen des übergeben Namens zurück
      end                                       # \- "@#{arg}" hängt ein @ vor den Inhalt von arg, to_sym wandelt den String um in ein Symbol
    end
  end
  def self.setter *args # *args packt alle Parameter in ein Array names args
    args.each do |arg|
      define_method :"#{arg}=" do |new_value|              # define_method übergibt dem Block die übergeben Parameter
        instance_variable_set("@#{arg}".to_sym, new_value) # \- setter-methoden enden mit einem =
      end
    end
  end
end

class PaarMit2
  def initialize links # Konstruktor
    @links = links # Instanzvariblen werden bei Erwähnung in einer beliebigen Methode automatisch erzeugt
    @rechts = 2
  end
  getter :links, :rechts
  setter :links
end

paar = PaarMit2.new(4) # new ruft immer den Konstruktor auf
paar.links       # => 4
paar.rechts      # => 2
paar.links = 9   # => 9
paar.links       # => 9
paar.rechts = 8  # => Fehler: NoMethodError (undefined method `rechts=')

Diese Techniken eignen sich auch gut zum Debuggen von Anwendungen oder zum Analysieren schlecht dokumentierter Anwendungen oder Bibliotheken. Bspw. antwortet jedes Objekt beim Aufruf der Methode methods mit einer Liste aller seiner Methoden, instance_variables gibt eine Liste aller Instanzvariablen zurück.

Integration in Unix[Bearbeiten | Quelltext bearbeiten]

Ruby bietet sich, ähnlich wie Perl, an, direkt in die Pipeline der Unix-Shell integriert zu werden. Möglich machen das Kommandozeilenparameter des Rubyinterpreters, die ermöglichen Programmlogik und übliches Programmverhalten zu definieren (üblicherweise die gleiche Operation auf jeder Zeile auszuführen). Ruby erweitert die Standard-Unixwerkzeuge mit fortgeschrittene Methoden zur Textanalyse und Textverarbeitung.

Weiterhin bietet Ruby auch die Möglichkeit, innerhalb eines Programms bequem Prozesse zu starten, deren Eingabe zu steuern und Ausgabe und Rückgabewerte auszulesen. Code innerhalb `` wird direkt an die Unix-Shell übergeben. Bspw. speichert der Befehl os_string = `uname -a` den Betriebssystemnamen direkt in eine Rubyvariablen. Der Rückgabewert des letzten Programmaufrufs wird analog zur Shell automatisch in der globalen Variablen $? gespeichert. Auch Signalbehandlung, Sockets und Threads werden ohne zusätzliche Bibliotheken direkt vom Sprachkern unterstützt. Zum Beschleunigen der Verarbeitung gibt es in Ruby ein Modul namens FileUtils, das viel der Funktionalität vieler Unix-Dateiverwaltungsprogramme (rm, cp, chmod) direkt in Ruby abbildet.

Syntax und Grundlagen[Bearbeiten | Quelltext bearbeiten]

Namenskonvention[Bearbeiten | Quelltext bearbeiten]

Ruby folgt der üblichen Konvention für Methoden- und Variablennamen, ein Name muss mit einem Kleinbuchstaben oder Unterstrich beginnen, dann dürfen beliebige Buchstaben (nach Unicode), Ziffern und Unterstriche folgen. Methoden dürfen zusätzlich mit einem Ausrufe- oder Fragezeichen enden. Nach der üblichen Konvention dient Ersteres als Hinweis dass diese Methode eine schärfere Version der gleichen Methode ohne Ausrufezeichen ist (verändert den Zustand des Objekts, wirft Fehler, …), Letzteres bedeutet dass die Funktion ein Boolean zurückgibt (true oder false). Fängt ein Variablenname mit einen Großbuchstaben an, so ist es eine Konstante. Variablen können zusätzlich mit einem Sonderzeichen anfangen, welches den Gültigkeitsbereich beschreibt.

Variablenbezeichner[Bearbeiten | Quelltext bearbeiten]

Ruby unterscheidet fünf Gültigkeitsbereiche:

  • Normalerweise ist eine Variable lokal innerhalb des umgebenden Blocks oder der umgebenden Methode gültig.
  • Ein @ vor Variablen deklariert Instanzvariable, sie werden dann dauerhaft der Instanz zugeordnet und sind nur in dieser sichtbar. Auf Instanzvariablen kann von außen nur über Methoden zugegriffen werden. Zugriff auf nicht vorhanden Instanzvariablen werfen keinen Fehler, sondern geben nil zurück
  • Ein vorangestelltes @@ macht Variablen zu Klassenvariablen, die zur umgebenden Klasse gehören.
  • Mit $ werden Variablen global und sind damit im gesamten Programm sichtbar.
  • Der Gültigkeitsbereich für Konstanten richtet sich nach lokalen Verschachtelungstiefe und kann mit :: spezifiziert werden

Methodenaufrufe und Ausdrücke[Bearbeiten | Quelltext bearbeiten]

In Ruby werden Methodenaufrufe nicht zwingend mit nachfolgenden Klammern gekennzeichnet. gets.chomp ist also äquivalent zu gets().chomp(). Wenn Methoden Parameter benötigen müssen diese in Klammern gesetzt werden wenn auf dem Ergebnis eine weitere Operation ausgeführt werden soll. "a,b,c".split ","ist ok, "a,b,c".split(",").join(" ") benötigt aber zwingend das erste Klammernpaar. Da Klammern auch zur Gruppierung eingesetzt werden, sollte bei erwünschtem Methodenaufruf niemals ein Leerzeichen zwischen Methodenname und Klammer gesetzt werden ([1,2].join␣("|") * 2  != [1,2].join("|") * 2 => [1,2].join(("|") * 2) != ([1,2].join("|")) * 2 => "1||2" != "1|21|2").

Jede Anweisung bildet einen Ausdruck, der einer Variablen zugewiesen kann. Einzelne Anweisungen werden durch Zeilenumbrüche oder Semikolons getrennt. Der letzte Ausdruck innerhalb einer Methode bildet automatisch deren Rückgabewert. Es kann aber auch schon vorher mit Schlüsselwort return frühzeitig zurückgesprungen werden.

Datentypen[Bearbeiten | Quelltext bearbeiten]

Die elementaren Datentypen besitzen bequemere Konstruktoren als das übliche Klasse.new

  • String: "Text", 'Text' oder %{Text} Text
  • Integer: 3 (Zehnersystem), 0775 oder 0o775 (Oktalsystem), 0xFF (Hexadezimalsystem) 0b1001 (Binärsystem) Ganzzahl (unbeschränkte Größe)
  • Float: 3.0 Fließkommazahl
  • Complex: 1 + 1i Komplexe Zahl
  • Array: [1,2,3] Auflistung, kann beliebige verschiedene Datentypen enthalten
  • Hash: { 1 => "eins", 2 => "zwei" } Zuordnung, ordnet jedem Wert vor den Pfeilen (Schlüssel) genau einen Wert zu
  • Regexp: /\A(eins|zwei)\z/ oder %r{\A(eins|zwei)\z} Regulärer Ausdruck
  • Range: 1..3 oder 1...4 (rechtes Element ausschließend) Intervall
  • Symbol: :zeichen Symbol, siehe unten

Dynamische Strings[Bearbeiten | Quelltext bearbeiten]

Zeichenketten sind in Ruby standardmäßig veränderbar, d. h. ein String-Objekt kann seinen Wert zur Laufzeit verändern. text = "Felar"; a[2..3] = "hle"; puts a verändert den Wert von text direkt (und gibt Fehler aus). Viele String-Methoden gibt es sowohl in einer verändernden Variante und in einer Variante die ein neues String-Objekt erzeugt.

a = "ABC"
b = a.downcase
puts a, b # => ABC abc
a.downcase!
puts a, b # => abc abc

Strings können wie jedes Ruby-Objekt durch Aufruf der Methode freeze eingefroren werden (bspw. "ABC".freeze), und sind danach unveränderbar.

Symbole[Bearbeiten | Quelltext bearbeiten]

Dieser etwas ungewöhnliche und auch umstrittene Datentyp ist in eine Art Mischung aus Integer und String. Er dient primär als Merkhilfe für Schlüssel von Hashes, da hier dynamische Strings unpraktisch sind und Integer wenig Speicher benötigen. Symbole besitzen die Textmanipulationsmethoden von String nicht, können aber jederzeit in Strings umgewandelt werden. Werden Symbole in Hashes als Schlüssel verwendet, kann eine vereinfachte Schreibweise verwendet werden. Statt { :a => 1, :b => 2 } kann auch { a: 1, b: 2 } geschrieben werden. Ruby-intern werden Symbole auch häufig verwendet, so können Methoden auch mit ihrem Symbolnamen aufgerufen werden: 1 < 4 kann auch als 1.send(:<, 4) geschrieben werden. Einfache Blöcke die nur eine Methode des übergebenen Objekts aufrufen können mit Symbolen vereinfacht geschrieben werden, wobei das Symbol die aufzurufende Methode bezeichnet. [1,2,3].map{|i| i.to_s} kann auch als [1,2,3].map(&:to_s) geschrieben werden (& wandelt das Symbol in einen Block um).

Wahrheitswerte[Bearbeiten | Quelltext bearbeiten]

In Ruby gibt es drei verschiedene Wahrheitswerte, true, false und nil. Nil steht hierbei für ein fehlendes Ergebnis und wird wie false als Falsch ausgewertet. True und alle anderen Objekte werden als Wahr ausgewertet. puts "Ja!" if 0 evaluiert also zu Wahr, „Ja!“ wird ausgegeben.

Fehlerbehandlung[Bearbeiten | Quelltext bearbeiten]

In den meisten Programmiersprachen muss der auf Fehler zu überprüfende Programmteil explizit markiert werden (meist durch die Schlüsselwörter try und catch), in Ruby muss für die üblichsten Fälle, der Programmteil umfasst die gesamte Methode oder den gesamten Block, kein extra Bereich markiert werden. Wenn eine Abgrenzung erforderlich sein sollte kann der Bereich mit begin und end markiert werden.

def gib_summe_aus arg1, arg2
  # Methodenlogik
  unless arg1.is_a?(Numeric) && arg2.is_a?(Numeric)
    raise ArgumentError.new("Bitte nur Zahlen eingeben") # raise wirft Fehler
  end
  puts arg1 + arg2
rescue ArgumentError => e # Fange ArgumentError ab
  puts "Es ist ein Fehler bei der Paremterübergabe aufgetreten"
rescue => e # Fange alle weiteren Fehler ab
  puts e.message
ensure # wird auf jeden Fall ausgeführt
  puts 'Methodenende'
end

Alternative Bedingungen / Schleifen[Bearbeiten | Quelltext bearbeiten]

Bedingungen und Schleifen bieten sowohl eine Suffixform also auch ein eigenes Schlüsselwort für das Inverse (inspiriert von Perl).

a = 5
if a < 10;  puts a; end
puts a if a < 10 # Suffixform
unless a >= 10; puts a; end # Invers
puts a unless a >= 10 # Invers + Suffixform

while a < 10;  puts a;  a += 1; end
(puts a; a+=1) while a < 20 # Suffixform
until a >= 30;  puts a;  a += 1; end # Invers
(puts a; a +=1) until a >= 40 # Invers + Suffixform

Reguläre Ausdrücke[Bearbeiten | Quelltext bearbeiten]

Reguläre Ausdrücke sind in Ruby direkt im Sprachkern enthalten. Ruby benutzt eine eigene Regex-Engine names Onigmo, deren Syntax und Funktionsumfang zum größten Teil kompatibel mit PCRE ist. Zusätzlich ist es möglich Rubyvariablen direkt in reguläre Ausdrücke zu interpolieren und beliebige Programmlogik durch Blöcke beim Suchen und Ersetzen zu verwenden. Bspw. addiert folgender Befehl alle Zahlen in einem Text um eins: puts "test, 1, 2, 3".gsub(/(\d+)/){|zahl| zahl.to_i + 1} # => test, 2, 3, 4

Konstanten[Bearbeiten | Quelltext bearbeiten]

Konstanten sind in Ruby alle Variablen die mit einem Großbuchstaben anfangen. Alle Klassen und Module sind somit Konstanten. Das Besondere an Konstanten in Ruby ist ihre Verschachtelung. Konstanten innerhalb von Konstanten können mittels :: aufgerufen werden und unterliegen keinen Zugriffsrechten (wie etwa Methoden oder Instanzvariablen). Deswegen können Module als Namensräume benutzt werden, alle Variablen bleiben darin versteckt, auf alle Konstanten (wie bspw. Klassen) kann über den Modulname qualifiziert zugegriffen werden (bspw. MeinModul::MeineKlasse.new).

Bestandteile[Bearbeiten | Quelltext bearbeiten]

Interaktive Ruby-Shell[Bearbeiten | Quelltext bearbeiten]

Interactive Ruby (irb) ist eine Read-Eval-Print Loop (REPL), mit welcher der Anwender interaktiv Ruby programmieren kann. Irb wird mit dem Ruby-Interpreter ausgeliefert und kann zum Analysieren und Testen eingesetzt werden:

irb(main):001:0> (5 + 7) * 2
=> 24
irb(main):002:0> ((5 + 7) * 2).to_s.reverse
=> "42"
irb(main):003:0> "Ein Beispielstring".size
=> 18

Als Alternative zu Irb gibt es Pry, eine REPL, welche weitaus tiefgreifendere Möglichkeiten zur Introspektion und zum Debuggen mitbringt.

RDoc und ri[Bearbeiten | Quelltext bearbeiten]

RDoc ist ein Software-Dokumentationswerkzeug, welches aus Ruby- und C-Quelltexten automatisch HTML-Dokumentationsdateien erstellt. Weiterhin wird eine Datenbank aufgebaut, die mit dem Tool ri durchsucht werden kann. RDoc und ri sind Bestandteil der Standarddistribution und werden zusammen mit dem Interpreter ausgeliefert.

Rake[Bearbeiten | Quelltext bearbeiten]

Rake steht für Ruby-Make und ist eine Alternative zu Make aus C. Da es in Ruby keine Kompilationsphase gibt, dienen Rakefiles zum Automatisieren repetitiver Aufgaben, bspw. dem Generieren von Dokumentation, dem Upload von Dateien oder dem Packen von Projekten. Rake kann wie Make Abhängigkeiten auflösen, wenn für Aufgabe B erst Aufgabe A erfüllt sein muss, so führt Rake automatisch Aufgabe A aus. Rake ist eine domänenspezifische Sprache, d. h. es lässt sich bequem wie eine Auszeichnungssprache lesen, bietet aber die vollen Möglichkeiten die Ruby bietet.

Paketverwaltung[Bearbeiten | Quelltext bearbeiten]

Ruby-Bibliotheken werden üblicherweise als Gems (RubyGems) gepackt und auf rubygems.org, dem zentralen Gem-Register, veröffentlicht. RubyGems löst bei der Installation oder bei Updates die im Paket angegeben Abhängigkeiten eigenständig rekursiv auf und bietet dabei sogar die Möglichkeit Pakete gleichzeitig in unterschiedlichen Versionen bereitzustellen. Seit Ruby 1.9 ist Rubygems Bestandteil der Standardbibliothek von Ruby. Heute wird es meist zusammen mit Bundler benutzt, einem Programm welches Schnappschüsse aus einer Kollektion von Gems erstellt und diese Anordnung auf anderen Maschinen (oder andern Projektordnern) wieder herstellen kann.

Implementierungen[Bearbeiten | Quelltext bearbeiten]

Referenzimplementierung[Bearbeiten | Quelltext bearbeiten]

Die Referenzimplementierung von Ruby wurde von Yukihiro „Matz“ Matsumoto als Interpreter in C entworfen. Dieser wird meist als MRI (Matz’s Ruby Interpreter) oder auch als CRuby bezeichnet und ist derzeit am weitesten verbreitet. Den Kern bildet YARV (Yet Another Ruby VM), eine virtuelle Maschine. Statt ein Rubyprogramm direkt auszuführen, wird es zunächst in Bytecode übersetzt und dann von YARV interpretiert, wodurch sich ein Geschwindigkeitsvorteil ergibt. Weiterhin enthält diese Version eine leistungsstarke Regexp-Maschine namens Oniguruma und unterstützt Multibyte-Zeichensätze wie UTF-8.

Der offizielle Interpreter läuft auf den folgenden Betriebssystemen:

Alternative Implementierungen[Bearbeiten | Quelltext bearbeiten]

Es gibt zahlreiche alternative Ruby-Implementierungen mit unterschiedlichem Umfang und Zielen:

  • JRuby, eine Neuimplementierung des Ruby-Interpreters in Java mit dem Ziel, Ruby nahtlos in die Java-Plattform zu integrieren. JRuby ist fast vollständig kompatibel zu Ruby 2.3. JRuby ist zudem kompatibel zu einigen Ruby-Erweiterungen von Rubinius (Foreign Function Interface, Multi-VM-API), allerdings nicht mit C-Erweiterungen von CRuby.
  • Rubinius, eine von Smalltalk-80 inspirierte Implementierung. Abgesehen von der virtuellen Maschine ist Rubinius vollständig in Ruby geschrieben. Rubinius ist nahezu vollständig kompatibel zu Ruby 2.3. Rubinius nutzt die Low Level Virtual Machine (LLVM) und ist kompatibel mit C-Erweiterungen der Referenzimplementierung.
  • Mruby ist eine weitere Rubyimplementierung erschaffen vom Ruby-Schöpfer Yukihiro Matsumoto. Mruby ist eine auf eingebettete Systeme zugeschnittene Version von Ruby, welche sich durch geringen Speicherbedarf und hohe Modularisierung auszeichnet. Mruby stellt nicht die vollständige Funktionalität von CRuby bereit. Mruby kann den Quellcode in Bytecode übersetzen, welcher dann ohne Kompilationsschritt interpretiert oder in C-Programme eingebettet werden kann.
  • Opal ist ein Compiler von Ruby nach JavaScript. Es handelt sich um eine Implementierung der Ruby-Corelib und Stdlib, sowie den zugehörige Gems. Mit Opal lassen sich clientseitige Webanwendungen in Ruby realisieren. Es gibt einige Einschränkungen, die der Umsetzung nach JavaScript geschuldet sind.
  • TruffleRuby ist eine Ruby-Implentierung die auf der GraalVM läuft. Ziel der Implementierung ist es die Vorteile der GraalVM mittels Ruby nutzbar zu machen. Diese sind Geschwindigkeit (Start- und Laufzeit), Parallelisierung und das kontrollierte Ausführen von C-Erweiterungen. TruffleRuby wird maßgeblich von Oracle entwickelt.

Historische Rubyimplementierungen:

  • IronRuby ist eine Implementierung, die Ruby ins .NET-Framework integriert und in C# implementiert ist.
    Das Projekt ist seit 13. März 2011 inaktiv und wird nicht mehr weiterentwickelt.
  • Cardinal, ein Interpreter für die virtuelle Maschine Parrot. Das Projekt ist seit Juni 2013 inaktiv.
  • MagLev ist eine Implementierung des Unternehmens Gemstone für deren proprietäre Smalltalk VM. Das Projekt ist seit 2015 inaktiv.
  • MacRuby ist eine Implementierung in Objective-C von Apple, die bis Version 0.4, wie Ruby 1.9, YARV nutzt, ab Version 0.5 allerdings, wie Rubinius, auf die LLVM setzt. Die Weiterentwicklung wurde mit dem 5. Januar 2015 eingestellt.
  • Ruby Enterprise Edition ist eine modifizierte Version der Referenzimplementierung, bei der im Wesentlichen der Garbage Collector neu implementiert wurde. REE ist lediglich mit Ruby 1.8 kompatibel, die aktuellste Version stammt aus dem Jahr 2012.

Die Kompatibilität zur Referenzimplementierung wird durch das RubySpec-Projekt überprüft. Es stellt dabei sowohl eine Testsuite als auch eine Spezifikation für Ruby dar. RubySpec war ursprünglich ein Teil von Rubinius, wurde aber ausgelagert und danach auch von einer Vielzahl anderer Entwickler vorangetrieben. Es wird in vielen Ruby-Implementierungen verwendet.

Mit Hilfe des Ruby Version Managers[7] oder Rbenv[8] besteht die Möglichkeit, mehrere Ruby-Implementierungen und Gem-Versionen parallel zu betreiben.

Ein wichtiges Merkmal der Implementierungen ist, ob sie in der Lage sind, Ruby on Rails auszuführen. Derzeit können dies neben der Referenzimplementierung nur JRuby[9] und Rubinius[10].

Verbreitung und Einsatz[Bearbeiten | Quelltext bearbeiten]

Ruby ist für alle gängigen Desktop-Betriebssysteme frei erhältlich, in den meisten Linux-Distributionen ist es in den mitgelieferten Paketquellen enthalten, unter macOS sogar vorinstalliert.

Größte Verbreitung findet Ruby im Einsatz als Webserver-Skriptsprache. Das verbreitetste Framework hierbei ist Ruby on Rails, wobei es aber zahlreiche Alternativen verschiedener Größen gibt (bspw. Sinatra und Hanami). Die Liste großer Railsprojekte ist lang, am bekanntesten sind möglicherweise kommerzielle Webseiten wie Github und Airbnb oder Gemeinschaftsprojekte wie Diaspora, Redmine und Discourse. In Ruby geschriebene Webtechnologien wie Sass und Haml finden auch außerhalb des Ruby-Ökosystems Verwendung.
Weiter große Verbreitung findet Ruby als Skriptsprache zum Verwalten und Automatisieren von Serveraufgaben, angefangen als Ersatz für komplexere Bash-Skripte, als auch für größere Anwendungen, bspw. Puppet (Serverkonfiguration über das Netzwerk), Metasploit (Penetrationstests), YaST (OpenSUSE-Serveradminstration) und Vagrant (Verwaltung virtueller Maschinen).
Ebenfalls wird Ruby wegen des flexiblen Syntax gerne als domänenspezifische Sprache benutzt. Mit mruby gibt es auch eine Ruby-Variante die sich auf Eingebette Systeme spezialisiert. Vereinzelt wird Ruby auch als Skript-Sprache in Spielen verwendet, bspw. im RPG-Maker.

Kritik[Bearbeiten | Quelltext bearbeiten]

Kritik an der Sprache wurde aus verschiedenen Gründen geübt:

  • Da Variablen vor Gebrauch nicht deklariert werden müssen, können bei Tippfehlern unerwartete Laufzeitfehler auftreten.
  • Ruby ist nicht immer abwärtskompatibel.[11]
  • Metaprogrammierung und Monkeypatching erlauben es einem Codestück, alle Klassen und Objekte des Prozesses, in dem es ausgeführt wird, zu ändern.[12]

Auch an der Referenzimplementierung bemängeln Kritiker mehrere Aspekte:

Sonstiges[Bearbeiten | Quelltext bearbeiten]

Nutzungsbedingungen[Bearbeiten | Quelltext bearbeiten]

Ruby ist freie Software. Deshalb ist es kostenlos nutzbar und im Quelltext verfügbar. Dadurch ergibt sich die Möglichkeit, die Sprache an seine eigenen Bedürfnisse anzupassen oder sie in eigene Programme einzubinden.

Der Interpreter und die Standardbibliothek von Ruby sind grundsätzlich unter den Bedingungen der 2-clause BSD-Lizenz nutzbar. Des Weiteren besteht die Möglichkeit, Ruby unter einer eigenen freien Lizenz zu verwenden.[15] Die Ruby-Lizenz ist GPL-kompatibel und wird von der Free Software Foundation als „frei“ akzeptiert.[16]

Ältere Versionen von Ruby (1.9.2 und früher) verwendeten anstatt der BSD-Lizenz die GPL V2. Als Begründung für den Wechsel wurden Inkompatibilitäten der alten Lizenzierung mit der GPL V3 angeführt.[17][18]

Literatur[Bearbeiten | Quelltext bearbeiten]

Für Programmieranfänger

Einstieg für Programmierer

Vertiefung für Programmierer

  • David A. Black: The Well-Grounded Rubyist. 2. Auflage. Manning, Shelter Island 2014, ISBN 978-1-61729-169-2 (englisch, Errata, Quellcode).
  • Hal Fulton, André Arko: The Ruby Way. Solutions and Techniques in Ruby Programming. 3. Auflage. Addison-Wesley, Upper Saddle River, u. a. 2015, ISBN 978-0-321-71463-3 (englisch, Auszug [PDF]).

Gesamtdarstellungen

  • David Flanagan, Yukihiro Matsumoto: Die Programmiersprache Ruby. O’Reilly Media, 2008, ISBN 978-3-89721-874-1

Für Fortgeschrittene

  • Lucas Carlson, Leonard Richardson: Ruby Cookbook. O’Reilly Media, 2. Auflage 2015, ISBN 1-4493-7371-2 (englisch)
  • Pat Shaughnessy: Ruby Under a Microscope. No Starch Press, 1. Auflage 2013, ISBN 1-59327-527-7 (englisch)
  • Russ Olsen: Eloquent Ruby. Addison-Wesley Professional, 2011, ISBN 978-0-321-58410-6 (englisch)
  • Russ Olsen: Design Patterns in Ruby. Addison-Wesley Professional, 2007, ISBN 978-0-321-49045-2 (englisch)

Normen und Standards[Bearbeiten | Quelltext bearbeiten]

  • ISO/IEC 30170 (englisch; standardisiert Ruby auf über 317 Seiten). Erste Version April 2012.

Weblinks[Bearbeiten | Quelltext bearbeiten]

 Commons: Ruby – Sammlung von Bildern, Videos und Audiodateien
 Wikibooks: Ruby-Programmierung – Lern- und Lehrmaterialien

Einzelnachweise[Bearbeiten | Quelltext bearbeiten]

  1. [1]
  2. D Programming Language 1.0 , Intro. Digital Mars
  3. iso.org
  4. RubyConf: History of Ruby
  5. Ein Interview mit dem Schöpfer von Ruby
  6. https://www.ruby-lang.org/en/about/
  7. Ruby Version Manager
  8. https://github.com/rbenv/rbenv rbenv
  9. JRuby Wiki. Abgerufen am 23. Februar 2017 (englisch).
  10. Evan Phoenix: Rails on Rubinius. 17. Mai 2008, archiviert vom Original am 22. März 2016; abgerufen am 23. Februar 2017 (englisch).
  11. infoq.com
  12. Avdi Grimm: Monkeypatching is Destroying Ruby. 23. Februar 2008, abgerufen am 23. Februar 2017 (englisch).
  13. infoq.com
  14. igvita.com
  15. Rubys Lizenzbedingungen. Abgerufen am 23. Februar 2017.
  16. Liste der GPL-kompatiblen Lizenzen. Abgerufen am 23. Februar 2017.
  17. Mailinglistendiskussion zum Lizenzwechsel. Archiviert vom Original am 11. Juni 2015; abgerufen am 8. Juli 2012.
  18. Beschluss zum Lizenzwechsel auf dem Ruby Developers Meeting 2010. Abgerufen am 23. Februar 2017.