---
theme: gaia
paginate: true
title: Ruby-Schulung Linuxhotel Oktober 2026
---
<style>
  section {
    background: #fefefe;
    color: #004;
  }
  section.invert {
    background: #004;
    color: #fefefe;
  }

  section.lead h1 {
    font-size: 4em;
  }

  section.small p {
    font-size: 0.6em;
  }

  section marp-pre {
    background: #002;
    border-radius: 5px;
  }

  section code {
    background: #002;
    border-radius: 5px;
  }

</style>

# Willkommen zur
## Ruby-Schulung
## im Linuxhotel
### 12.10.2026

---
<!-- class: lead invert -->
# Vorstellung

---
<!-- class: -->
# Jan Krutisch

- Freier Software-Entwickler und [Gründer](https://depfu.com)
- Hamburg
- Ruby/ Rails seit 2005
- Projekte in allen Formen und Farben

---

# Jetzt Ihr

- Name
- Vorkenntnisse (Auch: Andere Programmiersprachen)
- Kursziele, ggf. Anwendungsfeld

---

# Was uns erwartet

- 5 volle Tage
- Viel Praxis, viel Pausen
- Viele Fragen, bitte!

---

# Das wichtigste

- Editor der Wahl
- Das Pad

---

# Themen (Grob)

1. Tag: Einführung, Installation, Grundlagen
2. Tag: Fortsetzung, der Kern von Ruby
3. Tag: Objektorientierung
4. Tag: Die Standard-Library, Rubygems, das Web
5. Tag: This page intentionally left blank

---

# Try Ruby & Registrierung

- https://try.ruby-lang.org/
- Registrierung für lab.linuxhotel.de
---

# ...

---
<!-- class: lead invert -->
# Ruby

---
<!-- class: -->
# Ruby
## Entwickelt von Yukihiro "Matz" Matsumoto ab 1995
## Bekannt in Europa und USA seit ca. 2002
## Große Popularität im Westen dank Ruby on Rails ab 2004/ 2005
---

# Design-Goals:
- Developer Happiness
- Von Beginn an Objektorientiert
- Ideen aus Perl, Lisp und Smalltalk

---

# Offizielle Implementierung:
## MRI ("Matz' Ruby-Interpreter")

---

# Diverse andere Implementierungen:
## JRuby (JVM)
## TruffleRuby
## DragonRuby
## mruby
## ...

---

# Ruby ist...
## ...objektorientiert
## ...elegant
## ...lesbar
## ...schön?

---

# Beispiele

```ruby
3.times do
  print "Welcome"
end
```

```ruby
20.days.from_now
# (Nicht wirklich ruby)
```

---

# Installation

---

# Viele Wege führen nach Rom

---

# Windows

## https://rubyinstaller.org/

---

# Mac

## homebrew

---

# Linux

## Paketmanager

---
# Aber

![w:800](images/ruby-on-ubuntu-lts.png)

# vs.

![w:800](images/ruby-release-schedule.png)

---

# Generic Version Managers
## asdf, mise, proto, aqua

# Ruby Version Managers
## rbenv, rvm

---

# New kid on the block

## rv

---

# Für heute:

```bash
mise install ruby@3.4.10
mise use ruby@3.4.10
```

---

# `hello_world.rb`

```ruby
#!/usr/bin/env ruby
puts "Hello World"
```

---
# Anatomie eines Ruby-Skripts
- "shebang" `#!/usr/bin/env ruby`
- Einrückung: 2 Leerzeichen
- Nicht vergessen: `chmod +x`
- Kommentare starten mit # (wie bash, python etc.)

---

# Über Ruby aufrufen

(Dann kein shebang nötig)

`$ ruby script.rb`

---

# Zeichenkodierung

- UTF-8 hat gewonnen (Code ist UTF-8 per default, seit Ruby 2.0.0)
- Strings haben eigenes encoding (UTF-8 in meisten Fällen default)
- Umfangreiche Funktionen für Transcoding

---

# Syntax

---

# Eine Anweisung pro Zeile, ohne zwingendes Semikolon

```ruby
puts "Eins"
puts "Zwei"
```
---

# Semikolons können verwendet werden, sehr unüblich

```ruby
puts "Eins"; puts "Zwei"
```
---

# IRB

`$ irb`

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis



---
<!-- class: -->
# Erste Schritte

- Schreibe ein Programm, das auf drei getrennten Zeilen auf der Konsole eine Ausgabe macht.
- Führe das Programm testweise in der Konsole aus.
- Lade Dein Programm in der IRB mit Hilfe von require hinzu. Was passiert?

---

# Variablen

## Lokale Variable

```ruby
a = 1
```

---

# Variablen

- Muss mit kleinem Buchstaben anfangen (Ausnahmen!)
- Üblich: snake_case
- Möglich: Fast alles (`🎂 = "lie"`)
- Keine Deklaration
- Keine Typdeklaration
---

# Konstanten

```ruby
EPOCH_YEAR = 1970
```

---

# Konstanten
- Sind nicht konstant
- Müssen mit großem Buchstaben anfangen
- Üblich: SNAKE_CASE
- Auch hier: Emojis sind möglich

---

# Globale Variablen

```ruby
$foo = "bar"
```

---

# Globale  Variablen

- Nein
- Nein
- Nein

---

# Datentypen

---

# Literale

```ruby
nil                         # Nichts
1, 300_000, 3.14, 2.4e-20   # Zahlen
'hallo', "hallo", %q(hallo) # Zeichenketten
:status, :through           # Symbole
true, false                 # Wahrheitswerte
10..20, 10...20, 'a'..'z'   # Bereiche
/pattern/, /^\d+/           # Reguläre Ausdrücke (Regexp)
```

---

# Zeichenketten

```ruby
a = 'aha'
b = "soso"
b = "jaja\nnundenn"
c = "->#{b}<-"
d = %Q("#{a}")
```
---

# Symbole
- Global einmalig
- unveränderbar
- Parameter, Konfiguration, etc. etc.

---
# Alles ist ein Objekt
```ruby
true.class # => TrueClass
2.class    # => Integer
2.2.class  # => Float
"hello".class # => String
```

---

# Alles ist ein Objekt
- Datentypen sind Klassen
- Werte sind Objekte dieser Klassen
- Weitgehende Introspektion (`2.class.superclass # => Numeric`)

---

# Objekte haben Methoden

```ruby
5.next              # 6
'hallo'.length      # 5
'hallo'.chop        # "hall"
:name.is_a? Symbol  # true
```

---

# Klammern um Argumente?

```ruby
'hallo'.length()
:name.is_a?(Symbol)
```

---

# Typ-Umwandlung

```ruby
23.to_s    # "23"
"42".to_i  # 42
"42".to_f  # 42.0
"3.3".to_f # 3.3
"abc".to_i # 0
```
---

# Aha!

```ruby
23.to_s(16)    # "17"
"abc".to_i(16) # 2748
```

---

# "Globale" Methoden

(Dokumentation: [Kernel](https://ruby-doc.org/4.0.6/Kernel.html))

```ruby
puts "Hallo" # Zeile ausgeben

a = gets     # Zeile einlesen
```

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis

---
<!-- class: -->
# Nachfolger

Schreibt ein Programm, das den Benutzer auﬀordert, eine Ganzzahl einzugeben.
Anschließend soll das Programm den Nachfolger dieser Ganzzahl ausgeben.

---

# Operatoren


```ruby
5 + 1               # 6
5 / 2               # 2 (Achtung!)
5.0 / 2.0           # 2.5
"Hello" + " World"  # "Hello World"
true && true        # true
true and false      # false
true || false       # true
false or true       # true
!true               # false
not false           # true
```

---

---
# Kurzformen

Aus Operator und Zuweisung lassen sich Kurzformen
bilden

```ruby
a = a + 1
a += 1
a ||= 5
a = a || 5
```

---
# Eigene Methoden Definieren

```ruby
def add(a, b)
  return a + b
end
```
---
# Methoden
- Benennung wie lokale Variablen
- Zusätzlich gewisse Sonderzeichen erlaubt
- Beliebige Anzahl Parameter möglich

---
# Sonderzeichen in Methodennamen

```ruby
  full? # Wahrheitswert erwartet
  reset! # Seiteneffekte, In-Place, Achtung!
```
---
# Zuweisungen/ "Attribute"
```ruby
  car.plate=("XUF 203")
  car.plate = "XUF 203"
  puts car.plate()
  puts car.plate
```
---

# Operatoren
```ruby
def +(b)
  # ...
end

2.+(3)
2 + 3

```
---

# Default-Parameter

```ruby
def add(a, b = 1)
  return a + b
end
add(3)    # 4
add(3, 5) # 8
```
---

# Benannte parameter
## ("keyword args")

```ruby
def format_currency(amount:, currency: 'EUR')
  formatted_amount = sprintf('%.2f', amount)
  "#{formatted_amount} #{currency}"
end
format_currency(amount: 23) # 23.00 EUR
format_currency(currency: '$', amount: 6) # 6.00 $
```

---

# Rückgabewerte

```ruby
def add(a, b) # "Ruby Style"
  a + b
end

def add(a, b) # explizit
  return a + b
end
```

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis

---
<!-- class: small -->
# Hello World & Quadratzahlen

Schreibt eine hello-world-Methode, die einen
personalisierten Gruß ausgibt. Dazu soll ein Parameter
“name” übergeben werden. Anschließend soll der Gruß
“Hallo <name>” ausgegeben werden, wobei <name>
durch den übergebenen Namen ersetzt wird.

Schreibt eine Methode, um Quadratzahlen zu berechnen.
Als Parameter soll eine Zahl übergeben werden.
Rückgabewert soll das Quadrat der Zahl sein.
Schreibt jeweils einen Aufruf der Methode, der die
Funktionsweise testet und das Ergebnis auf der Konsole ausgibt.


---
<!-- class: -->
# Kontrollstrukturen

---
# Fallunterscheidungen

```ruby
if a > 1000
  puts "zu groß"
elsif a == 23
  puts "nicht erlaubter Wert"
else
  puts "korrekte Eingabe"
end
```
---

# Kompakte version

```ruby
  puts "OK" if a == 23
```
---
# Ternär

```ruby
(a == 2) ? "zwei" : a.to_s
```
---
# Unless

```ruby
return 20 unless status == 200
```

---

# Unless

- Eignet sich gut für "Guards"
- Bitte nicht:
  - Mit komplexen boolschen Ausdrücken (Knoten im Kopf garantiert)
  - Mit else kombinieren

---


# Bedingungen

Beliebige Ruby-Ausdrücke können als Bedingungen
benutzt werden
- `true` ist wahr
- `false` ist unwahr
- `nil` ist unwahr
- Alles andere (auch `0`, `""` etc.) ist wahr
- Kombination mit `&&` und `||` und `!`
- (Theoretisch auch mit `and` und `or` und `not`)
- Klammern nur für Präzendenz.

---

# Alles ist ein Ausdruck

```ruby
a = if b == 1
  'Eins'
elsif b == 2
  'Zwei'
else
  b.to_s
end
```
---

# Und wenn dann?!?

```ruby
a = if false
  'Falsch'
end

# => a = nil
```

---
# Vergleiche

```ruby
2 == 2          # true
3 == 3.0        # true
3.equal? 3.0    # false

2 != 3          # true
3 > 2           # true
```

---
# Fallunterscheidungen

```ruby
case a
when 1
  'Eins'
when 2
  'Zwei'
else
  a.to_s
end
```
---

# Fallunterscheidungen

```ruby
case var
when Fixnum, Float
  'Zahlwert'
when String
  'Zeichenkette'
else
  'Unbekannter Datentyp'
end
```
---
# Drei =

```ruby
4 === 4         # true
Integer === 4   # true
2 === 2.0       # true
4 === Integer   # false
```

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)

# Praxis
---
<!-- class: -->
# Taschenrechner

Schreibt ein Taschenrechnerprogramm, das den Nutzer
nacheinander auﬀordert eine Zahl, einen Operator und
dann noch eine Zahl einzugeben.
Das Programm soll anhand des Operators die passende
Rechnung mit den beiden Zahlen durchführen und das
Ergebnis ausgeben.

---
# Schleifen
```ruby
loop do
  puts "I WON'T STOP!"
end
```
---
# Mehr Schleifen

```ruby
while true
  puts "Do something"
end

until false
  puts "Do something"
end
```

# Kontrolle
```ruby
loop do
  next if something?  # nächste iteration
  break if something? # exit
  redo if something?  # iteration wiederholen
end
```
---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis

---
<!-- class: -->
# Ratespiel

Schreibt ein Zahlratespiel, das eine zufällige Zahl von 1 bis
10 (oder 100, oder …) auswählt und den Benutzer so lange
die Zahl raten lässt, bis er korrekt geraten hat.
Bei jedem Durchlauf soll angezeigt werden, um den
wievielten Versuch es sich handelt. Anschließend soll der
Nutzer einen Tipp abgeben können. Das Programm gibt aus,
ob der Tipp korrekt war, oder ob die gesuchte Zahl kleiner
oder größer ist als der abgegebene Tipp.
Ist der Tipp korrekt, beendet sich das Programm.
Andernfalls wird wieder der Versuch angezeigt und um
einen neuen Tipp gebeten. Usw.

---
# Arrays

Sortierte Liste von Werten

```ruby
a = [1, 2, 5, 6]
a[0]                            # => 1
a = ["hallo", 1, 3.8, :name]
a << 3
a.push(3)
a.pop                           # => 3
a.first                         # "hallo"
```
---

# Hashes

## "Assoziative Arrays"
## "HashMaps"
## Schlüssel/Wert-Paare

---

# Hashes

# Allgemeine Syntax

```ruby
h = {:name => "Otto Mustermann", :strasse => "Musterstraße 22", :ort => "Musterstadt"}
```

# Wenn Schlüssel Symbols sind
```ruby
h = {name: "Otto Mustermann", strasse: "Musterstraße 22", ort: "Musterstadt" }
h[:name] \# "Otto Mustermann"
```

---
# Code blocks

```ruby
loop { puts "Endlos!" }

5.times do |i|
  puts i
end
```

---
# Iteratoren

```ruby
a = [1, 2, 3]
a.each { |element| puts element }
a.each_with_index do |element, i|
  puts "#{i}. #{element}"
end
```
---
# Iteratoren

```ruby
h = {eins: 1, zwei: 2, drei: 3}
h.each_key { |key| puts key }
h.each { |key, value| puts "#{key}: #{value}" }
```


# Select & Map
```ruby
a = [1, 2, 3, 4, 5, 6]
a.select {|e| e > 3}   # [4, 5, 6]
a.map {|e| e * 2}      # [2, 4, 6, 8, 10, 12]
```
---
# API-Dokumentationen Lesen lernen

[ruby-doc.org](https://ruby-doc.org/4.0.6/)

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)

# Praxis

---
<!-- class: -->
# API-Doku lesen

- Lest die API-Dokumentation von String, Array und/oder
Hash.
- Sucht Euch eine Methode aus, die Euch interessant
erscheint und lernt ihre Funktionsweise.
- Präsentiert Eure Methode.

---
# Code blocks als objekte

- Objekte vom Typ Proc
- Erzeugen mit `Proc.new`, `proc`, `lambda` oder `->`
- `lambda` / `->`
  - parameter-checks
  - Verhalten von `return`
- `call` zum ausführen

---

# Blöcke benutzen

```ruby
def benchmark
  a = Time.now
  yield
  puts Time.now - a
end

benchmark do
  1000000.times { "23000".to_i }
end
```

---

# Block?

```ruby
def frame(content = "")
  content = yield if block_given?
  border = "-" * (content.size + 4)
  puts border
  puts "| #{content} |"
  puts border
end

frame("hallo")
frame { "zufall#{rand(1009)}" }
```

---

# Block "durchreichen"

```ruby
def twice(&block)
  2.times &block
end

twice { puts "hallo" }
```

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis

---
<!-- class: -->
# Adressdatenbank

Details im Pad

---
<!--------------------------------------------------------------------------------- DAY 2 -->
# Dienstag

---
# Weitere Klassen aus Core

---

# Reguläre Ausdrücke

- Formaler Weg um Muster in Zeichenketten zu
beschreiben
- Mit einem Muster kann man suchen, validieren, parsen
und und und…
- Starker theoretischer Background
- Viel Verwendung in Perl und auf der Shell
- In Ruby Objekte

---

# Einfache Ausdrücke

```ruby
/a/
/hallo/
regexp = /test/
a = "hal"
/#{a}lo/ # /hallo/
```
---

# Pattern matching
```ruby
"hallo" =~ /a/    # => 1
"hallo" =~ /l/    # => 2
"hallo" =~ /test/ # => nil
"hallo" !~ /p/    # => true

if "hallo" =~ /allo/
  puts "Selber Hallo"
end
```

---

# Objektorientiert
```ruby
regexp = /l/
match_data = regexp.match("hallo")
match_data.size       # 1
match_data.pre_match  # "ha"
match_data.post_match # "lo"
/x/.match("hallo")    # nil
```

---
# Zeichengruppen

- Vokale: `[aeiou]`
- Großbuchstaben: `[A-Z]` (Falle: Umlaute!)
- Keine Kleinbuchstaben: `[^a-z]`
- Ziﬀern: `[0-9]`
- l,m,n,o: `[l-o]`
- Beliebiges Zeichen: `.`
- `/h.llo/` matcht auf `"hallo"`, `"hello"` usw.

---
# Quantifizierer

- Ein oder keinmal: `?`
- Beliebig oft (auch keinmal): `*`
- Beliebig oft, aber mindestens einmal: `+``
- Genau 5 mal: `{5}`
- 5 mal oder öfter: `{5,}`
- 5-10 mal: `{5,10}`


---
# Beispiel

Variablennamen in Ruby, Versuch 1:

```ruby
/[_a-z][a-zA-Z0-9_]*/
```
---

# Alternativen

eine Auswahl zwischen Alternativen wird mit `|` markiert

```ruby
/[_a-z][a-zA-Z0-9_]*(\?|!)?/
```

---
# Bereichsmarkierungen

- Zeilenanfang: `^`
- Zeilenende: `$`
- Stringanfang: `\A`
- Stringende: `\z`

---
# Beispiel

Variablennamen in Ruby, Versuch 2:
```ruby
/^[_a-z][a-zA-Z0-9\_]*$/
```

Variablennamen in Ruby, Versuch 3:

```ruby
/\A[_a-z][a-zA-Z0-9_]*\z/
```

---
# Zeichenklassen

- `\d` entspricht `[0-9]`, `\D` `[\^0-9]`
- `\s` Whitespace
- `\w` gültige Zeichen in Bezeichnern (`[a-zA-Z0-9\_]`)
- `\b` Wortgrenze

---
# Posix-Expressions

Mit Unicode-Unterstützung (Auswahl):

- `[[:alpha:]]` Buchstabe
- `[[:digit:]]` Ziﬀer
- `[[:alnum:]]` alphanumerisches Zeichen

---
# Beispiel

Variablennamen in Ruby, Versuch 4:
```ruby
/\A[_a-z]\w*\z/
```

---
# Modifikatoren

Am Ende eines Ausdrucks
Mehrere Kombinierbar

- `i` steht für case-insensitive
- `m` steht für multiline
- `x` für das entfernen von Whitespace
- `o` dafür, Ersetzungen nur einmal vorzunehmen

Darüberhinaus weitere für Encoding

---

# Gier / Greed
`*` wird häuﬁg benötigt, macht aber oft nicht, was man erwartet.

Beispiel HTML-Parsen: `/\<.*>/` ﬁndet nicht einzelne Tags, sondern das gesamte Dokument
`*` ist greedy (gierig) und sucht den größtmöglichen Match

`*?` sucht den kleinsten Match

---

# Arbeiten mit Matches

Für geklammerte Ausdrücke werden gematchte Strings
gespeichert ("Capture Groups")

```ruby
"hallo" =~ /h(a)ll(o)/ # => 0
$1 # => "a"
$2 # => "o"
```

---
# Objektorientiert

```ruby
date_exp = /(\d{1,2})\. (\w+) (\d{2,4})/
date_string = "23. Januar 2014"
match_data = date_exp.match(date_string)
match_data.size     # => 4
match_data[0]       # => "23. Januar 2014"
match_data[1]       # => "23"
match_data.captures # ["23", "Januar", "2014"]
```
---
# Benannte Gruppen

```ruby
exp = /(?<day>\d{1,2}). (?<month>\w+) (?<year>\d{2,4})/
string = "23. Januar 2014"
match_data = exp.match(string)
match_data[:day] # "23"
match_data[:month] # "Januar"
```
---
# Alle Fundstellen finden

```ruby
date = "23.01.2014"
date.scan(/\d+/)              # => ["23", "01", "2014"]
date.scan(/(\d{2})\.(\d{2})/) # => [["23", "01"]]
date.scan(/\d+/) { |match| puts match }
```
---
# Suchen & Ersetzen
```ruby
"hallo".sub(/l/, "test")  # => "hatestlo"
"hallo".gsub(/l/, "test") # => "hatesttesto"
"Otto Mustermann".sub(/(\w+)\s+(\w+)/, '\2, \1') # "Mustermann, Otto"
```

---
# Strings teilen

```ruby
"1,2,3".split(",") # => ["1", "2", "3"]
"1 2\t3\n4".split(/\s+/) # ["1", "2", "3", "4"]
```

---
# Dokumentation und Spielwiese

- [API-Docs](https://ruby-doc.org/3.4.1/Regexp.html)
- [Rubular.com](https://rubular.com)

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis

---
<!-- class: -->

# Taschenrechner, die Zweite

Verbessert das Taschenrechnerprogramm, indem Ihr die
Eingabe des Benutzers in einem einzigen Schritt verlangt
und mit Hilfe regulärer Ausdrücke auf ihre Gültigkeit prüft.

Hat der Benutzer nicht, wie gefordert, eine Zahl, bzw. einen
der Operatoren `+`, `-`, `*` und `/` eingegeben, soll keine
Berechnung erfolgen, sondern eine Fehlermeldung
ausgegeben werden.

---

# Kommandozeilenargumente

- Alle Kommandozeilenargumente landen in `ARGV`
- `ARGV` ist ein Array
- `ARGV.size` nennt Anzahl der Parameter
- (Anders als in C ist `ARGV[0]` nicht der Programmname)
- `OptionParser` für komplexere Fälle (`--help, -v, --verbose`)

---

```ruby
puts ARGV[0]
```
---


# Umgebungsvariablen

Umgebungsvariablen landen in der Konstanten `
`ENV` verhält sich wie ein Hash:

```ruby
ENV["HOME"] # "/home/dave"
```

---

# Shellout

```ruby
result = `uname`    # => "Linux\ņ"
system("uname -a")  # => true
```

---

# I/O

- `STDIN`, `STDOUT`, `STDERR`
- `puts` ist eigentlich `STDOUT.puts`
- `gets` ist `STDIN.gets`

---

# I/O-Operationen

- Zeilenweise: `gets`, `puts`
- Zeichenweise: `getc`, `putc`
- Mehr: `read`, `write`
- Gilt für Streams, Dateien, Netzwerk

---

# Dateien

```ruby
File.open("test.txt", "w") do |file|
  file.puts "Hallo"
end
```

“r“ead, “w“rite, “a“ppend (, “b“inary)

---
# Datei zeilenweise verarbeiten

```ruby
File.open("data.txt", "r") do |file|
  file.each_line do |line|
    puts line
  end
end
```

---

#  Helper

```ruby
File.exists?("/tmp/test.txt")   # false
File.basename("/tmp/test.txt")  # "test.txt"
File.dirname("/tmp/test.txt")   # "/tmp"
File.join("tmp", "test.txt")
# "tmp/test.txt"
Dir.mkdir("/tmp/testdir")
Dir.entries("/tmp")             # [...]
Dir.glob("/tmp/*.txt")          # [...]
```

---
<!-- class: lead -->
![bg contain opacity:0.2](images/pet.jpg)
# Praxis


---
<!-- class: -->
# Addressdatenbank die zweite

Erweitert Eure Adressdatenbank, so dass Ihr die Daten
dauerhaft abspeichern und auch wieder einlesen könnt.

---

# Datum und Uhrzeit

```ruby
t = Time.now    # 2026-10-27 18:21:04 +0200
t.year          # 2026
t + 60          # 2013-05-27 18:22:04 +0200
t.to_i          # Sekunden seit 01.01.1970
```
---
# strftime

```ruby
# %d Tag, %m Monat, %Y Jahr (vierstellig)
Time.now.strftime("%d.%m.%Y") # "25.09.2018"
```
---
<!--------------------------------------------------------------------------------- DAY 3 -->
# Mittwoch
---

# Objektorientierte Programmierung
# OO

- Stammt u.a. von Smalltalk, entwickelt im Xerox PARC
- Wird verwendet in C++, Java, Ruby, Python(, Perl, PHP …)
- Technische Vorteile, aber auch besser an den Menschen angepasst

---
# Was bedeutet Objektorientierung

- Objekte als Funktionseinheiten
- Objekte Bündeln Daten (Attribute) und Funktionen (Methoden)
- Objekte kommunizieren über Nachrichten (Methodenaufrufe)
- Vererbung als Möglichkeit der Abstraktion

---

# Ziele aus Entwicklersicht

Modellierung in Objekten entspricht eher dem
menschlichen Denken (citation needed)


---
# Ziele aus technischer Sicht

- Bessere Struktur
- Stärkere Kapselung
- Weniger Redundanz
- Wiederverwendbarkeit
- Oder zusammengefasst: Modularität

---
# Wichtige Begriffe

- Klasse (Bauplan)
- Instanz (konkretes Objekt, das nach Bauplan der Klasse
erstellt wurde)
- Instanzenvariable (Eigenschaft des Objekts)
- Instanzenmethode
- Klassenvariablen und Klassenmethoden

---
# Klassen entwerfen

- Welche Objekte soll mein System enthalten?
- Was sind die Daten, wer sind die Akteure?
- Wie sind Zuständigkeiten verteilt?
- Was kann ich möglicherweise wiederverwenden?

---

# Beispiel Musik-Player

- Klassen: Song, Artist, Album
- Song-Eigenschaften: Titel, Länge usw.
- Song-Verhaltensweisen: Abspielen, Pausieren usw.

---

# Beispiel Büromöbel

- Klassen: Möbelstück, Stuhl, Tisch
- Möbelstück-Eigenschaften: Beschreibung, Hersteller usw.
- Stuhl-Eigenschaften: Sitzhöhe, Armlehnen usw.
- Tisch-Eigenschaften: Länge, Breite, Höhe usw.
- Stuhl und Tisch sind beides Möbelstücke und teilen sich
die gemeinsamen Eigenschaften

---

# Klassen in Ruby

```ruby
class Song
  # [...]
end

s = Song.new
```

---
# Klassen in Ruby
- In einer .rb-Datei darf man beliebig viele Klassen deﬁnieren
- Best practice: Bei größeren Programmen eine Klasse pro Datei
- Code zwischen class und end wird einmalig ausgeführt (!)

---
# Methoden

```ruby
class Song
  def play
    puts "playing!"
  end
end

s = Song.new
s.play
```
---

# this is self

- `self` ist im Instanz-Kontext die **Instanz**
- `self` ist im Klassen-Kontext die **Klasse**

---

# Klassenmethoden

```ruby
class Player
  def self.play
    puts "Playing..."
  end
end

Player.play
```
---
# Konstruktor

```ruby
class Song
  def initialize(title)
  end
end
s = Song.new("Piano Man")
```
---

# Instanzvariablen
- `@instanzvariable`
- Repräsentiert internen Zustand, Zugriff von außen möglich, aber mit Absicht doof

---
# Instanzvariablen

```ruby
class Song
  def initialize(title)
    @title = title
  end

  def play
    puts "Now Playing: #{@title}"
  end
end
s = Song.new("Piano Man")
s.play
```

---

# "Attribute"

```ruby
class Song
  attr_accessor :title

  def initialize(title)
    @title = title
  end
end

s = Song.new("Piano Man")
s.title     # => "Piano Man"
s.title = "Piano Man 2"
```

---
# "Attribute"

`attr_accessor`, `attr_reader`, `attr_writer`

```ruby
class Song
  def title=(title)
    @title = title
  end

  def title
    @title
  end
end
```

---
# Klassenvariablen

- `@@songs_played` ist eine Klassenvariable
- Existiert genau einmal pro Klasse (inkl. Unterklassen)
- Übergeordnetes Wissen: Ein einzelner Song kann und muss nicht wissen, welche Songs bisher schon gespielt wurden
- Selten wirklich notwendig

---
# Praxis

---

# Bücher

Schreibt eine Klasse, die Bücher repräsentiert.
Ein konkretes Buch soll folgende Eigenschaften haben:
Titel, Autor, Seitenzahl, Preis
Alle Eigenschaften sollen von außen les- und schreibbar
sein.
Außerdem sollen sie bereits beim instanziieren von BuchObjekten übergeben werden.
Die Klasse soll nachhalten, wieviele Bücher instanziiert
wurden und eine Methode bieten, mit der die Anzahl bisher
instanziierter Bücher abgefragt werden kann.

---

# DRY: Vererbung

- Unterschiedliche Objekte können Gemeinsamkeiten aufweisen, sowohl bei Eigenschaften als auch bei Verhaltensweisen
- Auch hier bitte kein Copy & Paste
- Sind es gleichartige Objekte, kann man Vererbung nutzen
- Eine Oberklasse enthält alle Gemeinsamkeiten
- Unterklassen erben von dieser und implementieren nur noch die Unterschiede
- Vorsicht vor zu enger Kopplung
- In Ruby gibt es Alternativen

---

# Vererbung in Ruby

```ruby
class Square < Rectangle
end
```
---

# Vererbung in Ruby
- `Square` (Quadrat) erbt alle Eigenschaften von `Rectangle` (Rechteck) (Ein Quadrat ist ein Spezialfall eines Rechtecks)
- Das Überschreiben und Ergänzen von Methoden ist möglich
- Zugriﬀ auf überschriebene Methoden mit super

---

# Vererbung

```ruby
class Rectangle
  def initialize(width, height)
    @width = width
    @height = height
  end
  def area ; @width * @height ; end
end

class Square < Rectangle
  def initialize(width)
    super(width, width)
  end
end
```
---

# Sichtbarkeit

- `public` (implizit): immer aufrufbar
- `protected` : Nur aufrufbar, wenn Empfänger die selbe Klasse hat wie “self”
- `private` (Schlüsselwort) : Nur auf implizitem self aufrufbar (also nur innerhalb der selben Klasse)

---
# Praxis


---
# Kaffeemaschine


---

# Wenn's mal schief geht

Wie behandeln wir Fehlerfälle?

- In C-APIs (Und z.B. in Go): Rückgabewerte
- In OO-Sprachen (meistens): Exceptions ("Ausnahmen")

---

# Exceptions in Ruby

```ruby
raise "Uff, alles kaputt"
```

---

# Exceptions abfangen

```ruby
begin
  things_that_can_go_wrong
rescue ThingsWentWrongError => e
  puts "Things went wrong: #{e}"
ensure
  puts "we'll always get here"
end
```

---

# Eigene Exceptions

```ruby
class ThingsWentWrongError < StandardError
  attr_reader :thing
  def initialize(thing)
    @thing = thing
  end
end

raise ThingsWentWrongError.new("A Thing that went wrong")
```

# Sonderfall Methoden-Ebene

```ruby
def cannot_go_wrong
  things_that_can_go_wrong
rescue ThingsWentWrongError => e
  puts "Dammit"
end
```

---

# Detaillierte Fehlerbehandlung

```ruby
begin
  things_that_can_go_wrong
rescue ThingsWentWrongError => e
  puts "Thing went wrong"
rescue IOError => e
  puts "Something different went wrong"
end
```

---

# Retry / Re-Raise

```ruby
begin
  goes_sometimes_wrong
rescue ThingsWentWrongError
  retry
rescue FatalError
  puts "Okay, Mist"
  raise
end
```
---

# Praxis


---
# Kaffeemaschine mit Exceptions


- Entfernt alle interaktiven Elemente (puts, gets) aus der Klasse Kaﬀeemaschine.
- Ersetzt Ausgaben durch Rückgabewerte der Methoden.
- Erweitert die Klasse Kaﬀeemaschine um eine Fehlerausgabe mit Hilfe von Exceptions.
- Deﬁniert hierfür eigene Exceptions, die den drei möglichen Fehlerfällen beim Kaﬀeekochen entsprechen.
- Sorgt dafür, dass beim Auftreten eines Fehlerfalls die passende Exception geworfen wird.

---

# Module

- Code-Organisation (Namespace)
- Kapselung
- Wiederverwendung (Mixins)

---

# Module

```ruby
module MyModule
  # [...]
end
```

---

# Namespaces

```ruby
module MusicPlayer
  VERSION = "0.0.1"
  class Song
  end
end

puts MusicPlayer::VERSION
s = MusicPlayer::Song.new
```
---

# Mixins

- Besser für "cross cutting concerns" als Vererbung (Stuhl, Tisch, Benutzer)
- Verhindert unendliche Vererbungsbäume

---

# Mixins

```ruby
module Logger
  def log(msg) ; puts msg ; end
end

class User
  include Logger
end

u = User.new
u.log("hello")
```
---

# Klassenmethoden

```ruby
class System
  extend Logger
end
System.log("test")
```

---

# Benutzung erkennen

```ruby
module MyModule
  def self.included(klass)
    puts "MyModule wurde in #{klass} inkludiert"
  end
  def self.extended(klass)
    puts "#{klass} wurde durch MyModule erweitert"
  end
end
```

---

# Automatisiertes testen

- Ruby ist sehr dynamisch, keine echte Kompiler-Phase die z.B. Typfehler findet
- Automatisierte tests als Fangnetz
- Test first: Tests als Spezifikation, red-green-cycle

---

# Die Zwei Welten

- In Standard-Library: minitest
- Spec driven development: rspec

---

# Ein Test

```ruby
require 'minitest/autorun'
class SongTest < Minitest::Test
  def test_duration_in_minutes
    song = Song.new('test', 300)
    assert_equal 5, song.duration_in_minutes
  end
end
```

---
# Assertions

- `assert` / `refute`
- `assert_equal`
- `assert_instance_of`
- `assert_kind_of`
- `assert_nil`
- `refute_nil`
- `assert_raises`
- `assert_match`

---

# Setup

```ruby
class SongTest < MiniTest::Test
  def setup
    @song = Song.new('test', 300)
  end
  def test_duration_in_minutes
    assert_equal 5, @song.duration_in_minutes
  end
end
```

---

# Mocks/ Stubs

- Unit-Tests sollen immer nur eine Einheit testen
- Manche Komponenten sollten beim Testen nicht real ausgeführt werden (Bezahlsystem, Emails)
- Andere verlangsamen Tests vielleicht unnötig
- Wieder andere liefern nicht die zum Testen notwendigen reproduzierbaren Ergebnisse (Time.now !)
- Bei “Test ﬁrst”-Ansätzen existieren Komponenten möglicherweise noch gar nicht


---
# Mocks/ Stubs in Minitest

```ruby
mock = MiniTest::Mock.new
mock.expect(:to_s, 'test')
mock.to_s
mock.verify
Time.stub(:now, Time.at(1234567890)) do
  Time.now # 2009-02-14 00:31:30 +0100
end
```

---
# Test driven development - TDD

- Aus Test-ﬁrst entwickelte Vorgehensweise
- Red-Green-Refactor
- Red: Kleinen, fehlschlagenden Test schreiben
- Green: Code schreiben, bis Test erfolgreich läuft
- Refactor: Ergebnis kritisch betrachen, ggf. umstrukturieren und dabei mit Test absichern

---

# Tipps & Lebenslektionen
- Test ﬁrst / TDD ausprobieren
- Testen lohnt bei großen / langlebigen Projekten fast immer
- Wenn Testen kompliziert ist, ist das ein Hinweis, dass die Klasse zu komplex ist
- Wenn ein Bug entdeckt wird, immer erst Test schreiben, dann ﬁxen (Regressionstest)
- Auch Tests refactorn (Aber nicht zu sehr!)
- Üben, üben, üben

---
# Praxis

---
# Kaffeemaschine

---
# Testen

Schreibt Tests für Eure Kaﬀeemaschine.

---
# Code coverage

- Was ist ein guter Test?
- Metriken, z.B. wieviel Code wurde tatsächlich durchlaufen?
- Für Ruby z.B. SimpleCov (`gem install simplecov`)

---

# Debugging

- Skript debuggen: `require "debug"`
- Ausdrücke evaluieren mit `p`
- Kommandobsp.: `list`, `where`, `step`, `next`, `break`, `cont`
- Alternativ: Gems: byebug, pry

---
<!--------------------------------------------------------------------------------- DAY 4 -->
# Donnerstag

---

# Core vs. standard library vs. default gems

- Klassen aus Core stehen immer zur Verfügung (ohne require)
- Klassen der Stdlib werden mit Ruby zusamme ausgeliefert und können als gegeben betrachtet werden
- Trotzdem explizites require
- Stdlib besteht aus Klassen, die mit Ruby entwickelt werden, und vorinstallierten Gems (default gems)

---

# Standard library

- Datentypen: `BigDecimal`, `Date`, `IPAddr`, `Set`
- Parser/ Generatoren: csv, json, yaml, rexml
- Kryptographie: digest, securereandom, openssl
- Netzwerk: socket, net/http etc.
- Web: cgi, erb, webrick
- Tools: irb, rake, rdoc, debug

---
# CSV-Dateien verarbeiten

- Bibliothek ‘csv’ zum Lesen und Schreiben
- Gängige Eigenschaften können konﬁguriert werden, wie z.B.
  - Trennzeichen
  - Anführungszeichen
  - Zeilenumbruch
  - Kopfzeile
  - Encoding

---
# Lesen

```ruby
require "csv"
CSV.foreach("/tmp/test.csv") do |row|
  pp row
end
```
---
# Schreiben

```ruby
CSV.open("/tmp/test.csv", "w") do |csv|
  csv << [23,"test",11]
  csv << [42,"hallo", 12]
end
```

---

# Logger

- Simple Bibliothek zum Schreiben von Logﬁles
- Ausgabe in Datei oder Ausgabestrom
- Log-Level: debug, info, warn, error, fatal, unknown
- Sinnvolles Standardformat der Nachrichten
- Rotation von Logdateien

---

# Logger
```ruby
require 'logger'
logger = Logger.new("/tmp/logfile.log")
logger.level = Logger::WARN
logger.debug("setup complete")
logger.error("nothing much is happening...")
```

---
# Fileutils

- Methoden, die benannt sind und funktionieren wie auf der Shell
- Bsp: cp, rm, mv usw.
- Besonders nützlich: mkdir_p


---
# Rake

- Ruby-Variante des bekannten make-Tools
- sucht im aktuellen Verzeichnis automatisch nach Datei “Rakefile“
- einfache Syntax, aber volle Ruby-Power
- rake -T zum Auﬂisten aller Tasks
- Eingebaute Tasks z.B. für Tests

---
# Beispiel rakefile

```ruby
task :hello do
  puts "hello"
end
task :world do
  puts "world"
end

task :hello_world => [:hello, :world]
```

```bash
rake hello_world
```

---

# Rake testtask

```ruby
require "rake/testtask"
Rake::TestTask.new do |t|
  t.pattern = 'test/**/*_test.rb'
end
```

```bash
rake test
```

---
# Praxis


---
# Stdlib

Probiert eine oder mehrere der Bibliotheken aus der
Standard Library aus.

---
# Rubygems

- Paketverwaltung für Ruby-Programme und -Bibliotheken
- CLI ähnlich apt-get
- `gem install <paketname>`
- unzählige Pakete verfügbar
- [rubygems.org](https://rubygems.org)
- [ruby-toolbox.com](https://ruby-toolbox.com)

---

# Bundler

- Deklaration der Abhängigkeiten in `Gemfile`
- Lockfile: `Gemfile.lock` sorgt für reproduzierbare Installationen
- `bundle install`

---

# Rubygems selber bauen

- `<gemname>.gemspec` enthält deklaration
- Standardisiertes Verzeichnislayout
  - `lib` für code
  - `bin` oder `exe` für ausführbare Dateien
  - `test` für tests
- Tools, z.B. `bundle gem` zum generieren

---
# Praxis



---
# Kaffeemaschinen-Gem bauen

Erstellt ein ausführbares Programm, dass Eure
Kaﬀeemachine interaktiv benutzbar macht.
Paketiert diese Datei, die Kaﬀeemaschine und Eure Tests als
Gem.

---
# Webanwendungen mit Sinatra

- Simpler Ansatz, Webanwendungen mit Ruby zu entwickeln
- Gegenentwurf zum “schwergewichtigen” Ruby on Rails
- Sehr einfache API, ﬂexibel erweiterbar
- Ideal für einfache Anwendungen und Web-APIs
- `gem install sinatra`

---

# Sinatra DSL

```ruby
require "sinatra"
get '/' do
  "Willkommen"
end

post '/login' do
  "Willkommen #{params[:user]}"
end
```

---
# Templates

```ruby
get '/now' do
  @now = Time.now
  erb :now
end
```

```ruby
<p>Current time: <%= @now %></p>
```

---
# Layouts

- Layouts sind Templates, die ein yield enthalten
- Ideal für üblichen HTML-Rahmen, ﬁxe Header und Footer
- Default: layout.erb für ERb-Templates
- Für jedes Template konﬁgurierbar

---
# Parameter

```ruby
post '/login' do
  @username = params[:username]
  erb :welcome
end
```

# Parameter in der URL


```ruby
get '/posts/:id' do |id|
  @id = id
  erb :post
end
```

```ruby
<h3>Post: <%= @id %></h3>
```

---
# Praxis

---
# Webbasierte Kaffeemaschine

Entwickelt eine Webseite, über die sich Eure
Kaﬀeemaschine fernsteuern lässt.

---
# Fazit


---
# Jeder fängt mal klein an

```ruby
puts "Hello World"
```

---
# Erste strukturen

```ruby
def greet(name)
  puts "Hello #{name}"
end
```

---

# OO

```ruby
class Greeter
  def initialize(greeting)
    @greeting = greeting
  end

  def greet(name)
    puts "#{greeting} #{name}"
  end
end
```
---

# Ruby-Projekte

Web: rails, hanami, sinatra
Webseiten: jekyll, bridgetown
Projektmanagement: redmine, gitlab
Social Media: Mastodon
Dokumente erzeugen: prawn, hexapdf, axlsx
E-Mails: mail
HTML/XML: nokogiri, builder

---
# Und jetzt?




