轨道 6:Zeitwerk::NameError 不会从模块加载类



我有一个看起来像

的文件
#app/services/account/authenticate/base.rb
module Account
  module Authenticate
    AuthenticateError = Class.new(StandardError)
    class Base < ::Account::Base
      def self.call(*attrs)
        raise NotImplementedError
      end
    end
  end
end

现在,当我从rails c运行代码时,我有一个错误

> ::Account::Authenticate::AuthenticateError
=> NameError (uninitialized constant Account::Authenticate::AuthenticateError)
> ::Account::Authenticate.constants
=> [:Base, :ViaToken]

因此,Rails看不到AuthenticateError类。但是,当我将从此文件夹中创建一个嵌套类时,例如

=> Account::Authenticate::ViaToken
> ::Account::Authenticate.constants
=> [:Base, :AuthenticateError, :ViaToken]

AuthenticateError类现在可见

> ::Account::Authenticate::AuthenticateError
=> Account::Authenticate::AuthenticateError

解决此问题的解决方案是创建一个单独的文件authenticate_error.rb,该文件从一开始就可以使用,但是对于我来说,该解决方案并不理想。是否有任何解决方案来预加载所有类或SMTH?

(带有导轨的Ruby 2.6 6.0.0.rc2(

我在将导轨6.0.2应用于Ubuntu 18.04服务器时经历了同样的问题。

无法加载应用程序:zeitwerk :: nameError:预期文件/home/deploy/myapp/app/models/models/concerns/designation.rb定义常数名称,但没有

我发现问题是 zeitwerk 。Zeitwerk是Rails 6中使用的新的代码加载器引擎。它将成为所有Rails 6 项目的新默认设备,以取代旧的 Classic 引擎。Zeitwerk提供了代码自动加载,急切加载和重新加载的功能。

这是我解决的方式

导航到项目上的config/application.rb文件。

在应用程序模块中添加此行以切换到自动加载的classic模式:

config.autoloader = :classic

这是一个示例:

module MyApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
    config.autoloader = :classic
  end
end

您可以在 zeitwerk 上阅读更多信息:了解Rails中的Zeitwerk 6

将Rails应用程序从5.2更新为6.0,还可以击中Zeitwerk Bump!

如果要继续使用您当前正在使用的自动加载模式,请避免Zeitwerk,然后将此行添加到您的应用程序.rb文件(@promisepreston andles and rails doc(

config.autoloader = :classic

如果您想升级到Zeitwerk,则使用的命令是bin/rails zeitwerk:check(来自本指南文章(。

场景我们最接近这个特定的问题是我们在这样的子文件夹中有一个文件的位置:

#presenters/submission_files/base.rb
module Presenters
  module SubmissionFiles
    class Base < Showtime::Presenter
      def method_call
        #code_here
      end
    end
  end
end

删除额外的模块以:

 #presenters/submission_files/base.rb
 module Presenters
   class SubmissionFiles::Base < Showtime::Presenter
      def method_call
        #code_here
      end
   end
 end

然后,在应用程序中的其他Ruby文件中调用该方法时使用:Presenters::SubmissionFiles::Base.method_call

zeitwerk遵循conventional file structure,只要您遵守规则,就可以按需加载项目的类和模块(自动加载(。

# service/account/authenticate/base.rb
module Account
  module Authenticate
    puts "load service ....."
    AuthenticateError = Class.new(StandardError)
    class Base
    end
  end
end
::Account::Authenticate::AuthenticateError # uninitialized constant
::Account::Authenticate::Base # load service ....
::Account::Authenticate::AuthenticateError # OK

您可以看到,您第一次尝试到达常数AuthenticateError时,日志load service ...没有显示,因为您不玩zeitwerk规则:

  • 每当收到加载const ::Account::Authenticate::AuthenticateError的请求时,首先会检查并返回如果已经加载了常数,否则会查找与常数::Account::Authenticate::AuthenticateError相对应的文件/account/authenticate/authenticate_error.rb,以找到该常数定义,但找不到它。

  • 在步骤2上,当您调用::Account::Authenticate::Base时,它可以找到文件/account/authenticate/base.rb并加载它,在此期间,它也加载了在该文件上定义的常数AuthenticateError,现在我们拥有常数::Account::Authenticate::AuthenticateError,并且当然,步骤3可以。

现在让我们尝试使用zeitwerk规则,我创建一个文件/account/authenticate/authenticate_error.rb如下

# service/account/authenticate/authenticate_error.rb
module Account
  module Authenticate
    puts "load error ....."
    AuthenticateError = Class.new(StandardError)
  end
end

并尝试在步骤1

上尝试该常数
$ spring stop
$ rails c
> ::Account::Authenticate::AuthenticateError
load error .....
=> Account::Authenticate::AuthenticateError

它起作用,因为zeitwerk找到了文件account/authenticate/authenticate_error.rb。(请注意,文件名/____authenticate_error.rb仍然有效(

我的想法:我认为您可以安全地使用模块::Account::Authenticate中常数AuthenticateError,如果您想将这些错误常数公开到外部,则可以创建文件/account/authenticate/error.rb

# service/account/authenticate/error.rb
module Account
  module Authenticate
    module Error
     AuthenticateError = Class.new(StandardError)
    end
  end
end

然后,您可以访问::Account::Authenticate::Error::AuthenticateError,我认为它比将AuthenticateError放入base.rb中更清楚。

我能够通过不尝试对抗导轨6来解决它。Zeitwerk自动加载某些预期文件夹,其中包括应用程序/型号,应用程序/控制器,应用程序/助手等。

我创建了一个文件夹app/helpers并将我的services文件夹移入其中。

就是这样!

最新更新