ruby 例外

# TL;DR
rescueのデフォルト引数は `StandardError`
rescueで捕捉できるのは、引数に指定した例外クラス or 引数に指定した例外クラスを継承したクラス
rescueでは `StandardError` を捕捉するべき

---

```irb
[1] pry(main)> begin
[1] pry(main)*   do_something()
[1] pry(main)* rescue => e
[1] pry(main)*   puts 'ERROR', e # e is an exception object containing info about the error.
[1] pry(main)* end
ERROR
undefined method `do_something' for main:Object
=> nil
```

```irb
[2] pry(main)> begin
[2] pry(main)*   do_something()
[2] pry(main)* rescue ActiveRecord::RecordNotFound => e
[2] pry(main)*   puts 'ERROR', e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
[2] pry(main)* end
NoMethodError: undefined method `do_something' for main:Object
from (pry):7:in `<main>'
```

上記２つの違いは `rescue` の引数に `ActiveRecord::RecordNotFound` の有無です。
指定しない場合は undefined method \`do_something' for main:Object が捕捉されます。
指定した場合は捕捉されず、begin end後に例外が発生します。
なぜなら、 `ActiveRecord::RecordNotFound` を引数に指定した場合、 `ActiveRecord::RecordNotFound` か、  `ActiveRecord::RecordNotFound` を継承したエラーのみ、 `rescue` で捕捉することができるためです。
`NoMethodError` は、 `ActiveRecord::RecordNotFound` を継承していない、かつ、 `ActiveRecord::RecordNotFound` ではないので捕捉することができません。

補足すると、NoMethodErrorは
StandardError > NameError > NoMethodError
ActiveRecord::RecordNotFoundは
StandardError > ActiveRecord::ActiveRecordError
となっています。

話がそれましたが、たとえば `NoMethodError` の親である、 `StandardError` を `rescue` に指定すれば捕捉することができます

```irb
[5] pry(main)> begin
[5] pry(main)*   do_something()
[5] pry(main)* rescue StandardError => e
[5] pry(main)*   puts 'ERROR', e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
[5] pry(main)* end
ERROR
undefined method `do_something' for main:Object
=> nil
```

さらに、 `StandardError` の親である、 `Exception` を指定しても同じです。

```irb
[9] pry(main)> begin
[9] pry(main)*   do_something()
[9] pry(main)* rescue Exception => e
[9] pry(main)*   puts 'ERROR', e # Only rescues RecordNotFound exceptions, or classes that inherit from RecordNotFound
[9] pry(main)* end
ERROR
undefined method `do_something' for main:Object
=> nil
```

`Exception` の親となる例外のクラスは存在しません。
だとすると、エラーの親である、 `Exception` を捕捉すればいいのではないかと考えますが、それは良くないです。
なぜなら、 `Exception` はシステムレベルの例外も含まれます。アプリケーションレベルの例外を捉えたいのではないでしょうか。
例として、ctrl + c で実行中のプログラムを終了する場合、そこで処理は終了するべきですが、このように書くと終了しません。

```irb
[10] pry(main)> 1.upto(10) do |i|
[10] pry(main)*   begin
[10] pry(main)*     p i
[10] pry(main)*     sleep(1)
[10] pry(main)*   rescue Exception => e
[10] pry(main)*     p e.class
[10] pry(main)*     p "catch!"
[10] pry(main)*   end
[10] pry(main)* end
1
2
3
^CInterrupt # ctrl + c を押した
"catch!"
4
5
^CInterrupt # ctrl + c を押した
"catch!"
6
7
^CInterrupt # ctrl + c を押した
"catch!"
8
9
^CInterrupt # ctrl + c を押した
"catch!"
10
=> 1
```

このような動作は想定していないと思います。

また、このような例もあります。 `Exception` を継承した、例外クラスを捕捉します。

```irb
[12] pry(main)> class MyError1 < Exception; end
=> nil
[13] pry(main)> begin
[13] pry(main)*   raise MyError1
[13] pry(main)* rescue => e
[13] pry(main)*   puts "Exception handled! #{e}"
[13] pry(main)* end
MyError1: MyError1
from (pry):32:in `<main>'
```

間違えました。これでは `Exception` を捕捉できません。

**`rescue` に引数を指定しない場合、デフォルトで `StandardError` が引数になるからです。**

今回の場合、引数を指定しないので、 `StandardError` が引数です。
Exception > StandardError
となっているため、 `Exception` を捕捉することができませんでした。なので

