Rails的自动加载/恒定分辨率正在创建ghost模块

Ser*_*sev 7 ruby-on-rails

这是一个全新的Rails 5.1.4应用程序,带有模型和几个路由和控制器.

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

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

到现在为止还挺好.您可以查看主人,导航到http://localhost:3000/admin_area/welcome并查看它是否有效.

但如果我们要添加一个空目录app/presenters/admin_area/user/*,那么事情会变得奇怪.突然之间,User那个控制器不是我的模型,而是一个不存在的模块!

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

app/controllers/admin_area/welcome_controller.rb:3:in `index'
Run Code Online (Sandbox Code Playgroud)

当然,该模块没有任何[非内置]方法,也无法固定到磁盘上的源文件中.

问题:为什么添加一个空目录导致rails无法通过空气神秘地召唤模块,而不是正确地将名称解析User为我的模型?


*实际上,如果您按原样检查该分支,您将收到不同的错误.

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

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

Lau*_*nen 5

这是ruby常量查找的结果,以及Rails如何解析自动加载.

User控制器中的常量称为"相对引用",这意味着它应该相对于它发生的命名空间进行解析.对于此常量,有三种可能的变量,其中可以定义常量:

AdminArea::WelcomeController::User
AdminArea::User
User
Run Code Online (Sandbox Code Playgroud)

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!
Run Code Online (Sandbox Code Playgroud)

admin_area/user文件夹添加到presenters目录中时,您实际上是在定义这样的常量.Rails中的模块是自动创建的,因此您实际上不需要创建定义这些仅用作命名空间的模块的文件.

添加文件夹后,该文件夹出现在Rails查找中:

...
app/assets/admin_area/user.rb
app/assets/admin_area/user
...
app/presenters/admin_area/user #=> Here Rails finds the folder
Run Code Online (Sandbox Code Playgroud)

和Rails解析了User对该模块的引用.

但是这很容易修复,如果你想要UserAdminArea命名空间中使用的常量来引用顶级常量(而不是AdminArea::User模块),你应该通过在常量前面将"相对引用"更改为绝对引用::.

@user = ::User.new(name: 'Sergio')
Run Code Online (Sandbox Code Playgroud)