nilを意識しないとは
nilに対して不正なメソッド呼び出しをしようとした時には、その結果がnilであることを期待するようプログラミングをするケースが多いと思います。 そして、その場合には下記のようにActiveSupportのtry!メソッドを使用することが多いと思います。
result = nil
result.try!(&:first_method).try!(&:second_method) # => nil
上記のように、nilのときにはメソッド呼び出しを行わない、ということを分岐を書かずに実現することをnilを意識しないと言っています。
※追記:Ruby2.3以降なら通称ぼっち演算子がつかえるそうなので、そちらがベターだと思う。
result = nil
result&.first_method&.second_method # => nil
try!やifを重ねるのは美しくない
result = nil
return result.try!(&:first_method).try!(&:second_method)
result = nil
return nil if result.nil?
temp = result.first_method
return temp.nil? ? nil : temp.second_method
nilの可能性がある変数に対し、nilの場合はメソッド呼び出しを行わない場合、毎回try!やif分岐を加えると、なんだか読みづらくなります。
if分岐に至っては、メソッドチェーンができなくなり最悪です。
NilClass.method_missingのオーバーライドという黒魔術
なぜnilの場合にメソッドを呼び出さないようにするかというと、nilではない他のクラスのインスタンスに実装されているメソッドを呼びだそうとしている時にnilがレシーバとなると、呼び出すはずのメソッドが存在せず、NilClassからNoMethodErrorが発生するからです。
そして、NoMethodErrorは、存在しないメソッドが呼び出された時に実行されるmethod_missingメソッド内でraiseされています。
つまり、NilClassのmethod_missingメソッドをオーバーライドし、superを呼びださなければNoMethodErrorは発生しないことになります。NoMethodErrorが発生しなければ、try!やif分岐を組む必要もありません。
さらに、nilを返すことでtry!と同じような挙動を実現できます。
class NilClass
def method_missing(name, *args, &block)
nil
end
end
result = nil
result.first_method.second_method # => nil
使う時は慎重に
try!を使わなくてもnilをスルーできるのでコード量が減り、スマートに書くことが出来るようになりますが、タイポに気づきにくくなる等、弊害もあります。
result = nil
result.to_S # => nil
result.try!(&:to_S) # => NoMethodError: undefined method `to_S' for nil:NilClass
try!の場合はレシーバがnilの時にメソッドを呼ばない。
method_missingのオーバーライドの場合は、NilClassに存在しないメソッドが呼ばれてもエラーが出ないようにする。
というアプローチの違いから起きる弊害です。