nil:NilClassの「NoMethodError」への対処方法

f:id:ryoutaku_jo:20190218210507p:plain

【結論】
nilとは「何も存在しない」という事を表すオブジェクト

nilは「NilClass」クラスのオブジェクトなので、
 「NilClass」クラスに定義されていないメソッドを使用すると
 「nil:NilClass (NoMethodError)」というエラーが発生する

・このエラーには下記の様な対処方法がある
 ーif文でnilの場合の条件式を記述する
 ー「nilガード」を用いる
 ーnilにならない様に設計を見直す


【目次】


【本題】

nilのエラーなどに遭遇する機会が増えた

最近、ローカル環境では正常に動作するけど、本番環境では動作しないという
エラーがちょくちょく発生しています。

ログを見ると「nil:NilClass (NoMethodError)」というエラーでした。
ローカル環境ではきちんと入力したデータが、
本番環境では入力し忘れて、nilになったことで、そのデータを呼び出した際に
エラーになっていた様です。

この様に、nil関連のエラーへ対応する機会が増えて来たので、
これを機に、nilについてまとめてみました。

nilとは

「何も存在しない」という意味

空の配列オブジェクトや、数値の0などは、
それ自体が意味を持っているので、「nil」にはなりません。
文字通り、何も存在しない事を表すオブジェクトです。

nil:NilClass (NoMethodError)とは

まずNilClassとは、nilに定義されたクラスです。

そして「NilClass」に対して、「NilClass」に定義されていないメソッドを
使用した時に発生するエラーがnil:NilClass の「NoMethodError」
です

下記に例をあげます。

f:id:ryoutaku_jo:20190219103842p:plain

= image_tag product_image.first.name,class: "product__image"

上記のコードに対して、「undefined method `name' for nil:NilClass」という
エラーメッセージが表示されています。
直訳すると「nil:NilClassに対して、`name' は未定義のメソッドです」という意味です。

このコードは、image_tagで画像を表示させるhtml(haml)上の記述ですが、
その画像を指定している「product_image.first.name」にnameメソッドが使用されています。

先ほどのエラーメッセージから考えると、
「product_image.first」がnilになっていると考えられます。

このエラーには下記の様な対処方法があります。
 ーif文でnilの場合の条件式を記述する
 ー「nilガード」を用いる
 ーnilにならない様に設計を見直す

if文でnilの場合の条件式を記述する

f:id:ryoutaku_jo:20190219105311p:plain

「product_image.first」に対して使用している「nil?」は
nilの場合にtureを返すメソッドです。

これを利用してif文で、nilの場合には、
別の画像を差し込む記述に変えることで、エラーを防ぐことができます。

f:id:ryoutaku_jo:20190219105509p:plain
※実際に差し替えられた画像

また、「nil?」以外にも下記の様にnilを特定するメソッドは存在します。

nil?・・・nilの場合にtureを返す
blank?・・・nil」か「空の配列の場合にtureを返す
present?・・・nil」か「空の配列」以外の場合にtureを返す

nilガード」を用いる

f:id:ryoutaku_jo:20190219114242p:plain

上記で登場する「||=」という記述は、「nilガード」と呼ばれるものです。
これは、代入先の変数がnilの時だけ、値を代入するという記述です。

これにより、「image = product_image.first&.name」で、既に代入済みであれば、
別の画像は差し込まれず、逆に「product_image.first&.name」がnilになってしまい
「image」に代替え用の画像のパスが代入されます。

なお「&.」という記述ですが、これはメソッドのレシーバ(メソッドの直ぐ左側)がnilでも、
メソッド呼び出し時にエラーが起こさなくする
ことが出来るコードです。
「&.」は「try」というメソッドでも代替え可能です。

nilにならない様に設計を見直す

上記二つの対処方法でもエラーは発生しなくなりますが、nilを許容する運用を続けると、
思わぬところで別のエラーに遭遇してしまう
ケースも多々あります。

また、むやみにif文やnilガードを多用すると、
可読性を落としてしまう
要因にもなり得ます。

その為、極力nilが発生しない様に設計を見直すところから始めた方が良いかもしれません。

例えば、今回は出品した画像データがnilの場合にエラーになってしまいましたが、
その出品時に画像も投稿しないと出品が完了しない様にバリデーションをかけておく事などが
対処方法としてあげられます。

総括

nilを深掘りする中で、言語の特性というものを理解する良いきっかけになったと感じています。
Railsを触っているとRubyそのものに関する理解を深める機会が減ってくる様に感じるので、
疑問を抱いたら、掘り下げる習慣は続けるべきだと考えています。

なお、この記事を書くに当たって、色々調べる中で「null安全」というキーワードをを見つけました。

qiita.com

こちらも重要な概念だと思われるので、追々勉強していきたいと思います。

《今日の学習進捗》

チーム開発:24日目
今日は私用の為、開発に参加できず。
移動中の高速バスでブログを書く。

学習開始からの期間 :73日
今日までの合計時間:751h
一日あたりの平均学習時間:10.3h
今日までに到達すべき目標時間:667h
目標との解離:84h
「10,000時間」まで、

残り・・・「9249時間!」