はじめに
Misocaの開発チームでインターンをしているhmryuです。Misocaでは、Railsによる開発を行っているのですが、開発を進める中で検索しても、ドキュメントを読んでも、わからないことが時々あります。そんなときは、手探りながらRailsのソースコードやコミットログを調べるようにしています。
先日は、Object#try
の挙動でわからないことがあり、いろいろ調べていました。そこで今回は、Object#try
の実装や注意点について書きたいと思います。
Object#tryとは?
ActiveSupportのObject#try
*1は、「レシーバーがnil
の場合でもメソッドを呼び出すことができる」「レシーバーがメソッドを持っていなくても呼び出すことができる」メソッドです。
これを使うと、例えば、
# @personがnilの場合、NoMethodErrorが発生してしまう @person.name if @person
を
@person.try(:name)
と書くことができます。
また、
# @personがnon_existing_methodを持っていない場合、NoMethodErrorが発生してしまう @person.non_existing_method if @person.respond_to?(:non_existing_method)
を
@person.try(:non_existing_method)
とかけたりして便利です。
tryの実装
try
の実装は、以下のようになっています(version:4.2.4)
class Object def try(*a, &b) try!(*a, &b) if a.empty? || respond_to?(a.first) end def try!(*a, &b) if a.empty? && block_given? if b.arity.zero? instance_eval(&b) else yield self end else public_send(*a, &b) end end end class NilClass def try(*args) nil end def try!(*args) nil end end
NilClass#try
が定義されているため、初めの例のようにレシーバがnil
のケースでは、nil
が返されます。
また、レシーバのオブジェクトが引数のメソッドを持っていないケースでは、respond_to?
がfalse
になるため、NameNethod::Error
が発生しないようになっています。
try
とtry!
の違いは、呼び出し元のオブジェクトが引数のメソッドを持っていない時、NameNethod::Error
を発生するかどうかになります。
ちなみに、Rails3では、try
で存在しないメソッドを呼び出した場合は、NameNethod::Error
を発生するようになっていました。なので、Rails3のtry
は、Rails4のtry!
に相当します。*2
Delegatorを使うときの注意
とても便利なtry
ですが、Delegator
を使ったオブジェクトを対象にする場合は注意が必要です。例えば、
class A < Delegator def initialize end def __getobj__ B.new end def method_1 "A" end end class B def method_1 "B" end end
このように、A
クラスがB
クラスに委譲しているようなケースだと、
> a = A.new > a.method_1 => A > a.try(:method_1) => B
このようになります。a.method_1
については意図した通りにA
が帰ってくるのですが、a.try(:method_1)
ではB#method_1
が呼ばれてしまっていることがわかります。
Delegator
クラスはObject
クラスを継承していないため、try
メソッドを持っておらず、このような挙動になります。
> Delegator.superclass => BasicObject
ここで問題提起されており、こちらのコミットで解決されています。
Rubyのtry?
先日、2015年11月11日に、Ruby 2.3.0 の最初のプレビュー版が、リリースされました。
そこで新しく追加されたSafe navigation operatorは、ActiveSupportのObject#try!
のように動く演算子です。
a = 'a' a&.upcase # => "A" a = nil a&.upcase # => nil
Ruby 2.3.0 の最初のプレビュー版については、Misoca開発チームのeitoballさんが解説記事:Ruby 2.3 プレビュー(言語・組み込みライブラリ編)を書いているので、興味のある方は読んでみてください。