QA@IT

Rubyの例外クラスの階層を一覧するには?

2414 PV

Rubyの例外クラスはどういう階層になっているかを自分の環境で調べるにはどうすればいいですか?

Exceptionがいちばん祖先にいる階層のことで、何となくClassクラスのオブジェクトをリストアップして、それらの ancestors に Exception が含まれるかどうかを見ればいいような気がしつつ、やり方が分かりません。よろしくお願いします。

回答

Nick Sieger さんという方のブログでは ObjectSpace.each_object を使った方法が紹介されていますが、これでどうでしょうか。(僕も昔、調べました!)

exceptions = []
tree = {}

ObjectSpace.each_object(Class) do |cls|
  next unless cls.ancestors.include? Exception
  next if exceptions.include? cls
  next if cls.superclass == SystemCallError
  cls
    .ancestors
    .delete_if {|e| [BasicObject, 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)
    puts space + k.to_s
    indent += 2
    tree_printer.call t[k]
    indent -= 2
  end
end

tree_printer.call tree

Ruby 1.9.4-p194での実行結果は、以下です。

BasicObject
  Exception
    NoMemoryError
    ScriptError
      LoadError
        Gem::LoadError
      NotImplementedError
      SyntaxError
    SecurityError
    SignalException
      Interrupt
    StandardError
      ArgumentError
      EncodingError
        Encoding::CompatibilityError
        Encoding::ConverterNotFoundError
        Encoding::InvalidByteSequenceError
        Encoding::UndefinedConversionError
      FiberError
      IOError
        EOFError
      IndexError
        KeyError
        StopIteration
      LocalJumpError
      Math::DomainError
      NameError
        NoMethodError
      RangeError
        FloatDomainError
      RegexpError
      RuntimeError
        Gem::Exception
          Gem::CommandLineError
          Gem::DependencyError
          Gem::DependencyRemovalException
          Gem::DocumentError
          Gem::EndOfYAMLException
          Gem::FilePermissionError
          Gem::FormatException
          Gem::GemNotFoundException
          Gem::GemNotInHomeException
          Gem::InstallError
          Gem::InvalidSpecificationException
          Gem::OperationNotSupportedError
          Gem::RemoteError
          Gem::RemoteInstallationCancelled
          Gem::RemoteInstallationSkipped
          Gem::RemoteSourceException
          Gem::VerificationError
      SystemCallError
      ThreadError
      TypeError
      ZeroDivisionError
    SystemExit
      Gem::SystemExitException
    SystemStackError
    fatal
tree = {}

["a","b","c"].inject(tree) do |memo, cls|
  memo[cls] ||= {}
end

tree  # => {"a"=>{"b"=>{"c"=>{}}}}
編集 履歴 (4)
  • こういうのです、ありがとうございます! このコード、難しいですね。inject(tree)とやってなぜツリーがハッシュになるのか挙動がよく分かりません。それと、よく見ると、 exceptions って使ってないし、space ||= '' のところも実行されないコードパスっぽいですね。あと、1.9系なら、ObjectだけじゃなくてObjectBasicも対象外にした方がヨサゲでしょうか -
  • ご指摘をソースコードに反映しました。 -
  • ありがとうございます! -
ウォッチ

この質問への回答やコメントをメールでお知らせします。