```irb
[15] pry(main)> class MyError1 < Exception; end
=> nil
[16] pry(main)> begin
[16] pry(main)*   raise MyError1
[16] pry(main)* rescue Exception => e
[16] pry(main)*   puts "Exception handled! #{e}"
[16] pry(main)* end
Exception handled! MyError1
=> nil
```

このようにすることで、 `Exception` を捕捉することができます。

### 参考にさせていただきました
https://qiita.com/ngron/items/4c319ae7340b72c7e566
http://ruby-doc.com/docs/ProgrammingRuby/
https://blog.toshimaru.net/ruby-standard-error/
https://qiita.com/tsubasakat/items/6825bcefcad26da3471b
https://www.honeybadger.io/blog/ruby-exception-vs-standarderror-whats-the-difference/

下記の通りすると、例外クラスを見ることができます。（２パターン）
`1` @scivola さんに教えてくいただきました

```rb
exceptions = []
tree = {}
ObjectSpace.each_object(Class) do |cls|
  next unless cls.ancestors.include? Exception
  next if exceptions.include? cls
  next if cls.superclass == SystemCallError # avoid dumping Errno's
  exceptions << cls
  cls.ancestors.delete_if {|e| [Object, Kernel].include? e }.reverse.inject(tree) {|memo,cls| memo[cls] ||= {}}
end
tree_printer = Proc.new do |t, level = 0|
  t.sort_by { |c,| c.name }.each do |c, v|
    puts ('  ' * level) + c.to_s
    tree_printer[v, level + 1]
  end
end
tree_printer[tree]
```


`2` http://blog.nicksieger.com/articles/2006/09/06/rubys-exception-hierarchy/

```rb
exceptions = []
tree = {}
ObjectSpace.each_object(Class) do |cls|
  next unless cls.ancestors.include? Exception
  next if exceptions.include? cls
  next if cls.superclass == SystemCallError # avoid dumping Errno's
  exceptions << cls
  cls.ancestors.delete_if {|e| [Object, Kernel].include? e }.reverse.inject(tree) {|memo,cls| memo[cls] ||= {}}
end

indent = 0
tree_printer = Proc.new do |t|
  t.keys.sort { |c1,c2| c1.name <=> c2.name }.each do |k|
    space = (' ' * indent); space ||= ''
    puts space + k.to_s
    indent += 2; tree_printer.call t[k]; indent -= 2
  end
end
tree_printer.call tree
```

```irb
BasicObject
  ActiveSupport::Tryable
    JSON::Ext::Generator::GeneratorMethods::Object
      PP::ObjectMixin
        ActiveSupport::Dependencies::Loadable
          ActiveSupport::ToJsonWithActiveSupportEncoder
            ActiveSupport::Dependencies::Blamable
              Exception
                CGI::InvalidEncoding
                EventMachine::FileNotFoundException
                IRB::Abort
                MonitorMixin::ConditionVariable::Timeout
                NoMemoryError
                OAuth::RequestProxy::UnknownRequestType
                OAuth::Signature::UnknownSignatureMethod
                Pry::InputLock::Interrupt
                ScriptError
                  LoadError
                    AbstractController::Helpers::MissingHelperError
                    ActionController::MissingRenderer
                    CodeRay::PluginHost::HostNotFound
                    CodeRay::PluginHost::PluginNotFound
                    FFI::NotFoundError
                    FFI::PlatformError
                    Gem::LoadError
                      Gem::ConflictError
                      Gem::MissingSpecError
                        Gem::MissingSpecVersionError
                  NotImplementedError
                  SyntaxError
                    Dotenv::FormatError
                SecurityError
                SignalException
                  Interrupt
                    Sidekiq::Shutdown
                StandardError
                  AbstractController::ActionNotFound
                  AbstractController::Error
                    AbstractController::DoubleRenderError
                  ActionController::ActionControllerError
                  ActiveModel::ValidationError
                  ActiveRecord::ActiveRecordError
                    ActiveRecord::RecordNotFound
                  NameError
                    NoMethodError
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~省略
                  Zlib::Error
                    Zlib::BufError
                    Zlib::DataError
                    Zlib::GzipFile::Error
                      Zlib::GzipFile::CRCError
                      Zlib::GzipFile::LengthError
                      Zlib::GzipFile::NoFooter
                    Zlib::MemError
                    Zlib::NeedDict
                    Zlib::StreamEnd
                    Zlib::StreamError
                    Zlib::VersionError
                SystemExit
                  Gem::SystemExitException
                SystemStackError
                fatal
=> [BasicObject]