Techioz Blog

Ruby on Railsでメソッドが定義されているときに未定義メソッドエラーが発生する

概要

Ruby on Rails アプリで、コードを変更すると (たとえば、5 行目を i = f1 から i = f2 に変更すると、次のエラーが発生します。

これは私のコードです

class Api::V1::AppController < ApplicationController

  def login
    c = Master::Utilities.new
    i = c.f1
    z = c.f2
    render json: {m: 'login' , msg: "hello" , data: "#{z} - #{i}" } , status: :ok
  end
end

変更を元に戻しても問題は解決しません。 このエラーが発生した後、Pumaを再起動すると、コードを変更せずにすべてが再び正常に戻ります。

これらは私のコードです

このコードを config pplication.rb で使用してコードを自動ロードします

  config.paths.add Rails.root.join('lib').to_s, eager_load: true

Ruby on Rails を初めて使用するので、コードに小さな間違いがあると思いますが、どこが間違っているのかわかりません。

ありがとう

解決策

ここで問題が発生しているのは、ファイルを編集すると、rails が Master::Utilities.new への呼び出しを認識し、Master::Utilities の定義を再ロードすることです。しかし、この部分になると次のようになります。

Dir[File.join(__dir__, 'code', '*.rb')].each { |file| require file }

…たとえば、lib/master/code/1.rb がすでにロードされていることがわかります。 (require は、すでにロードされているファイルを再ロードしません!)。

したがって、メソッド定義はどれも定義されていないため、事実上、インスタンス メソッドのない Master::Utilities クラスが作成されます。

簡単で最小限の修正は、上記の require を load に置き換えて、「ファイルが以前にロードされた場合でも、常にファイルをロードする」と明示的に指定することです。

Dir[File.join(__dir__, 'code', '*.rb')].each { |file| load file }

ただし、これは Ruby/Rails でコードを構造化する非常に珍しい方法であることを指摘しておきます。おそらく、より従来的なアプローチに従うほうが、より多くのメリットが得られるでしょう。

単一クラスの定義を複数のファイルに分割することにはどのような利点やロジックがありますか?メソッドが定義されている場所を探したい場合、すべてを 1 か所にまとめるか、論理モジュールに分割する方が簡単ではないでしょうか。

「クラスを 1 か所でのみ定義し、クラス名をファイル名と一致させる」という規則に従えば、Rails での一括読み込みはすぐにスムーズに機能します。たとえば、次のようにすることもできます。

# lib/master/utilities.rb
module Master
  class Utilities
    def f1
      # ...
    end

    def f2
      # ...
    end
  end
end

または:

# lib/master/utilities/meaningful_name_1.rb
module Master
  module Utilities
    class MeaningFulName1
      def f1
        # ...
      end
    end
  end
end

# lib/master/utilities/meaningful_name_2.rb
module Master
  module Utilities
    class MeaningFulName2
      def f2
        # ...
      end
    end
  end
end

あるいは、本当にすべてのメソッドを 1 つの巨大なクラスに入れて、フォルダー内のすべてのファイルを自動的にロードしたい場合は (非常にまとまりがないように思えますが!)、メタプログラミングを書くこともできます。 String#classify を使用してファイル名をモジュール名に変換します。