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

时间:2017-10-18 13:11:59

标签: ruby-on-rails

这是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文件。但是一旦删除该文件,就会得到上述行为。

1 个答案:

答案 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')