Ruby、Railsエラー解説

uninitialized constant 〇〇Controller

コード

resources :techports, only: [:index]
class TechportsController < ApplicationController
  def index
  end
end

解説

uninitializedは「まだ初期化されていない」

constantは「定数」を意味します。

そのため、このエラーは「TechportsControllerというのは、まだ初期化されていない定数ですよ」という意味です。

そこで、まずは打ち間違えを疑ってください。

この場合、TechportsControllerを呼び出すのはrailsがroutesのresourcesの記載を元に推測するので、resourcesで正しくコントローラ名をスネークケースでかけているか。

次にcontrollerの方で期待されているコントローラ名になっているかを確認しましょう。

ですが、controllerにおいては確かにTechportsControllerが定義されているので問題ないです。

今回の原因はファイル名にあります。

ファイル名がtechport_controllerになっており、コントローラー名と対応していません

railsの場合、app以下のファイルとその定数やクラスを読み込みます。そしてファイル名とそこで実装されるクラス名と強い結びつきが生じます。

コードが合っていてもファイル名が間違っていると正しく目的のコードに辿り着けなくなりますので注意してください。

rails generate controller Techports

のようなコマンドによって生成することでこのような打ち間違えを減らすことができます。

ときどき、ファイル名もコードも、呼び出し先でも間違えていないに定数が定義されていない旨のエラーに見舞われることがあります。その定数が最近作成したばかりのgemや自作クラスの場合はrailsが読み込めていない可能性があります。

rails restart やdocker-composeを使っている場合は一度落としてdocker-compose upや場合によってはdocker-compose buildをし直すことも検討してみてください。

undefined method `split’ for nil:NilClass

コード

class TechportsController < ApplicationController
  def index
    techport = { url: "<https://techport-blog.com>", title: "Techport", author: "Porter" }
    @uri = techport[:uri].split("")
  end
end

解説

今回はsplitを対象にしてみましたが、splitの部分はなんでもあり得ます。

undefined methodとは「未定義のメソッド」

split for nil:NilClassとは「nil クラスにsplit」という意味です

つまり、「nilクラスにsplitってメソッドは定義されていませんよ

という意味です。

言葉通り受け取るなら、nilクラスにsplitを定義してくれてあれば問題が解決します。なのでnilクラスの実装を確認してsplitメソッドが実装されているか、されていない場合は実装してあげることになります。

ですが、nilクラスはrubyの基本オブジェクトなのでそれはしません。

原因はsplitが実装されていないことではなくてそれを呼び出そうとしている値がnilになってしまっていることです。

splitはtechport[:uri]から呼び出しており、これが文字列を返していないことが問題ですね。

urlは作成しているので打ち間違えが問題です。

今回はサンプルなため、原因がすぐ上のコードにありましたが、問題の発覚が難しいのはデータベースやライブラリから取得した値を変数に入れているときでしょう。

値にnilが入りうるのかどうかはdbやモデル、ライブラリの仕様によるので十分確認しましょう。

nilが入ることを許容する場合はその値を至るところでnilなそうでないかで処理を書き分ける必要がでてきます。

そのような場合は自作のデフォルト値を設定するか、早期リターンして以降の処理に進ませない工夫をするとよいです。

また少し実践的ですが、nilオブジェクトパターンを用いることもできます。

# デフォルト値
uri = (techport[:uri] || "default url").split("")
def index
    techport = { url: "<https://techport-blog.com>", title: "Techport", author: "Porter" }
    uri = techport[:uri]
    some_procedure(uri)
  end

  # 早期リターン(ガード節)
  def some_procedure(uri)
    return unless uri

    uri.split("")
  end
# nullオブジェクトパターン
class TechportController < ApplicationController
  def index
    @user = current_user || GuestUser.new
  end
end

class GuestUser
  def name
    "Guest"
  end

  def age
    0
  end

  def email
    "http://guest.com"
  end
end

# current_userのモデルと同じメソッドを定義してその返り値を用意することで、ゲストログインのユーザー安全に演出できます。
# view側では@user.nameはログイン中であってもログインしていなくてもちゃんと文字列が返るので以降の処理も正常に動作させることができ、
# viewファイルではユーザーがログインしているかしていないかで分岐を書く必要がなくなる

コメント

タイトルとURLをコピーしました