Monkey patching Devise(或任何 Rails gem)
Posted
技术标签:
【中文标题】Monkey patching Devise(或任何 Rails gem)【英文标题】:Monkey patching Devise (or any Rails gem) 【发布时间】:2013-06-11 16:52:39 【问题描述】:我在我的 Rails 项目中使用 Devise 身份验证 gem,我想更改它在 Flash 警报中使用的密钥。 (Devise 使用 :notice 和 :alert flash 键,但我想将它们更改为 :success 和 :error 以便我可以用Bootstrap 显示漂亮的绿色/红色框。)
所以我希望能够以某种方式覆盖DeviseController 中的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
但这会导致每个设计操作出错:
路由错误:未定义的方法“prepend_before_filter” 设计::SessionsController:Class
【问题讨论】:
您可能需要声明 DeviseController 的文件。我通常会使用DeviseController.class_eval
而不是重新打开该类以确保它已被声明。
@aceofspades- 你能把这个扩展成答案吗?我以前没有用过 .class_eval ,想看看你的想法..
【参考方案1】:
如果你尝试重新打开一个类,它的语法与声明一个新类的语法相同:
class DeviseController
end
如果这段代码在真正的类声明之前执行,它继承自 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
【讨论】:
是的,终于可以了。有趣的是,我所要做的就是使用DeviseController.class_eval do
-不需要devise_controller,它就可以工作。所以...我会继续努力的-谢谢!
奇怪的是,我无法使用 ruby 2.0 和 rails 4 来解决这个问题。@redxvii 的更复杂的答案也应该可以,但不能。失败警报的类别仍然是“alert”而不是“error”。
在部分中使用纯红宝石帮助了我:<div class="alert alert-<%= name == :notice ? "success" : "error" %>">
@aceofspades 我觉得我今天在跟踪你,你对我正在阅读的所有 Rubymotion 宝石都感兴趣。这对我不起作用,我收到 require': cannot load such file -- devise/app/controllers/devise_controller (LoadError)
@Dan2552 只需确保它可以根据标准 ruby 找到控制器类定义。您可以尝试对整个路径进行硬编码以使其正常工作,或者引用加载的 gem 目录。【参考方案2】:
使用 Rails 4 @aceofspades 答案对我不起作用。
我不断收到 require':cannot load such file -- devise/app/controllers/devise_controller (LoadError)
我没有搞乱初始化程序的加载顺序,而是使用了 to_prepare
事件钩子,没有 require 语句。它确保猴子修补发生在第一个请求之前。此效果类似于after_initialize
钩子,但确保重新加载后在开发模式下重新应用猴子补丁(在生产模式下结果相同)。
Rails.application.config.to_prepare do
DeviseController.class_eval do
# Your new methods here
end
end
注意to_prepare
上的 Rails 文档仍然不正确:请参阅此 Github issue
【讨论】:
【参考方案3】:在你的初始化文件中:
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。
【讨论】:
谢谢 - 你能提供一些关于def self.included klass
方法的 cmets,解释正在发生的事情吗?我以前从未见过这种情况
@RedXVII-感谢您的解决方案和解释-虽然这确实有效,但我将使用 aceofspades 更简单的解决方案。谢谢-【参考方案4】:
为闪存哈希的属性添加覆盖初始化器和别名怎么样,如下所示:
class ActionDispatch::Flash::FlashHash
alias_attribute :success, :notice
alias_attribute :error, :alert
end
这应该允许您的应用程序读取 flash[:notice] 或 flash[:success](flash.notice 和 flash.success)
【讨论】:
谢谢-虽然这是示例问题的不同解决方案,但我的问题特别是关于如何修补 gems。【参考方案5】:您需要在初始化器中覆盖 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
【讨论】:
使用您的代码,我仍然在所有设计操作中遇到错误,尽管是不同的:NameError in Devise::SessionsController#new undefined local variable or method 'require_no_authentication' for #<Devise::SessionsController:0x00000102907e20>
【参考方案6】:
这是你想要放在初始化 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
那么你应该得到预期的行为。 希望对您有所帮助,因为我没有测试过,请提供反馈,我会帮助您尝试不同的东西。
【讨论】:
我将上面的代码添加到名为 config/initializers/overrides.rb 的文件中,但现在我的应用程序抛出错误:Routing Error: undefined method 'prepend_before_filter' for Devise::SessionsController:Class
【参考方案7】:
我知道这是一个旧线程,但这可能仍然有帮助。您应该能够使用名为_from 路径的引擎从 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 做 # 你的新方法在这里 结尾【讨论】:
以上是关于Monkey patching Devise(或任何 Rails gem)的主要内容,如果未能解决你的问题,请参考以下文章
typescript Monkey Patch Observable +错误抛出
python gevent MonkeyPatchWarning: Monkey-patching ssl after