その他
    ホーム技術発信DoRubytryの使い方と注意点

    tryの使い方と注意点

    この記事はアピリッツの技術ブログ「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_spresent?メソッドなどの場合のみ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?

    モバイルバージョンを終了