这是a brand new Rails 5.1.4 app,带有模型和几个路线和控制器。
命名空间控制器引用顶级模型:
class AdminArea::WelcomeController < ApplicationController
def index
@user = User.new(name: 'Sergio')
end
end
到目前为止一切顺利。您可以查看主文件,导航到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
文件。但是一旦删除该文件,就会得到上述行为。
答案 0 :(得分:5)
这是ruby常量查找的结果,以及Rails如何解析自动加载。
控制器中的常量User
称为"relative reference",这意味着它应该相对于其发生的命名空间进行解析。
对于此常量,有三种可能的变量可以定义常量:
AdminArea::WelcomeController::User
AdminArea::User
User
Rails自动加载将这些常量映射到文件名中,并在autoload_path
上进行迭代,以便找到定义常量的文件。 E.g:
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
以引用该模块。
然而,这很容易修复,如果您希望User
命名空间中使用的AdminArea
常量引用顶级常量(而不是AdminArea::User
模块),你应该通过在::
前加上常量来将“相对引用”更改为绝对引用。
@user = ::User.new(name: 'Sergio')