【问题标题】:Rails' autoloading/constant resolution is creating ghost modulesRails 的自动加载/恒定分辨率正在创建幽灵模块
【发布时间】:2018-03-30 09:04:00
【问题描述】:

这里是a brand new Rails 5.1.4 app,带有一个模型和几个路由和控制器。

命名空间控制器正在引用顶级模型:

class AdminArea::WelcomeController < ApplicationController
  def index
    @user = User.new(name: 'Sergio')
  end
end

到目前为止一切顺利。您可以查看 master,导航到 http://localhost:3000/admin_area/welcome 并查看它是否有效。

但是如果我们were to add an empty directory app/presenters/admin_area/user/ *,那么事情就会变得很奇怪。突然之间,那个控制器中的User 不是我的模型,而是一个不存在的模块!

NoMethodError (undefined method `new' for AdminArea::User:Module):

app/controllers/admin_area/welcome_controller.rb:3:in `index'

当然,这个模块没有任何[非内置]方法,也不能固定到磁盘上的源文件。

问题:为什么添加一个空目录会导致 Rails 神秘地凭空变出一个模块,而不是正确地将名称 User 解析为我的模型?


* 实际上,如果你按原样检查那个分支,你会得到一个不同的错误。

NameError(未初始化的常量 AdminArea::WelcomeController::User)

因为 git 不允许我提交一个空目录,所以我在其中添加了一个 .keep 文件。但是,一旦您删除该文件,您就会得到上述行为。

【问题讨论】:

  • @jon1467:不,不是那个副本。除非我错过了什么。
  • 对不起,我想我误读了您的问题,询问如何将您的用户模型放在目录presenters 中。我的错。
  • 我认为this issue 可以描述您的问题,但没有迹象表明这是否已修复/更改。
  • @jon1467:更接近,但仍然没有。 Module.nesting 不影响这里的事情。在示例应用程序中它是AdminArea::WelcomeController,但在我的实际应用程序中,它是一个正确的嵌套 (module AdminArea; class WelcomeController)。两者都表现出相同的行为。

标签: ruby-on-rails


【解决方案1】:

这是 ruby​​ 常量查找的结果以及 Rails 如何解决自动加载问题。

控制器中的常量User 被称为"relative reference",这意味着它应该相对于它所在的命名空间进行解析。 对于此常量,可以定义常量的三种可能变体:

AdminArea::WelcomeController::User
AdminArea::User
User

Rails 自动加载将这些常量映射到文件名并遍历 autoload_paths 以找到定义常量的文件。例如:

app/assets/admin_area/welcome_controller/user.rb
app/assets/admin_area/welcome_controller/user
app/channels/admin_area/welcome_controller/user.rb
...
app/assets/admin_area/user.rb
app/assets/admin_area/user
...
app/assets/user.rb
...
app/models/user.rb #=> here it is!

当您将admin_area/user 文件夹添加到presenters 目录中时,您实际上是在定义这样一个常量。 Modules in Rails are automagically created,因此您实际上不需要创建文件来定义这些仅用作命名空间的模块。

当您添加文件夹时,该文件夹出现在 Rails 查找中:

...
app/assets/admin_area/user.rb
app/assets/admin_area/user
...
app/presenters/admin_area/user #=> Here Rails finds the folder

Rails 解析 User 以引用该模块。

但是这很容易解决,如果您希望 AdminArea 命名空间中使用的 User 常量引用顶级常量(而不是 AdminArea::User 模块),您应该更改“相对在常量前面加上::,将引用”转换为绝对引用

@user = ::User.new(name: 'Sergio')

【讨论】:

  • 我认为唯一缺少的部分是链接到自动创建模块的代码。
  • 你去guides.rubyonrails.org/… :) "如果 autoload_paths 有一个名为 admin.rb 的文件,Rails 会加载那个文件,但如果没有这样的文件并且找到一个名为 admin 的目录,Rails 会创建一个空模块,并将其动态分配给 Admin 常量。”
  • 就是这样,谢谢!我会尽快奖励赏金:)
猜你喜欢
  • 2013-10-28
  • 1970-01-01
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
相关资源
最近更新 更多