【问题标题】:Nested namespaces for controllers in RailsRails 中控制器的嵌套命名空间
【发布时间】:2017-01-18 16:39:21
【问题描述】:

我重构了我的 rails 应用程序,为每个子资源创建一个控制器是相应的命名空间。

api/v1/app/controller/manager.rb
api/v1/app/controller/manager/user.rb
api/v1/app/controller/manager/controller.rb
api/v1/app/controller/admin.rb
api/v1/app/controller/user.rb
api/v1/app/controller/controller.rb

manager命名空间下用户资源的类定义是这样的

class Api::V1::Manager::UserController < ApplicationController

这个控制器可以通过 routes.rb 访问:

 resources :manager , only: [:show ] do
   resources :user, only: [:index], controller: 'manager/user'
 end

生成

/api/v1/manager/:manager_id/user(.:format)       api/v1/manager/manager#index {:format=>"json"}

模型都在下面

app/models/manager.rb
app/models/user.rb

当我现在想访问 api/v1/app/controller/manager/user.rb 控制器或 api/v1/app/controller/manager.rb 中的 Manager 模型时,例如

class Api::V1::ManagerController < ApplicationController
  def index 
     Manager.find(...)
  end
end

class Api::V1::Manager::UserController < ApplicationController
  def index 
     Manager.find(...)
  end
end

我收到这些错误

{"error":"uninitialized constant Api::V1::Manager::UserController::Manager"}%   
{"error":"uninitialized constant Api::V1::Manager::Manager"}%                                                                                 

调用由正确的控制器处理:

Processing by Api::V1::Manager::UserController#index as JSON

解决方法是在调用中使用双冒号前缀

`::Manager.find(...)`.

我可以正常使用所有其他型号Admin.find(...)Controller.first。只有Manager.find(..) 不起作用。

将命名空间重命名为 ManagerResource 仍会产生相同的错误消息。

我希望能够在不同的命名空间下对控制器进行分组,并且仍然以相同的方式访问所有模型,这怎么可能?

更新

创建

api/v1/app/controller/api/v1/foo/customer_controller.rb
api/v1/app/controller/api/v1/manager_customer_controller.rb

启动服务器 (webrick) 后,所有端点都在工作。 将Manager.first 添加到任何控制器或更改使用Manager... 的文件中的某些内容会返回这些错误

`uninitialized constant Api::V1::Foo::UserController::Manager` 
`uninitialized constant Api::V1::ManagerUserController::Manager`
`uninitialized constant Api::V1::*any_controller*::Manager`

重启服务器解决了这个问题。

我可以使用Controller.first 或任何其他模型,例如api/v1/app/controller/controller.rb.服务器响应良好。

就像@Andrey Deineko 指出的那样,我现在明白模块和类名应该不同。

我不明白的是,为什么当我在名称与模型不同的命名空间下减去控制器时,这些错误只发生在特定模型上?

更新二

我删除了所有与管理器相关的命名空间和控制器。所以我回到了原来的 pre-controller-optimization 状态。

此错误仅发生在 Manger 模型中。在控制台中Manager.class 无论如何都会显示Class。 但是在控制器中会发生这种情况:

module Api
 module V1
  class Manager < Api::ApiBaseController
   def index
     puts User.class #=> class
     puts Manager.class #=> module 
     puts ::Manager.class #=> class
     puts Controller.class #=> class
     ...
   end
  end
 end
end


class Api::V1::Manager < Api::ApiBaseController
   def index
     puts User.class #=> class
     puts Manager.class #=> {"error":"uninitialized constant Api::V1::ManagerController::Manager"}
     puts ::Manager.class  
     puts Controller.class  
     ...
   end
 end

当我更改顺序以使 ::Manager 首先一切正常,然后类匹配

 class Api::V1::Manager < Api::ApiBaseController
   def index
     puts User.class #=> class
     ::puts Manager.class #=> class
     puts Manager.class  #=> class
     puts Controller.class  #=> class
     ...
   end
 end

命名空间Api::V1::... 适用于所有其他控制器。

【问题讨论】:

  • ManANger 字有错误。没事吧?
  • 啊是的只是一个错字;)
  • 可能是Manager模块id与Manager模型冲突,请尝试更改Manager模块名称
  • @arieljuod 是的,将名称从 app/model/manager.rb 更改为 app/model/manager_foo 让我可以使用 `ManagerFoo.find(...) 但我不确定我理解这意味着什么
  • 命名空间像模块一样工作,问题似乎是Manager是模棱两可的,你需要告诉Rails你想要什么Manager,我不确定是否有一个包装所有模型的命名空间,我将更改为 Api::V1::ManagerApi:: 或其他东西

标签: ruby-on-rails ruby ruby-on-rails-4


【解决方案1】:

原因很简单,答案就在于 Ruby 的常量解析机制。

基本上,拥有同名的模块和类是非常糟糕的主意。

但如果您确实需要为模块和类使用相同的名称,请务必正确引用它们。

意思是,如果您不想更改命名,使用 :: 引用 Manager 类是您唯一的解决方案

Rails 为开发和生产模式下的自动/预加载类添加了一些魔力,因此您可能会在不同的模式下面临不同的问题。

您可能想通读this official guide 在 Rails 中加载常量。

【讨论】:

  • 好的,非常感谢这篇超级有趣的文章!但我愿意重命名,但将管理器子资源的命名空间更改为 api/v1/controller/resources/user.rb 会给我同样的错误
  • @theDrifter 你可以很容易地在控制台中检查它:运行Manager.class - 直到它返回Module,而不是Class - 你仍然有一些名为Manager的模块。
  • @theDrifter 如果您在这里遇到任何新情况,请随时通知我
  • 嘿,所以Manager.class 每次都是class。我尝试在与manager.rb 相同的目录中创建另一个名为manager_user_controller.rb 的控制器,但出现相同的错误:当我启动/重新启动服务器时,控制器正在工作,但是在manager_*_controller.rb the autoload fails and uninitialized 常量Api 中填充了例如Manager.first: :V1::Managerresources::UserController::Manager` 或uninitialized constant Api::V1::ManagerUserController::Manager 错误弹出。当我将命名空间重命名为 Foo 时,也会出现这种行为
  • @theDrifter 这不是你在玩的小东西 ;) 查看this thread - Sergio 的回答很棒。
【解决方案2】:

你的控制器代码有误:

class Api::V1::ManagerController << ApplicationController

必须是:

class Api::V1::ManagerController < ApplicationController

不是&lt;&lt;,应该是&lt;

【讨论】:

  • 感谢您如此仔细地查看代码,但这是我这边的另一个错字
【解决方案3】:
api/v1/app/controller/manager.rb
api/v1/app/controller/manager/user.rb
api/v1/app/controller/manager/controller.rb
api/v1/app/controller/admin.rb
api/v1/app/controller/user.rb
api/v1/app/controller/controller.rb

将这一切改为:

app/controller/api/v1.......

你做错了命名空间。

【讨论】:

  • 你好,是的,我所有的控制器都在api/v1/app/controller/*下,我跳过了这个
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-19
  • 1970-01-01
  • 2012-05-14
  • 2023-03-11
  • 1970-01-01
  • 2012-07-08
  • 2011-07-06
相关资源
最近更新 更多