CodeBox CodeBox

【Rails】undefined method for nil:NilClassの原因

Ruby / Rails
けい

概要

『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を使おうとしているのか確認するようにしましょう!

解説は以上です!

ABOUT ME

けい
ベンチャーのフロントエンジニア。 主にVueとTypescriptを使っています。ライターのための文字数カウントアプリ:https://easy-count.vercel.app/