この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
Railsのtryを使用するにあたってのアドバイスを頂いたので、今回は、tryについてまとめてみました。
tryとは?
簡単に説明すると、レシーバがnilでもNoMethodErrorが発生せずに、nilを返してくれるメソッドです。このメソッドは、railsのactivesupportというgemをインストールすると使用することができます(ほとんどの場合、Railsの環境構築をすると入っています)。tryの細かい動きについては、自分の環境のactivesupport4.1.4のコードが分かりやすかったので、こちらのコードを使用して説明していきます。※バージョンによっては、処理内容が異なる可能性があります。
(githubのactivesupportのコード:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/object/try.rb)
activesupport-4.1.4/lib/active_support/core_ext/object/try.rb
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
def try!(*a, &b)
if a.empty? && block_given?
yield self
else
public_send(*a, &b)
end
end
end
class NilClass
def try(*args)
nil
end
def try!(*args)
nil
end
end
見て貰えれば分かりますが、レシーバがnilの場合は、引数に関わらず必ずnilが返るようになっています。次に、レシーバがオブジェクトの場合のtryとtry!の違いに注目してみます。tryは、respond_to?でメソッド名が正しくない場合に、メソッドを実行しないので、エラーが発生しませんが、try!は、respond_to?が無い為、メソッド名が正しくない場合は、NoMethodErrorが発生してしまいます。
下記のクラスとデータを使用してtryを使用した簡単な例を挙げます。(Person.job_idは、Job.idの外部キー)
class Job < ActiveRecord::Base
has_many: persons
end
class Person < ActiveRecord::Base
belongs_to: job
end
# Aさんの仕事を取得
person_a = Person.find_by(name: 'A')
person_a.job.job_name
=> engineer
# Bさんの仕事を取得
person_b = Person.find_by(name: 'B')
person_b.job.job_name
=> NoMethodError # Bさんは仕事(job_id)を持っていないので、仕事名を取得すると、NoMethodErrorが発生します。
# tryを使用すると、NoMethodErrorを回避することができます。
person_b.job.try(:job_name)
=> nil
tryをチェインするときの注意点
tryをチェインするときは、原則tryを使用します。レシーバがnilでもエラーが出ないto_s
やpresent?
メソッドなどの場合のみtryを使用しなくても良いです。tryは、レシーバがnilになる可能性がある場合に使用する為、tryの後にnilで動かないメソッドを記述するとエラーの原因となります。
person_c = Person.find_by(name: 'C')
# 悪い例
person_c.try(:job).job_name
person_c.try(:job).try(:job_name).swapcase
# 良い例
person_c.try(:job).try(:job_name)
person_c.try(:job).try(:job_name).try(:swapcase)
person_c.try(:job).present?