猴子修补Devise(或任何Rails gem)

我在我的Rails项目中使用Devise身份validationgem,我想更改它在闪存警报中使用的密钥。 (设计使用:通知和:警告闪存键,但我想将它们更改为:成功和:错误,以便我可以使用Bootstrap显示漂亮的绿色/红色框。)

所以我希望能够以某种方式覆盖set_flash_message中的set_flash_message方法。

这是新方法:

 def set_flash_message(key, kind, options = {}) if key == 'alert' key = 'error' elsif key == 'notice' key = 'success' end message = find_message(kind, options) flash[key] = message if message.present? end 

但我只是不知道该把它放在哪里。


更新:

基于答案,我使用以下代码创建了config / initializers / overrides.rb文件:

 class DeviseController def set_flash_message(key, kind, options = {}) if key == 'alert' key = 'error' elsif key == 'notice' key = 'success' end message = find_message(kind, options) flash[key] = message if message.present? end end 

但这会导致每个Devise操作出错:

路由错误:Devise :: SessionsController:Class的未定义方法’prepend_before_filter’

如果您尝试重新打开一个类,则它与声明一个新类的语法相同:

 class DeviseController end 

如果此代码在实际类声明之前执行,则它inheritance自Object而不是扩展Devise声明的类。 相反,我尝试使用以下内容

 DeviseController.class_eval do # Your new methods here end 

这样,如果未声明DeviseController ,您将收到错误。 结果,你可能会最终得到

 require 'devise/app/controllers/devise_controller' DeviseController.class_eval do # Your new methods here end 

使用Rails 4 @aceofspades的答案对我不起作用。

我一直得到要求’: cannot load such file -- devise/app/controllers/devise_controller (LoadError)

我没有使用require语句来使用to_prepare事件挂钩而不是使用初始化器的加载顺序。 它确保猴子修补在第一次请求之前发生。 此效果类似于after_initialize挂钩,但确保在重新加载后在开发模式下重新应用猴子修补(在prod模式下结果相同)。

 Rails.application.config.to_prepare do DeviseController.class_eval do # Your new methods here end end 

注意to_prepare上的rails文档仍然不正确:请参阅此Github问题

如何在flash哈希的属性中添加覆盖初始值设定项和别名,如下所示:

 class ActionDispatch::Flash::FlashHash alias_attribute :success, :notice alias_attribute :error, :alert end 

这应该允许你的应用程序读取flash [:notice]或flash [:success](flash.notice和flash.success)

在初始化文件中:

 module DeviseControllerFlashMessage # This method is called when this mixin is included def self.included klass # klass here is our DeviseController klass.class_eval do remove_method :set_flash_message end end protected def set_flash_message(key, kind, options = {}) if key == 'alert' key = 'error' elsif key == 'notice' key = 'success' end message = find_message(kind, options) flash[key] = message if message.present? end end DeviseController.send(:include, DeviseControllerFlashMessage) 

这是非常残酷的,但会做你想要的。 mixin将删除先前的set_flash_message方法,强制子类回退到mixin方法。

编辑:当mixin包含在类中时调用self.included。 klass参数是包含mixin的Class。 在这种情况下,klass是DeviseController,我们在其上调用remove_method。

您需要在初始化程序中覆盖DeviseController,同时保留其超类。

就像是:

 class DeviseController < Devise.parent_controller.constantize def set_flash_message(key, kind, options = {}) if key == 'alert' key = 'error' elsif key == 'notice' key = 'success' end message = find_message(kind, options) flash[key] = message if message.present? end end 

这是你想要初始化rails文件夹的那种东西,因为它特别是这个应用程序的自定义配置,其次你应该这样使用:

 class DeviseController def set_flash_message(key, kind, options = {}) if key == 'alert' key = 'error' elsif key == 'notice' key = 'success' end message = find_message(kind, options) flash[key] = message if message.present? end end 

那么你应该得到预期的行为。 希望它有所帮助,因为我没有测试过,请不要给予反馈,我会帮助你尝试不同的东西。

我知道这是一个旧线程,但这可能仍然有用。 您应该能够使用引擎called_from path从gem目录中获取该文件。

  需要File.expand_path('../../ app / helpers / devise_helper',Devise :: Engine.called_from)
  需要File.expand_path('../../ app / controllers / devise_controller',Devise :: Engine.called_from)

   DeviseController.class_eval做
     #你的新方法在这里
  结束