Techioz Blog

Object::try が nil オブジェクトに送信された場合に機能するのはなぜですか?

概要

Ruby で nil オブジェクトのメソッドを呼び出そうとすると、NoMethodError 例外が発生し、次のメッセージが表示されます。

"undefined method ‘...’ for nil:NilClass"

ただし、Rails には、nil オブジェクトに送信された場合に nil を返す try メソッドがあります。

require 'rubygems'
require 'active_support/all'

nil.try(:nonexisting_method) # no NoMethodError exception anymore

では、その例外を防ぐために try は内部的にどのように機能するのでしょうか?

解決策

ActiveSupport 4.0.0 では 2 つの try メソッドが定義されています。1 つはオブジェクト インスタンス用です。

class Object
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      public_send(*a, &b) if respond_to?(a.first)
    end
  end
end

もう 1 つは NilClass インスタンス (nil オブジェクト) 用です。

class NilClass
  def try(*args)
    nil
  end
end

ここで、nil を返すメソッドを定義する Object インスタンス (Ruby の他のすべてのものと同様、実際には Object から継承する nil を除く) があるとします。

class Test
  def returns_nil
    nil
  end
end

したがって、Test.new.try(:returns_nil) または Test.new.not_existing_method を実行すると、Object#try が呼び出され、パブリック メソッド (response_to?) が存在するかどうかがチェックされます。実行する場合はメソッド (public_send) を呼び出します。そうでない場合は nil を返します (他の行はありません)。

nil を返すこれらのメソッドをもう一度呼び出してみると、次のようになります。

Test.new.try(:returns_nil).try(:any_other_method)
Test.new.try(:not_existing_method).try(:any_other_method)

NilClass#try を呼び出します。これは nil#try であり、単にすべてを無視して nil を返します。したがって、他のすべての試行は nil インスタンスで呼び出され、nil を返します。