include?などで対象のオブジェクトがnilの場合にNoMethodErrorを発生させずに、nilではない場合にのみメソッドを実行したり条件分岐させたりしたい場合の処理について調べてつつ色々試したのでメモ。
通常
sample = nil
if sample.include?("a")
sample
else
sample.to_a << "a"
end
#=>NoMethodError: undefined method `include?' for nil:NilClass
#["a"]の入った配列を返すことなくエラーで処理が止まってしまう。
実際には違うコードですが、要はinclude?で真の場合と偽の場合で処理を分岐させたかった時にこのエラーが表示されるようになってしまったので、対処法を調べました。
unless nil?を使う
sample = nil
unless sample.nil?
if sample.include?("a")
sample
else
sample.to_a << "a"
end
else
sample.to_a << "a"
end
# =>["a"]
冗長すぎる
tryメソッド(Active Support)
sample = nil
if sample.try(:include?, "a")
sample
else
sample.to_a << "a"
end
#=>["a"]
&.(Ruby)
sample = nil
if sample&.include?("a")
sample
else
sample.to_a << "a"
end
#=>["a"]
今回はtryと&.で解決しました。なお、nil以外のレシーバに対して呼び出した際に少し違いがあり、メソッドが呼び出せない時にtryはnilを返し、&.はエラーを吐くといった違いがあるようです。(try!にすれば&.と同様にエラーにできます。)
#ArrayクラスにStringクラスメソッドを使ってみる。
sample = ["a", "b", "c"]
sample.upcase
#=>NoMethodError: undefined method `upcase' for ["a", "b", "c"]:Array
sample.try(:upcase)
#=> nil
sample&.upcase
#=>NoMethodError: undefined method `upcase' for ["a", "b", "c"]:Array
sample.try!(:upcase)
#=>NoMethodError: undefined method `upcase' for ["a", "b", "c"]:Array
try!にすると&.と同じくエラー吐くようにできますので、両者の違いはなくなりつつありますが、調べてみると&.の方が高速なようなので、今回は&.を使用しました。
# 参考
Active Support コア拡張機能(https://railsguides.jp/active_support_core_extensions.html)