2015-10-16 20:09:14 +03:00
|
|
|
|
---
|
|
|
|
|
language: ruby
|
2020-05-21 01:07:24 +03:00
|
|
|
|
filename: ruby-de.rb
|
2015-10-16 20:09:14 +03:00
|
|
|
|
contributors:
|
|
|
|
|
- ["David Underwood", "http://theflyingdeveloper.com"]
|
|
|
|
|
- ["Joel Walden", "http://joelwalden.net"]
|
|
|
|
|
- ["Luke Holder", "http://twitter.com/lukeholder"]
|
|
|
|
|
- ["Tristan Hume", "http://thume.ca/"]
|
|
|
|
|
- ["Nick LaMuro", "https://github.com/NickLaMuro"]
|
|
|
|
|
- ["Marcos Brizeno", "http://www.about.me/marcosbrizeno"]
|
|
|
|
|
- ["Ariel Krakowski", "http://www.learneroo.com"]
|
|
|
|
|
- ["Dzianis Dashkevich", "https://github.com/dskecse"]
|
|
|
|
|
- ["Levi Bostian", "https://github.com/levibostian"]
|
|
|
|
|
- ["Rahil Momin", "https://github.com/iamrahil"]
|
2020-05-21 01:07:24 +03:00
|
|
|
|
- ["Gabriel Halley", "https://github.com/ghalley"]
|
|
|
|
|
- ["Persa Zula", "http://persazula.com"]
|
|
|
|
|
- ["Jake Faris", "https://github.com/farisj"]
|
|
|
|
|
- ["Corey Ward", "https://github.com/coreyward"]
|
|
|
|
|
- ["Jannik Siebert", "https://github.com/janniks"]
|
|
|
|
|
- ["Keith Miyake", "https://github.com/kaymmm"]
|
2015-10-16 20:09:14 +03:00
|
|
|
|
translators:
|
|
|
|
|
- ["Christian Albrecht", "https://github.com/coastalchief"]
|
2016-10-21 18:40:03 +03:00
|
|
|
|
- ["Dennis Keller", "https://github.com/denniskeller"]
|
2020-05-21 01:07:24 +03:00
|
|
|
|
- ["Paul Götze", "https://gitub.com/paulgoetze"]
|
2015-10-16 20:09:14 +03:00
|
|
|
|
lang: de-de
|
|
|
|
|
---
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
```ruby
|
|
|
|
|
# Das ist ein Kommentar
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
|
|
|
|
=begin
|
2020-05-21 01:07:24 +03:00
|
|
|
|
Das ist ein mehrzeiliger Kommentar.
|
|
|
|
|
Die Anfangszeile muss mit "=begin" beginnen
|
|
|
|
|
und die Endzeile muss mit "=end" beginnen.
|
|
|
|
|
|
|
|
|
|
Alternativ kannst du jede Zeile in einem
|
|
|
|
|
mehrzeiligen Kommentar mit dem # Zeichen beginnen.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
=end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# In Ruby ist (fast) alles ein Objekt.
|
|
|
|
|
# Das schließt Zahlen ein...
|
|
|
|
|
3.class #=> Integer
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...und Zeichenketten (Strings)...
|
|
|
|
|
"Hallo".class #=> String
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...und sogar Methoden!
|
|
|
|
|
"Hallo".method(:class).class #=> Method
|
|
|
|
|
|
|
|
|
|
# Simple Arithmetik
|
2015-10-16 20:09:14 +03:00
|
|
|
|
1 + 1 #=> 2
|
|
|
|
|
8 - 1 #=> 7
|
|
|
|
|
10 * 2 #=> 20
|
|
|
|
|
35 / 5 #=> 7
|
2020-05-21 01:07:24 +03:00
|
|
|
|
2 ** 5 #=> 32
|
|
|
|
|
5 % 3 #=> 2
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Bitweise Operatoren
|
|
|
|
|
3 & 5 #=> 1
|
|
|
|
|
3 | 5 #=> 7
|
|
|
|
|
3 ^ 5 #=> 6
|
|
|
|
|
|
|
|
|
|
# Arithmetik ist aber eigentlich nur syntaktischer Zucker
|
|
|
|
|
# um eine Methode eines Objekts aufzurufen
|
2015-10-16 20:09:14 +03:00
|
|
|
|
1.+(3) #=> 4
|
|
|
|
|
10.* 5 #=> 50
|
2020-05-21 01:07:24 +03:00
|
|
|
|
100.methods.include?(:/) #=> true
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
## Spezielle Werte sind Objekte
|
|
|
|
|
nil # Equivalent zu null in anderen Sprachen
|
|
|
|
|
true # Wahrheitswert
|
|
|
|
|
false # Falschheitswert
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
|
|
|
|
nil.class #=> NilClass
|
|
|
|
|
true.class #=> TrueClass
|
|
|
|
|
false.class #=> FalseClass
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Gleicheit
|
2015-10-16 20:09:14 +03:00
|
|
|
|
1 == 1 #=> true
|
|
|
|
|
2 == 1 #=> false
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Ungleichheit
|
2015-10-16 20:09:14 +03:00
|
|
|
|
1 != 1 #=> false
|
|
|
|
|
2 != 1 #=> true
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Neben false selbst, ist nil der einzige andere
|
|
|
|
|
# zu Falsch evaluierende Wert
|
|
|
|
|
|
|
|
|
|
!!nil #=> false
|
|
|
|
|
!!false #=> false
|
|
|
|
|
!!0 #=> true
|
|
|
|
|
!!"" #=> true
|
|
|
|
|
|
|
|
|
|
# Weitere Vergleiche
|
2015-10-16 20:09:14 +03:00
|
|
|
|
1 < 10 #=> true
|
|
|
|
|
1 > 10 #=> false
|
|
|
|
|
2 <= 2 #=> true
|
|
|
|
|
2 >= 2 #=> true
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Kombinierter Vergleichsoperator (gibt `1` zurück wenn das erste Argument
|
|
|
|
|
# größer ist, und `-1`, wenn das zweite Argument größer ist, sonst `0`)
|
|
|
|
|
1 <=> 10 #=> -1 (1 < 10)
|
|
|
|
|
10 <=> 1 #=> 1 (10 > 1)
|
|
|
|
|
1 <=> 1 #=> 0 (1 == 1)
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
### Logische Operatoren
|
|
|
|
|
true && false #=> false
|
|
|
|
|
true || false #=> true
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Es gibt alternative Versionen der logischen Operatoren mit niedrigerer
|
|
|
|
|
# Wertigkeit. Diese werden meistens zur Flusskontrolle eingesetzt, um
|
|
|
|
|
# verschiedenen Ausdrücke zu verketten bis einer true oder false zurück
|
|
|
|
|
# liefert.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# `do_something_else` wird nur ausgewertet wenn `do_something` true ist.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
do_something() and do_something_else()
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# `log_error` wird nur ausgewertet wenn `do_something` false ist.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
do_something() or log_error()
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# String Interpolation
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
placeholder = 'Ruby'
|
|
|
|
|
"Ich kann in #{placeholder} Platzhalter mit doppelten Anführungszeichen füllen."
|
|
|
|
|
#=> "Ich kann in Ruby Platzhalter mit doppelten Anführungszeichen füllen."
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Du kannst Strings mit `+` verbinden, nicht jedoch mit anderen Typen
|
|
|
|
|
'hallo ' + 'Welt' #=> "hallo Welt"
|
|
|
|
|
'Hallo ' + 3 #=> TypeError: no implicit conversion of Integer into String
|
|
|
|
|
'hallo ' + 3.to_s #=> "hallo 3"
|
|
|
|
|
"hallo #{3}" #=> "hallo 3"
|
|
|
|
|
|
|
|
|
|
# ...oder Strings mit Operatoren kombinieren
|
|
|
|
|
'hallo ' * 3 #=> "hallo hallo hallo "
|
|
|
|
|
|
|
|
|
|
# ...oder Strings an andere Strings anhängen
|
|
|
|
|
'hallo' << ' Welt' #=> "hallo Welt"
|
|
|
|
|
|
|
|
|
|
# Du kannst Text mit einer neuen Zeile am Ende ausgeben
|
|
|
|
|
puts "Ich gebe Text aus!"
|
|
|
|
|
#=> Ich gebe Text aus!
|
|
|
|
|
#=> nil
|
|
|
|
|
|
|
|
|
|
# ...oder Text ohne einen Zeilenumbruch ausgeben
|
|
|
|
|
print "Ich gebe Text aus!"
|
|
|
|
|
#=> "Ich gebe Text aus!" => nil
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
|
|
|
|
# Variablen
|
|
|
|
|
x = 25 #=> 25
|
|
|
|
|
x #=> 25
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Beachte, dass Zuweisungen den zugewiesenen Wert zurückgeben.
|
|
|
|
|
# D.h. du kannst mehrfache Zuweisungen machen.
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
x = y = 10 #=> 10
|
|
|
|
|
x #=> 10
|
|
|
|
|
y #=> 10
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Nutze snake_case für Variablennamen.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
snake_case = true
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Nutze verständliche Variablennamen.
|
|
|
|
|
path_to_project_root = '/guter/Name/'
|
|
|
|
|
m = '/schlechter/Name/'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Symbole sind unveränderliche, wiederverwendbare Konstanten, welche intern
|
|
|
|
|
# als Integer repräsentiert werden. Sie werden häufig anstelle von Strings
|
|
|
|
|
# verwendet, um semantisch sinnvoll Werte zu übermitteln.
|
|
|
|
|
# Symbols werden mit dem Doppelpunkt gekennzeichnet.
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
:pending.class #=> Symbol
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
status = :pending
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
status == :pending #=> true
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
status == 'pending' #=> false
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
status == :approved #=> false
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Strings können in Symbole konvertiert werden und umgekehrt.
|
|
|
|
|
status.to_s #=> "pending"
|
|
|
|
|
"argon".to_sym #=> :argon
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
# Arrays
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Das ist ein Array.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Array können verschiedene Typen beinhalten
|
2015-10-16 20:09:14 +03:00
|
|
|
|
[1, 'hello', false] #=> [1, "hello", false]
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
## Arrays könnenindiziert werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Von vorne...
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array[0] #=> 1
|
2020-05-21 01:07:24 +03:00
|
|
|
|
array.first #=> 1
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array[12] #=> nil
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...oder von hinten...
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array[-1] #=> 5
|
2020-05-21 01:07:24 +03:00
|
|
|
|
array.last #=> 5
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...oder mit einem Startindex und einer Länge...
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array[2, 3] #=> [3, 4, 5]
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...oder mit einem Range...
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array[1..3] #=> [2, 3, 4]
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Du kanns ein Array umkehren.
|
|
|
|
|
# Gib ein neues Array mit umgkehrten Werten zurück
|
|
|
|
|
[1,2,3].reverse #=> [3,2,1]
|
|
|
|
|
|
|
|
|
|
# Kehre ein Array an Ort und Stelle um, um die Variable mit den
|
|
|
|
|
# umgekehrten Werten zu aktualisieren.
|
|
|
|
|
a = [1,2,3]
|
|
|
|
|
a.reverse! #=> a==[3,2,1] wegen des Aufrufs von reverse mit Ausrufezeichens ('!')
|
|
|
|
|
|
|
|
|
|
# Wie bei der Arithmetik, ist Zugriff mit [index] nur
|
|
|
|
|
# syntaktischer Zucker für den Aufruf der `[]` Methode auf dem Objekt.
|
|
|
|
|
array.[] 0 #=> 1
|
|
|
|
|
array.[] 12 #=> nil
|
|
|
|
|
|
|
|
|
|
# Du kannst Werte zu einem Array hinzufügen...
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array << 6 #=> [1, 2, 3, 4, 5, 6]
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Oder so
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array.push(6) #=> [1, 2, 3, 4, 5, 6]
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# ...und testen ob ein Element schon vorhanden ist
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array.include?(1) #=> true
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Hashes sind Rubys Hauptdatenstruktur for Schlüssel/Wert Paare.
|
|
|
|
|
# Hashes werden durch geschweifte Klammern gekennzeichnet.
|
|
|
|
|
hash = { 'Farbe' => 'grün', 'Nummer' => 5 }
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
hash.keys #=> ['farbe', 'nummer']
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Hashes can be quickly looked up by key.
|
|
|
|
|
hash['Farbe'] #=> "grün"
|
|
|
|
|
hash['Nummer'] #=> 5
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Abfragen eines nicht vorhandenen Schlüssels, gibt nil zurück.
|
|
|
|
|
hash['nicht vorhanden'] #=> nil
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Wenn du Symbole als Schlüssel in einem Hash verwendest, kannst du
|
|
|
|
|
# eine alternative Syntax verwenden.
|
|
|
|
|
hash = { :defcon => 3, :action => true }
|
|
|
|
|
hash.keys #=> [:defcon, :action]
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
hash = { defcon: 3, action: true }
|
|
|
|
|
hash.keys #=> [:defcon, :action]
|
|
|
|
|
|
|
|
|
|
# Testen ob ein Schlüssel oder Wert im Hash existiert
|
|
|
|
|
hash.key?(:defcon) #=> true
|
|
|
|
|
hash.value?(3) #=> true
|
|
|
|
|
|
|
|
|
|
# Tipp: Arrays und Hashes sind Enumerables!
|
|
|
|
|
# Sie haben viele nützliche Methoden gemein, wie each, map, count, und andere.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
|
|
|
|
# Kontrolstrukturen
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Bedingungen
|
2015-10-16 20:09:14 +03:00
|
|
|
|
if true
|
2020-05-21 01:07:24 +03:00
|
|
|
|
'wenn Bedingung'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
elsif false
|
2020-05-21 01:07:24 +03:00
|
|
|
|
'sonst wenn, optional'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
else
|
2020-05-21 01:07:24 +03:00
|
|
|
|
'sonst, auch optional'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Wenn eine Kontrollstruktur keinen Code-Block, sondern einen einzigen
|
|
|
|
|
# Ausdruck ausführt, dann kannst du die nachgestellte if-Notation verwenden
|
|
|
|
|
warnings = ['Nachname fehlt', 'Adresse zu kurz']
|
|
|
|
|
puts("Vorhandene Warnungen:\n" + warnings.join("\n")) if !warnings.empty?
|
|
|
|
|
|
|
|
|
|
# Formuliere die Bedingung um, wenn sich `unless` besser liest als `if`
|
|
|
|
|
puts("Vorhandene Warnungen:\n" + warnings.join("\n")) unless warnings.empty?
|
|
|
|
|
|
|
|
|
|
# Schleifen
|
|
|
|
|
# Traditionell ist das Benutzen von `for` Schleifen in Ruby eher unüblich.
|
|
|
|
|
# Stattdessen werden diese mit Hilfe von Enumerables implementiert, was mit
|
|
|
|
|
# dem Aufrufen von `each` einhergeht.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
(1..5).each do |counter|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts "Iteration #{counter}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Was in etwa das selbe ist wie Folgendes (selten in Ruby zu sehen).
|
|
|
|
|
for counter in 1..5
|
|
|
|
|
puts "Iteration #{counter}"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Das `do |variable| ... end` Konstrukt wird `block` genannt.
|
|
|
|
|
# Blocks sind vergleichbar mit Lambdas, anonymen Funktionen
|
|
|
|
|
# oder Closures in anderen Programmiersprachen.
|
|
|
|
|
# Sie können als Objekte übergeben, aufgerufen oder als Methoden
|
|
|
|
|
# zugewiesen werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Die `each` Methode eines Ranges führt den Block einmal für jedes
|
|
|
|
|
# Element des Ranges aus.
|
|
|
|
|
# Dem Block wird eine counter Variable als Parameter übergeben.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Du kannst einen Block auch mit geschweiften Klammern schreiben.
|
|
|
|
|
(1..5).each { |counter| puts "Iteration #{counter}" }
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Each kann auch über den Inhalt von Datenstrukturen iterieren.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
array.each do |element|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts "#{element} is Teil des Arrays"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
hash.each do |key, value|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts "#{key} ist #{value}"
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Um auf den Laufindex zuzugreifen kannst du `each_with_index` verwenden
|
|
|
|
|
# und eine index Variable definieren.
|
|
|
|
|
array.each_with_index do |element, index|
|
|
|
|
|
puts "#{element} ist Nummer #{index} im Array"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
counter = 1
|
|
|
|
|
while counter <= 5 do
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts "Iteration #{counter}"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
counter += 1
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
#=> Iteration 1
|
|
|
|
|
#=> Iteration 2
|
|
|
|
|
#=> Iteration 3
|
|
|
|
|
#=> Iteration 4
|
|
|
|
|
#=> Iteration 5
|
|
|
|
|
|
|
|
|
|
# Es gibt einige andere hilfreiche Schleifenfunktionen in Ruby.
|
|
|
|
|
# Wie etwa 'map', 'reduce', 'inject' und viele andere mehr.
|
|
|
|
|
# Map zum Beispiel iteriert über das Array, führt für jedes Element
|
|
|
|
|
# die Anweisungen aus,
|
|
|
|
|
# die im Block definiert sind und gibt ein völlig neues Array zurück.
|
|
|
|
|
array = [1,2,3,4,5]
|
|
|
|
|
doubled = array.map do |element|
|
|
|
|
|
element * 2
|
|
|
|
|
end
|
|
|
|
|
puts doubled
|
|
|
|
|
#=> [2,4,6,8,10]
|
|
|
|
|
puts array
|
|
|
|
|
#=> [1,2,3,4,5]
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Case Konstruct
|
2015-10-16 20:09:14 +03:00
|
|
|
|
grade = 'B'
|
|
|
|
|
|
|
|
|
|
case grade
|
|
|
|
|
when 'A'
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'So wird’s gemacht'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
when 'B'
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Viel Glück beim nächsten Mal'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
when 'C'
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Das kannst du besser'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
when 'D'
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Gerade so durch'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
when 'F'
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Durchgefallen!'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
else
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Anderes Bewertungssystem, was?'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
#=> "Viel Glück beim nächsten Mal"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Case kann auch Ranges benutzen
|
2015-10-16 20:09:14 +03:00
|
|
|
|
grade = 82
|
|
|
|
|
case grade
|
|
|
|
|
when 90..100
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Hurra!'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
when 80...90
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'OK gemacht'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
else
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Durchgefallen!'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
#=> "OK gemacht"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Fehlerbehandlung
|
2015-10-16 20:09:14 +03:00
|
|
|
|
begin
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Code der einen Fehler wirft...
|
|
|
|
|
raise NoMemoryError, 'Dein Speicher ist voll.'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
rescue NoMemoryError => exception_variable
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'NoMemoryError ist aufgetreten', exception_variable
|
2015-10-16 20:09:14 +03:00
|
|
|
|
rescue RuntimeError => other_exception_variable
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'RuntimeError ist aufgetreten'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
else
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Das wird ausgeführt, wenn keine Fehler geworfen wurden'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
ensure
|
2020-05-21 01:07:24 +03:00
|
|
|
|
puts 'Dieser Code wird immer ausgeführt, egal was vorher passiert'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Methoden
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def double(x)
|
|
|
|
|
x * 2
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Methoden (und Blocks) geben implizit den Wert des letzten Anweisung zurück.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
double(2) #=> 4
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Klammern sind optional wenn die Anweisung dadurch nicht mehrdeutig wird.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
double 3 #=> 6
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
double double 3 #=> 12
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def sum(x, y)
|
|
|
|
|
x + y
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Die Argumente einer Methode werden durch ein Komma getrennt.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
sum 3, 4 #=> 7
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
sum sum(3, 4), 5 #=> 12
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# yield
|
|
|
|
|
# Alle Methoden haben implizit einen optionalen block Parameter.
|
|
|
|
|
# Dieser kann durch das Schlüsselwort 'yield' ausgeführt werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def surround
|
|
|
|
|
puts '{'
|
|
|
|
|
yield
|
|
|
|
|
puts '}'
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
surround { puts 'hallo Welt' }
|
|
|
|
|
|
|
|
|
|
#=> {
|
|
|
|
|
#=> hallo Welt
|
|
|
|
|
#=> }
|
|
|
|
|
|
|
|
|
|
# Blocks können in ein 'Proc' Objekt umgewandelt werden.
|
|
|
|
|
# Dieses ist eine Art Container um den Block und erlaubt ihn an eine
|
|
|
|
|
# andere Methode zu übergeben, ihn in einen anderen Gültigkeitsbereicht
|
|
|
|
|
# einzubinden oder ihn andersweitig zu verändern.
|
|
|
|
|
# Am häufigsten findet man dies bei Parameterlisten von Methoden, in Form
|
|
|
|
|
# eines letzten '&block' Parameters, der den Block – wenn es einen gibt –
|
|
|
|
|
# entgegen nimmt und ihn in ein 'Proc' umwandelt. Die Benennung '&block' ist
|
|
|
|
|
# hier nur eine Konvention; es würde genauso mit '&pineapple' funktionieren.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def guests(&block)
|
2020-05-21 01:07:24 +03:00
|
|
|
|
block.class #=> Proc
|
|
|
|
|
block.call(4)
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Die 'call' Methode eines Proc ist ganz ähnlich zum Aufruf von 'yield', wenn
|
|
|
|
|
# ein Block vorhanden ist. Die Argumente, die 'call' übergeben werden, werden
|
|
|
|
|
# als Argumente and den Block weitergereicht.
|
|
|
|
|
|
|
|
|
|
guests { |n| "Du hast #{n} Gäste." }
|
|
|
|
|
# => "Du hast 4 Gäste."
|
|
|
|
|
|
|
|
|
|
# Du kannst eine Liste von Argumenten übergeben, die dann in ein Array
|
|
|
|
|
# umgewandelt werden. Dafür gibt es den splat-Operator (`*`).
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def guests(*array)
|
|
|
|
|
array.each { |guest| puts guest }
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Destrukturierung
|
|
|
|
|
|
|
|
|
|
# Ruby destrukturiert Arrays automatisch beim Zuweisen mehrerer Variablen.
|
|
|
|
|
a, b, c = [1, 2, 3]
|
|
|
|
|
a #=> 1
|
|
|
|
|
b #=> 2
|
|
|
|
|
c #=> 3
|
|
|
|
|
|
|
|
|
|
# In manchen Fällen will man den splat-Operator (`*`) verwenden um ein Array in
|
|
|
|
|
# eine Liste zu destrukturieren.
|
|
|
|
|
ranked_competitors = ["John", "Sally", "Dingus", "Moe", "Marcy"]
|
|
|
|
|
|
|
|
|
|
def best(first, second, third)
|
|
|
|
|
puts "Gewinner sind #{first}, #{second} und #{third}."
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
best *ranked_competitors.first(3) #=> Gewinner sind John, Sally and Dingus.
|
|
|
|
|
|
|
|
|
|
# Der splat-Operator kann auch in Parametern verwendet werden.
|
|
|
|
|
def best(first, second, third, *others)
|
|
|
|
|
puts "Gewinner sind #{first}, #{second} und #{third}."
|
|
|
|
|
puts "Es gab #{others.count} andere Teilnehmer."
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
best *ranked_competitors
|
|
|
|
|
#=> Gewinner sind John, Sally und Dingus.
|
|
|
|
|
#=> Es gab 2 andere Teilnehmer.
|
|
|
|
|
|
|
|
|
|
# Per Konvention enden alle Methoden, die einen Wahrheitswert zurück geben, mit einem
|
|
|
|
|
# Fragezeichen.
|
|
|
|
|
5.even? #=> false
|
|
|
|
|
5.odd? #=> true
|
|
|
|
|
|
|
|
|
|
# Wenn ein Methodenname mit einem Ausrufezeichen endet, dann tut diese Methode
|
|
|
|
|
# per Konvention etwas Destruktives, wie z.B. das aufrufende Objekt zu
|
|
|
|
|
# verändern.
|
|
|
|
|
# Viele Mehtoden haben eine !-Version um eine direkte Änderung zu machen und
|
|
|
|
|
# eine Nicht-!-Version, die ein neues Objekt mit den Veränderungen zurück gibt.
|
|
|
|
|
company_name = "Dunder Mifflin"
|
|
|
|
|
company_name.upcase #=> "DUNDER MIFFLIN"
|
|
|
|
|
company_name #=> "Dunder Mifflin"
|
|
|
|
|
# Diesmal verändern wir company_name direkt.
|
|
|
|
|
company_name.upcase! #=> "DUNDER MIFFLIN"
|
|
|
|
|
company_name #=> "DUNDER MIFFLIN"
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
# Klassen
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Du kannst eine Klasse mit dem Schlüsselwort 'class' definieren.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Human
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Eine Klassenvariable. Sie wird von allen Instanzen einer Klasse geteilt.
|
|
|
|
|
@@species = 'H. sapiens'
|
|
|
|
|
|
|
|
|
|
# Konstruktor bzw. Initializer
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def initialize(name, age = 0)
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Weise das Argument der Instanzvariable 'name' zu.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
@name = name
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Wenn kein 'age' angegeben wurde wird der Standartwert aus der Argumentenlist verwendet.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
@age = age
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Setter Methode
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def name=(name)
|
|
|
|
|
@name = name
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Getter Methode
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def name
|
|
|
|
|
@name
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Getter & Setter können auch kürzer mit der attr_accessor Methode erstellt werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
attr_accessor :name
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Getter & Setter Methoden können auch einzeln erstellt werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
attr_reader :name
|
|
|
|
|
attr_writer :name
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Eine Klassenmethode unterscheidet sich durch ein 'self' von einer
|
|
|
|
|
# Instanzmethode.
|
|
|
|
|
# Sie kann nur auf der Klasse und nicht auf einer Instanz der Klasse
|
|
|
|
|
# aufgerufen werden.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def self.say(msg)
|
|
|
|
|
puts msg
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def species
|
|
|
|
|
@@species
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Instanziieren einer Klasse
|
2015-10-16 20:09:14 +03:00
|
|
|
|
jim = Human.new('Jim Halpert')
|
|
|
|
|
dwight = Human.new('Dwight K. Schrute')
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Du kannst die Methoden des erstellten Objekts aufrufen.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
jim.species #=> "H. sapiens"
|
|
|
|
|
jim.name #=> "Jim Halpert"
|
|
|
|
|
jim.name = "Jim Halpert II" #=> "Jim Halpert II"
|
|
|
|
|
jim.name #=> "Jim Halpert II"
|
|
|
|
|
dwight.species #=> "H. sapiens"
|
|
|
|
|
dwight.name #=> "Dwight K. Schrute"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Aufrufen einer Klassenmethode
|
2015-10-16 20:09:14 +03:00
|
|
|
|
Human.say('Hi') #=> "Hi"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Der Gültigkeitsbereich einer Variablen wird durch ihren Namen definiert.
|
|
|
|
|
# Variablen, die mit $ beginnen sind global gültig.
|
|
|
|
|
$var = "Ich bin eine globale Variable"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
defined? $var #=> "global-variable"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Variablen, die mit @ beginnen, sind innerhalb einer Instanz gültig.
|
|
|
|
|
@var = "Ich bin eine Instanzvariable"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
defined? @var #=> "instance-variable"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Variablen, die mit @@ beginnen, sind für die Klasse gültig.
|
|
|
|
|
@@var = "Ich bin eine Klassenvariable"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
defined? @@var #=> "class variable"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Variablen, die mit einem Großbuchstaben beginnen, sind Konstanten
|
|
|
|
|
Var = "Ich bin eine Konstante"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
defined? Var #=> "constant"
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Class ist in Ruby auch ein Objekt. Deshalb kann eine Klasse Instanzvariablen
|
|
|
|
|
# haben. Eine Klassenvariable wird zwischen der Klasse und all ihren
|
|
|
|
|
# Ableitungen geteilt.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Basis Klasse
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Human
|
|
|
|
|
@@foo = 0
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def self.foo
|
|
|
|
|
@@foo
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def self.foo=(value)
|
|
|
|
|
@@foo = value
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
# Abgeleitete Klasse
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Worker < Human
|
|
|
|
|
end
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
Human.foo #=> 0
|
|
|
|
|
Worker.foo #=> 0
|
|
|
|
|
|
|
|
|
|
Human.foo = 2
|
|
|
|
|
Worker.foo #=> 2
|
|
|
|
|
|
|
|
|
|
# Ableitungen einer Klasse haben keinen Zugriff auf eine Eine Klassen-Instanzvariable.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Human
|
|
|
|
|
@bar = 0
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def self.bar
|
|
|
|
|
@bar
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
def self.bar=(value)
|
|
|
|
|
@bar = value
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Doctor < Human
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
Human.bar #=> 0
|
|
|
|
|
Doctor.bar #=> nil
|
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
module ModuleExample
|
|
|
|
|
def foo
|
|
|
|
|
'foo'
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
# Ein Einbinden (include) eines Moduls bindet seine Methoden an die Instanzen
|
|
|
|
|
# der Klasse.
|
|
|
|
|
# Ein Erweitern (extend) eines Moduls bindet seine Methoden an die Klasse
|
|
|
|
|
# selbst.
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Person
|
|
|
|
|
include ModuleExample
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
2015-10-16 20:09:14 +03:00
|
|
|
|
class Book
|
|
|
|
|
extend ModuleExample
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
Person.foo #=> NoMethodError: undefined method `foo' for Person:Class
|
|
|
|
|
Person.new.foo #=> "foo"
|
|
|
|
|
Book.foo #=> "foo"
|
|
|
|
|
Book.new.foo #=> NoMethodError: undefined method `foo'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Callbacks werden ausgeführt, wenn ein Modul eingebunden oder erweitert wird.
|
|
|
|
|
module ConcernExample
|
|
|
|
|
def self.included(base)
|
|
|
|
|
base.extend(ClassMethods)
|
|
|
|
|
base.send(:include, InstanceMethods)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
module ClassMethods
|
|
|
|
|
def bar
|
|
|
|
|
'bar'
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
|
|
|
|
|
module InstanceMethods
|
|
|
|
|
def qux
|
|
|
|
|
'qux'
|
|
|
|
|
end
|
2015-10-16 20:09:14 +03:00
|
|
|
|
end
|
2020-05-21 01:07:24 +03:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
class Something
|
|
|
|
|
include ConcernExample
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
Something.bar #=> "bar"
|
|
|
|
|
Something.qux #=> NoMethodError: undefined method `qux'
|
|
|
|
|
Something.new.bar #=> NoMethodError: undefined method `bar'
|
|
|
|
|
Something.new.qux #=> "qux"
|
2015-10-16 20:09:14 +03:00
|
|
|
|
```
|
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
## Weitere Links
|
2015-10-17 15:39:21 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
_(z.T. auf Englisch)_
|
2015-10-16 20:09:14 +03:00
|
|
|
|
|
2020-05-21 01:07:24 +03:00
|
|
|
|
- [Offizielle Ruby Website](https://www.ruby-lang.org/de/)
|
|
|
|
|
- [Learn Ruby by Example with Challenges](http://www.learneroo.com/modules/61/nodes/338) - Eine Variante dieses Dokuments mit in-Browser Challenges.
|
|
|
|
|
- [RubyMonk](https://rubymonk.com/) - Lerne Ruby mit einer Reihe interaktiver Tutorials.
|
|
|
|
|
- [Offizielle Dokumentation](http://ruby-doc.org/core)
|
2015-10-16 20:09:14 +03:00
|
|
|
|
- [Ruby from other languages](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/)
|
2020-05-21 01:07:24 +03:00
|
|
|
|
- [Programming Ruby](http://www.amazon.com/Programming-Ruby-1-9-2-0-Programmers/dp/1937785491/) - Eine ältere [freie Ausgabe](http://ruby-doc.com/docs/ProgrammingRuby/) ist online verfügbar.
|
|
|
|
|
- [Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide) - Ein von der Community erstellter Ruby coding style guide.
|
|
|
|
|
- [Try Ruby](http://tryruby.org) - Lerne die Grundlagen der Ruby Programmiersprache, interaktiv im Browser.
|