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 を返します。