Devise有一个名为devise_group
的便捷功能(链接到documentation),可以创建一个包含多个设计模型的组。
文档真的是自我解释的。如果您有两个名为的设计模型,例如Admin
和User
,则可以像devise_group
那样使用:
class ApplicationController < ActionController::Base
devise_group :blogger, contains: [:user, :admin]
end
这将为authenticate_user!
和authenticate_admin!
提供方法authenticate_blogger!
,除非用户或管理员已登录,否则会重定向。
我们一直在生产中使用它,效果很好。我们可以灵活地使用authenticate_admin!
将某些控制器/操作限制为管理员,并在我们同时访问它时使用authenticate_blogger!
。
由于我们有一些复杂的业务逻辑,我们不得不在ApplicationController中覆盖authenticate_user!
,遵循这个很好的StackOverflow回答here。
基本上它建议覆盖ApplicationController#authenticate_user!当我们希望流程遵循Devise时,调用super
。
当我们尝试使用`authenticate_blogger!做同样的解决方案时,出现了问题。如果我们这样做:
class ApplicationController < ActionController::Base
devise_group :blogger, contains: [:user, :admin]
def authenticate_blogger!
super
end
end
// Another controller
class DashboardController < ApplicationController
before_action :authenticate_blogger!
end
Rails引发了这个错误:
super: no superclass method `authenticate_blogger!' for #<DashboardController:0x00007fd453ca5d80> Did you mean? authenticate_user!
任何想法为什么在ApplicationController中覆盖authenticate_user!
的覆盖范围内的super都可以正常工作,但同样的设计组不会发生同样的情况吗?
编辑1:找出原因,但可以使用一些帮助改进解决方案
查看devise source code,devise_group
使用Ruby class_eval
在其调用的类的上下文中定义实例方法,如authenticate_blogger!
。
因此,当我们在devise_group
中使用ApplicationController
时,就像我们将authenticate_blogger!
定义为ApplicationController中的实例方法一样。
这就是为什么当我们在ApplicationController中手动定义方法authenticate_blogger!
并调用super
时它会引发异常,因为我们实际上在同一个类(ApplicationController)中覆盖了相同的实例方法,在祖先链中找不到任何东西。
authenticate_user!
位于Devise::Controllers::Helpers
的祖先链中(我可以看到它调用ApplicationController.ancestors`。
我们所做的hacky-proof-of-concept-fix是创建一个TempController
,在其中定义devise_group
,并使ApplicationController继承它:
class TempController < ActionController::Base
devise_group :advertiser, contains: [:user, :broker]
end
// In application_controller.rb
class ApplicationController < TempController
def authenticate_blogger!
super // this now works since it goes up in the ancestor chain and finds authenticate_blogger in TempController
end
end
即使很难对我的调查感到高兴...有关修复此问题的任何建议,而不必让ApplicationController不继承ActionController :: Base,就像它在Rails中的默认值一样?