简体   繁体   English

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

[英]Monkey patching Devise (or any Rails gem)

I'm using the Devise authentication gem in my Rails project, and I want to change the keys it's using in flash alerts. 我在我的Rails项目中使用Devise身份验证gem,我想更改它在闪存警报中使用的密钥。 (Devise uses :notice and :alert flash keys, but I want to change them to :success and :error so that I can display nice green/red boxes with Bootstrap .) (设计使用:通知和:警告闪存键,但我想将它们更改为:成功和:错误,以便我可以使用Bootstrap显示漂亮的绿色/红色框。)

So I want to be able to somehow override the set_flash_message method in DeviseController . 所以,我希望能够以某种方式覆盖set_flash_message方法DeviseController

Here's the new method: 这是新方法:

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

But I just don't know where to put it. 但我只是不知道该把它放在哪里。


UPDATE: 更新:

Based on an answer I created a config/initializers/overrides.rb file with the following code: 基于答案,我使用以下代码创建了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

But this causes an error on every Devise action: 但这会导致每个Devise操作出错:

Routing Error: undefined method 'prepend_before_filter' for Devise::SessionsController:Class 路由错误:Devise :: SessionsController:Class的未定义方法'prepend_before_filter'

If you try to reopen a class, it's the same syntax as declaring a new class: 如果您尝试重新打开一个类,则它与声明一个新类的语法相同:

class DeviseController
end

If this code is executed before the real class declaration, it inherits from Object instead of extending the class declared by Devise. 如果此代码在实际类声明之前执行,则它继承自Object而不是扩展Devise声明的类。 Instead I try to use the following 相反,我尝试使用以下内容

DeviseController.class_eval do
  # Your new methods here
end

This way, you'll get an error if DeviseController has not been declared. 这样,如果尚未声明DeviseController ,您将收到错误。 As a result, you'll probably end up with 结果,你可能会最终得到

require 'devise/app/controllers/devise_controller'

DeviseController.class_eval do
  # Your new methods here
end

Using Rails 4 @aceofspades answer didn't work for me. 使用Rails 4 @aceofspades的答案对我不起作用。

I kept getting require': cannot load such file -- devise/app/controllers/devise_controller (LoadError) 我一直得到要求': cannot load such file -- devise/app/controllers/devise_controller (LoadError)

Instead of screwing around with load order of initializers I used the to_prepare event hook without a require statement. 我没有使用require语句来使用to_prepare事件挂钩而不是使用初始化器的加载顺序。 It ensures that the monkey patching happens before the first request. 它确保猴子修补在第一次请求之前发生。 This effect is similar to after_initialize hook, but ensures that monkey patching is reapplied in development mode after a reload (in prod mode the result is identical). 此效果类似于after_initialize挂钩,但确保在重新加载后在开发模式下重新应用猴子修补(在prod模式下结果相同)。

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

NB the rails documentation on to_prepare is still incorrect: See this Github issue 注意to_prepare上的rails文档仍然不正确:请参阅此Github问题

What about adding in the override initializer and alias for the attributes of the flash hash, like this: 如何添加覆盖初始化程序和flash哈希属性的别名,如下所示:

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

This should allow your application to read flash[:notice] or flash[:success](flash.notice and flash.success) 这应该允许你的应用程序读取flash [:notice]或flash [:success](flash.notice和flash.success)

In your initializer file : 在初始化文件中:

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)

This is pretty brutal but will do what you want. 这是非常残酷的,但会做你想要的。 The mixin will delete the previous set_flash_message method forcing the subclasses to fall back to the mixin method. mixin将删除先前的set_flash_message方法,强制子类回退到mixin方法。

Edit: self.included is called when the mixin is included in a class. 编辑:当mixin包含在类中时调用self.included。 The klass parameter is the Class to which the mixin has been included. klass参数是包含mixin的Class。 In this case, klass is DeviseController, and we call remove_method on it. 在这种情况下,klass是DeviseController,我们在其上调用remove_method。

You need to overwrite DeviseController while keeping around its superclass, in your initializer. 您需要在初始化程序中覆盖DeviseController,同时保留其超类。

Something like: 就像是:

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

This is the kind of thing that you will want to put on initialize rails folder, because it's a custom config for this application in particular, second you should use like so: 这是你想要初始化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

then you should get the expected behavior. 那么你应该得到预期的行为。 hope it helps since i dont tested, of not pls give a feedback and i will help you try something diferent. 希望它有所帮助,因为我没有测试,不请给出反馈,我会帮助你尝试不同的东西。

I know this is an old thread but this might still be helpful. 我知道这是一个旧线程,但这可能仍然有用。 You should be able to require the file from the gem directory using the engine called_from path. 您应该能够使用引擎called_from path从gem目录中请求该文件。

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

  DeviseController.class_eval do
    # Your new methods here
  end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM