【Rails】例外処理の書き方(begin, rescue, raise,retry, ensure)

##例外処理とは
例外処理とは、**エラーがでたときの処理を行うことです。**
エラー原因を特定しやすくしたり、システム側で不具合が起こさないためにも、例外処理を書くことは大切です。

そこで今回は以下の使い方について簡単に紹介していきます。
**・begin**
**・rescue**
**・raise**
**・retry**
**・ensure**

僕も初めてみたときはただの暗号でしたが、使っていくうちにある程度理解してきました.
それではいきましょう。

##例外処理で使う、beginとrescueの使い方

例えば、システム内で10を0で割った後に、文字を出力する処理があった場合。

```ruby
puts 10 / 0
puts "こんにちは"
```
```
divided by 0 (ZeroDivisionError)
```
このように0で割り切ろうとすると`ZeroDivisionError`が発生し、次の処理に移ることができません。

そこで、例外処理をいれてみましょう。

```ruby

begin
  10 / 0
rescue
  p "0で割れません"
end
puts "こんにちは"
```

```
"0で割れません"
"こんにちは"
```
今度は、エラーにならずに、しっかりと最後まで処理が実行されましたね。

ここでは、
**1.エラーの対象となりそうな箇所を`begin`で囲う。**
**2.エラーが発生した場合の処理を`rescue`の中に書く。**
といった書き方をしてあげることで、エラーが発生したときの対処を可能にしました。

これで万が一エラーが起きてしまった場合にも対処することができますね。

また、beginなしでワンライナーで書く時でも使えます。

```ruby

hoge = 10 / 0 rescue 0
fuga = 10 / nil rescue 0
puts hoge
puts fuga
```

```
0
0
```
これで計算の途中でどこかに予期せぬデータが入ってエラーが発生しても`rescue`の後に指定した値を返してくれます。


##resuece => e　でエラー内容を格納
また、rescueの後に引数を指定してあげることで、変数の中にエラー内容を格納することができます。

```ruby

begin
  10 / 0
rescue => e
  puts e
end
```

```
divided by 0
```
エラーオブジェクトが出力されました。
`divided by 0`は**例外クラス**のオブジェクトであることも確認してみましょう。

```ruby

begin
  10 / 0
rescue => e
  puts e.class
  puts e.class.superclass
  puts e.class.superclass.superclass
end
```

```
ZeroDivisionError
StandardError
Exception
```

ここでいう`Exception`は、例外クラスの元となるクラスになります。(その親はObject)

##エラーごとに処理を行う

rescueの後にエラーメッセージを指定することで、
**どのエラーが出た時にどの処理をするか**,というのを条件分岐させることができます。

```ruby
begin
  10 / 0
rescue NoMethodError
  puts "そのようなメソッドはありません"
rescue ZeroDivisionError
  puts "0で割れません"
end
```

```
0で割れません
```

**1.NoMethodErrorじゃない。次のrescueへ。**
**2.ZeroDivisionErrorだ。どんな例外処理が書いてあるんだろう。(0で割れませんを出力)**
という流れで、上から順にあてはまったものから処理されていきます。

ただ注意点として、**対象の例外クラスより先にその親クラスがあった場合、その親クラスが先に実行されてしまうということ。**

実際にみてみましょう。

```ruby

begin
  10 / 0
rescue StandardError
  puts "親クラスは先に実行されるよ"
rescue ZeroDivisionError
  puts "0で割れません"
end
```

```
親クラスは先に実行されるよ
```
`ZeroDivisionError`の親クラスは`StandardError`なので、そっちが先に実行されてしまいました。
これではエラーの原因が分からなくなってしまうので、一番起こりそうなエラーから書くようにしましょう。

##例外処理にでてくる、raiseの使い方
raiseで任意の例外を発生させることができます。

```ruby
begin
  raise NoMethodError
rescue => e
  p e
end
```

```
NoMethodError
```

使用用途としては、
・パラメータが想定されたものではない
・不正なアクセスがきた
といった場合に, **明示的にエラーを発生させ、処理を中断させたいとき**に使います。


また、独自のエラーを作ることも可能で、そのときは`StandardError`を継承させてあげます。

```ruby

class Hoge < StandardError #例外クラスを継承
end

begin
  raise Hoge
rescue => e
  p e
end
```

```
#<Hoge: Hoge>
```

##retryの使い方
retryを使うことで、**エラーが起きても、再度beginに戻って実行することができます。**

```ruby

num = 0
begin
  puts 10 / num
rescue ZeroDivisionError => e
  puts e
  num = 1
  retry #beginブロックを再度実行
end

puts "終了しました"
```

```
divided by 0
10
終了しました
```
処理の流れとしては,
1. 変数numが0なので、`10 / 0` で`ZeroDivisionError`が発生
2. **例外オブジェクトを出力。**変数numの0が1に代わり, **retryでbeginを再度実行**
3. 変数numが1なので、`10 / 1`で **10を出力**
4. 例外エラーはないのでbegin内の実行は終了し、最後に**終了しましたを出力**

また、注意点としては、再度実行してもエラーが解決されない場合は永遠に無限ループしてしまうこと。
例えば、先ほどのコードでnumに1を足さない場合を想定してみましょう。

```ruby
num = 0

begin
  puts 10 / num
rescue ZeroDivisionError => e
  puts e
  num = 0 #0を足す
  retry
end

puts "終了しました"
```

```
divided by 0
divided by 0
divided by 0
....
```

このように、永久に無限ループしてしまい、誤った使い方をするとサーバーがクラッシュするので気をつけましょう。 
強制終了は`Ctrl + C`です。

##ensureの使い方

ensureを使うことで、**例外が発生してもしなくても実行される処理**を書くことができます。

```ruby
begin
  puts "例外なし"
rescue => e
  puts e
ensure
  puts "ensureは絶対実行する!"
end
```

```
例外なし
ensureは絶対実行する!
```


使用用途としては、何らかの処理を行っている最中にエラーが発生した場合でも、確実に実行したい処理がある場合に使い書います。
（例: ファイルを開いている最中に不具合が発生しても確実に閉じるようにする)

##まとめ
**1. begin・・・ 例外処理を始めるときに一番最初に書くもの**
**2. rescue　・・・　例外処理の内容を書く場所**
**3. retry　・・・　エラーが起きても、beginに戻って例外処理を再度実行するもの**
**4. raise ・・・　任意のエラーを明示的に発生させる**
**5. ensure　・・・　例外が発生してもしなくても確実に実行される処理を書く**