Die Programmiersprache Ruby

Blog| Forum| Wiki  

Auch Ruby kennt wie jede vernünftige höhere Programmiersprache Fehlermeldungen (auch Errors oder Exceptions). Es gibt in Ruby nicht nur eine Errorart, sondern einige verschiedene, z.B. TypeError, ArgumentError, NotImplementedError und viele andere; dabei werden verschiedene Arten von Fehlern unterschieden, u.a. :

  • ScriptError: Unter dieser Klasse werden alle Exceptions zusammengefasst, die mit dem Code an sich zu tun haben, z.B. SyntaxError. Sie können standardmäßigerweise nicht aufgefangen werden[2].
  • StandardError: Die größte Exception-Gruppe, hierunter fallen die bekanntesten Fehlermeldungen wie TypeError und ArgumentError.
  • Errno::XY:Das Errno-Modul enthält betriebssystemabhängige Fehlermeldungen.
  • fatal: Eine Exception, die Ruby nur intern werfen kann. Sie diagnostiziert Fehler im Ruby-Quellcode selbst.

Eine vollständige Liste aller Exceptionklassen gibt es im Artikel zu Rubys interner Klassenstruktur.

Inhaltsverzeichnis

raise

Man kann eine Fehlermeldung von Hand werfen, dazu benutzt man die Methode Kernel#raise. Sie nimmt als erstes Argument eine Errorklasse an, die angibt, zu welchem Typ die Exception gehört, als zweites optional einen String zur genaueren Beschreibung des Fehlers und zuletzt ebenfalls optional ein Array aus Kernel#caller, das angibt, wie weit der Stack zum Auslöser des Fehlers angegeben werden soll. Der Aufruf von Kernel#caller sollte allerdings vorsichtig verwendet werden, denn wenn ein Fehler im Code eines Programms entsteht und man kein Backtrace hat, ist das Programm nur schwer zu debuggen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#Beispiel: Ohne Aufruf von Kernel#caller
def a
  raise(RuntimeError, "Error in a!")
end
def b
  a
end
def c
  b
end
c
#=>Test.rb:2:in `a': Error in a! (RuntimeError)
#=>        from Test.rb:6:in `b'
#=>        from Test.rb:10:in `c'
#=>        from Test.rb:13

#Caller ohne Argumente: 
def a
  raise(RuntimeError, "Error in a!", caller)
end
def b
  a
end
def c
  b
end
c
#=>Test.rb:6:in `b': Error in a! (RuntimeError)
#=>        from Test.rb:10:in `c'
#=>        from Test.rb:13

#Aufruf von Caller so, dass nur die Zeile im Hauptskript angegeben wird: 
def a
  raise(RuntimeError, "Error in a!", caller[caller.length - 1])
end
def b
  a
end
def c
  b
end
c
#=> Test.rb:13: Error in a! (RuntimeError)

Eigene Exceptionklassen

Reichen die von Ruby bereits vorgegebenen Klassen für Fehler nicht aus, kann man eigene definieren; wenn sie eine Subklasse von einer bereits existierenden Exceptionklasse sind, kann man sie wie jede andere Exceptionklasse benutzen:

1
2
3
4
class NoError < SyntaxError
end
raise(NoError, "No error occured!")
#=>No error occured! (NoError)

Handling

Wenn denn mal ein Fehler auftaucht, kann er mithilfe der begin/rescue/ensure[1]-Klausel abgefangen werden[2]. begin leitet den Codeblock ein, in dem Fehler abgefangen werden sollen, darauf folgt eine rescue-Anweisung, der man optional mitgeben kann, welchen Typ von Error sie auffangen kann (StandardError ist vorgegeben). Zusätzlich kann man alle Informationen zur geworfenen Exception mithilfe von => einer beliebigen Variablen zuordnen. Tut man dies nicht, sind alle Informationen zum letzten Error in der globalen Variable $! verfügbar. rescue-Anweisungen können wie eine Case-Anweisung geschachtelt werden und zusätzlich von einer ensure-Anweisung begleitet werden, die immer ausgeführt wird, egal, ob es Fehler gab oder nicht. Die else-Anweisung[3] dagegen wird nur durchlaufen, wenn keine Fehler aufgetaucht sind.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
begin
  raise "An Error?!"
rescue => var
  p "Kein Error: #{var.message}"
end
#=> "Kein Error: An Error?!"
begin
  raise "An Error?!"
rescue
  p "Kein Error: #{$!.message}"
end
#=> "Kein Error: An Error?!"

begin
  p "No Error!"
rescue ArgumentError
  #Mache irgendwas...
rescue TypeError
  #Mache irgendwas...
rescue NoMethodError
  #Mache irgendwas...
ensure
  puts "ensure..."
end
#=> "No Error!"
#=> ensure...

begin
  p "No Error!"
rescue ArgumentError
  #Mache irgendwas...
rescue TypeError
  #Mache irgendwas...
rescue NoMethodError
  #Mache irgendwas...
else
  puts "No errors occured!"
end
#=> "No Error!"
#=> No errors occured!

retry

Ist man sich sicher, dass keine Gefahr einer Endlosschleife besteht, kann der unter begin stehenden Code mithilfe des Schlüsselworts retry wiederholt werden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@variable = false
begin
  raise "Fehlschlag!" unless @variable
  puts "Funktioniert!"
rescue
  unless @variable
    @variable = true
    puts "Ein Fehlversuch!"
    retry
  else
    raise #Wenn auch beim zweiten Mal Fehlschlag, Errormeldung zeigen
  end
end
#=> Ein Fehlversuch!
#=> Funktioniert!

Änderungen an Exception

Hier werden die Änderungen an der Klasse Exception gelistet, sobald ein Feature Freeze für Ruby 1.9.1 stattgefunden hat.

Fußnoten

  • [1]Der catch/throw-Block hat nichts mit Exceptions zu tun - catch unterbricht Code und führt ihn an einer anderen Stelle weiter, wenn es dasselbe Symbol von throw erhält, das es selbst als Argument hat. Siehe Kernel#catch.
  • [2]Um spezielle Exceptions zum Beispiel vom Typ SyntaxError, welches ja eine Subklasse von ScriptError und nicht von StandardError ist, abzufangen, muss entsprechend in rescue gesetzt werden:
1
2
3
4
5
begin
  raise SyntaxError
rescue SyntaxError
  puts "SyntaxError rescued!"
end
  • [3]Die else-Anweisung muss unbedingt vor der ensure-Anweisung gebraucht werden:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#So nicht: 
begin
  puts "NoError!"
rescue
  #Mache irgendwas...
ensure
  #Mache irgendwas...
else
  #Mache irgendwas...
end
#=> syntax error, unexpected kELSE
#Sondern so: 
begin
  puts "NoError!"
rescue
  #Mache irgendwas...
else
  #Mache irgendwas...
ensure
  #Mache irgendwas...
end

Siehe auch