概要
『undefined method '〇〇' for nil:NilClass』が発生する原因とチェックポイントを解説していきます。この記事を読んでどこに着目するのかコツを掴んでいただければと思います!
結論
先に結論を言うと、このエラーが起きる原因は『nil(=中身がないもの)』に対してあるメソッドを実行しようとしているために起きます。なので見るべきポイントはメソッド(.name)ではなく、メソッドの前の部分(@user)に値がしっかり入っているかどうかです。
# エラーが起きるパターン
@user = nil
@user.name => undefined method 'name' for nil:NilClass
# エラーが起きないパターン
@user = {id:1, name: "太郎"}
@user.name => "太郎"
エラーが起きる例を見てみよう
1.undefined method...の内容を翻訳
発生したエラーの画面が下図です。
エラーの画像を拡大するとこんな感じです。
def show
@tasks = @board.tasks ← ここで怒られている!
end
今回発生した『undefined method 'tasks' for nil:NilClass』は直訳すると、『Nilクラスのnilオブジェクトに対するtasksメソッドが定義されていない』という意味になります。
要するに、『対象のオブジェクトに対してtasksというメソッドを実行したいんだけど、そもそもオブジェクトが空だから実行できませんよ!』
中身がないものに対して、tasksというメソッドを実行していたからエラーが発生していたんですね!なのでチェックすべきは、「tasks」ではなく、その前のインスタンス変数(= @task)だということです。
2. undefined method...のコードを確認
今回エラーが発生しているのが、Boardコントローラーのshowアクションの部分です。
class BoardsController < ApplicationController
before_action :authenticate_user!
==途中コード省略==
def show
@tasks = @board.tasks
end
==途中コード省略==
end
@tasks = @board.tasksでBoardに紐づくTaskの一覧を取れるようにしています。
しかし@boardがshowアクション内で定義されていないので、『@boardって何?』とRailsが解釈できなくなり、結果的に@boardがnilになってしまうわけです。
ですのでちゃんと『@boardとは〇〇のことを指していますよ!』と教えてあげる記述が必要になります。
3.showアクションのコードを修正
次のコードのように@boardの記述を追加しました。
class BoardsController < ApplicationController
before_action :authenticate_user!
==途中コード省略==
def show
@board = Board.find(params[:id]) #追加
@tasks = @board.tasks
end
==途中コード省略==
end
ちなみに『find(params[:id])』はテーブル(データが入った表)から『id』で検索するという意味になります。
今回のようにBoardテーブルが自分自身が持つキー(主キー)で検索します。そのため『:id』と書くだけでRailsはid = Boardのidなんだと解釈してくれます。
@board = Board.find(params[:id])
しかし次のコードではエラーが発生してしまいます。どこがおかしいのか分かるでしょうか?
【間違った例】
class TasksController < ApplicationController
def show
@board = Board.find(params[:id])
@task = Task.find(params[:id])
end
end
どこがおかしいかというと、『@board = Board.find(params[:id])』の部分です。この書き方だと[:id]はTaskテーブルが持つidとして捉えられてしまいます。
本来、[:id]はBoardテーブルのidであって欲しいはずですよね?逆に『@task = Task.find(params[:id])』は問題ありません。
ここでの[:id]はTaskテーブルのidとなります。
【正しい例】
class TasksController < ApplicationController
def show
@board = Board.find(params[:board_id]) #ここを修正
@task = Task.find(params[:id])
end
end
params[:id]を使用する際は、どのコントローラーでそのidを使おうとしているのか確認するようにしましょう!
解説は以上です!