通过访问原始文件覆盖 rails helpers
Posted
技术标签:
【中文标题】通过访问原始文件覆盖 rails helpers【英文标题】:Override rails helpers with access to original 【发布时间】:2012-05-15 08:07:16 【问题描述】:我想使用 Rails 熟悉的助手,但功能略有改变。在我看来,我希望能够做这样的事情:
module AwesomeHelper
#... create alias of stylesheet_link_tag to old_stylesheet_link_tag
def stylesheet_link_tag(*args)
if @be_awesome
awesome_stylesheet_link_tag *args
else
old_stylesheet_link_tag *args
end
end
end
在我看来,我有三个选择:
-
猴子补丁:重新打开 rails helper 模块。如果 Rails 团队更改了他们的帮助模块的名称,我的代码就会变得脆弱。并非不可克服,但并不理想。
使用不同的方法名: 试图坚持通用的 Rails 接口可能是我的失败。我的更改可能会让其他开发者感到困惑
分离方法(新):不确定这是否可行,或者它是否具有与 1 相同的缺点。将对此进行研究,但这可能是一个很好的起点。
所以这里的问题是,我是坚持使用这些次优解决方案之一,还是有另一种我没有考虑过的方法?如果我选择选项 3,有没有办法在不直接解决 rails helper 模块的情况下做到这一点?
(注意:我已经删除了上下文,因为它对问题没有任何帮助。)
【问题讨论】:
【参考方案1】:尝试使用alias_method
:
module AwesomeHelper
alias_method :original_stylesheet_link_tag, :stylesheet_link_tag
def stylesheet_link_tag(*sources)
if @be_awesome
awesome_stylesheet_link_tag *sources
else
original_stylesheet_link_tag *sources
end
end
end
【讨论】:
【参考方案2】:我真的鼓励你考虑你的选项 #2,以一种对调用者来说显而易见的方式覆盖 rails 方法的行为。
您的新方法应该被称为awesome_stylesheet_link_tag
,以便其他 Rails 开发人员可以阅读您的代码并提出问题“链接标签有什么了不起的地方?”。
作为一个较小的更改,您可以进行覆盖,但将:awesome => true
作为参数传递,这样他们至少可以知道正在发生的事情。
更改stylesheet_link_tag
等广泛使用的方法的行为会在未来产生潜在的误解,而无需任何操作。
【讨论】:
感谢您的意见。虽然通常我同意,但在这种特殊情况下,我认为一致性是合理的——我这样做是为了使用 wicked_pdf 并让完全相同的代码生成 PDF 或网页。虽然默认情况下 wicked_pdf 会按照您所说的那样 (wicked_pdf_stylesheet_link_tag),但它需要太多重复,而且我认为如果您生成 PDF,功能可能会发生变化是可以接受的。但是您提出了一个很好的观点,并提供了一些方便的提示,所以谢谢。【参考方案3】:有比您列出的任何选项更好的方法。只需使用super
:
module AwesomeHelper
def stylesheet_link_tag(*sources)
if @be_awesome
awesome_stylesheet_link_tag *sources
else
super
end
end
end
在 AwesomeHelper 中覆盖 stylesheet_link_tag
将确保在调用 stylesheet_link_tag
时,Ruby 会在它到达 ActionView::Helpers::AssetTagHelper
之前在方法查找路径中遇到它。如果@be_awesome
是true
,那么您就可以负责并在此停止操作,如果不是,则对super
的不带括号的调用将透明地传递到Rails 实现的所有参数。这样,您就不必担心 Rails 核心团队会在您身上转移任何东西!
【讨论】:
你知道吗...这太明显了,我正在绞尽脑汁想弄清楚为什么我认为它不起作用!今晚我要试一试,如果有效,我的大脑会很严厉,可能涉及一堵砖墙。当然,在接受你的回答之后......:D @user208769 呵呵呵呵。棒极了。据我所知,以这种方式覆盖方法通常在任何情况下都是可取的。 Class#ancestors 非常有助于在方法查找路径中找出一个好的位置来劫持方法调度(或者需要包含具有覆盖的自定义模块以达到最佳效果)。 什么? :) 你在开玩笑吧!这是一个巨大的陷阱!你必须在每个包含 AssetTagHelper 的类中包含你的助手。时间飞逝,您或其他人可能会忘记需要包含您的补丁。您只需包含 AssetTagHelper 并开始怀疑:为什么我的网站现在看起来不同了?当你和补丁制造者是同一个人时,这很好。但如果没有呢? @jdoe 是的,你是对的。为了使用一个人的代码,它需要被包含在内。 :) 我不认为这是一个“陷阱”。在ApplicationController
中使用AbstractController::Helpers::ClassMethods::helper 可能有助于减轻您的顾虑。【参考方案4】:
我不使用这个gem,所以我会以更通用的方式回答你。
假设您想记录对link_to
helper 的调用(是的,人为的示例,但显示了这个想法)。查看 API 可以让您了解 link_to
位于 ActionView::Helpers::UrlHelper
模块内。因此,您在 config/initializers
目录中创建了一些文件,其内容如下:
# like in config/initializers/link_to_log.rb
module ActionView::Helpers::UrlHelper
def link_to_with_log(*args, &block)
logger.info '**** LINK_TO CALL ***'
link_to_without_log(*args, &block) # calling the original helper
end
alias_method_chain :link_to, :log
end
此功能的核心——alias_method_chain
(可点击)。在定义方法 xxx_with_feature
之后使用它。
【讨论】:
是的,这种方法就是我所说的“猴子修补特定的 Rails 模块”的意思——效果很好,但是如果 Rails 核心更改了它们的模块名称,我的代码就会中断。这可能没什么大不了的,但我很好奇是否有其他解决方案。也就是说,忘记了 alias_method_chain,谢谢你提醒我! PS:已更新问题以删除 gem 示例。希望这种布局不那么混乱!谢谢。 风险永远存在!如果您担心alias_method_chain
,那么您不应该:它自版本 1.4.0(2007 年)以来就存在。如果您担心程序的其他部分,那么请确保获得不错的测试覆盖率。
已弃用。 ***.com/questions/3689736/…以上是关于通过访问原始文件覆盖 rails helpers的主要内容,如果未能解决你的问题,请参考以下文章
使用 Rails 3.2.11 和 RSpec 发布原始 JSON 数据