2012-05-06 69 views
19

我想使用导轨熟悉的助手,但功能稍有变化。我看到它的方式,我希望能够做一些事情,如:覆盖导轨助手访问原始

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 

我看到它的方式,我有三种选择:

  1. 猴子补丁:重新打开轨道助手模块。如果Rails团队改变了他们的帮手模块的名字,我的代码就变成了脆弱的源泉。不是无法克服的,但并不理想。
  2. 使用不同的方法名称:试图坚持共轨接口可能是我的失败。我的更改可能会成为其他开发人员混淆的来源
  3. 分离方法(新):不确定这是否可行,或者它是否与1具有相同的缺点。是否会研究此问题,但这可能是一个好结果初始点。

所以这里的问题是,我坚持这些次优解决方案之一,还是有另一种方式,我没有考虑?如果我去选项3,有没有办法做到这一点,而不直接处理铁轨辅助模块?

(注:我已删除的背景下,因为它增加了没什么问题)

回答

30

有一个比任何列出的选项更好的方法。只需使用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被调用,红宝石将它击中ActionView::Helpers::AssetTagHelper之前遇到它的方法查找路径。如果@be_awesometrue,那么您将负责并在那里停止执行任务,如果没有,则在没有括号的情况下调用super将透明地传递所有参数直至实现Rails。这样你就不必担心Rails的核心团队在你身上移动东西!

+0

你知道吗......这太疯狂了,我试图绞尽脑汁想出为什么我认为这是行不通的!我今晚会尝试它,如果它有效,我会用我的大脑说一些严厉的话,可能涉及到一堵砖墙。当然,接受你的答案后......:D – user208769

+1

@ user208769 Hehehe。棒极了。据我所知,在任何情况下,这种方式的优先方法通常是优选的。 [Class#ancestors](http://ruby-doc.org/core-1.9.3/Module.html#method-i-store)在确定劫持方法的方法查找路径中的一个好位置方面确实很有帮助调度(或者您的自定义模块与覆盖需要包含在最好的效果)。 – Cade

+0

什么? :) 你在开玩笑吧!这是一个巨大的困扰!你的方式,你必须在每个包括AssetTagHelper的类中包含你的帮手。时光飞逝,你或其他人可能忘记需要你的补丁包括在内。您只需包含AssetTagHelper并开始怀疑:为什么我的网站现在看起来不一样?当你和补丁制作者是同一个人时,这很好。但是,如果不是? – jdoe

6

我不使用这种宝石,所以我会回答你在一个更通用的方法。

假设你想将呼叫记录到link_to助手(是的,人为的例子,但显示了这个想法)。通过查看API,您可以了解位于ActionView::Helpers::UrlHelper模块内的link_to。所以,你在你的,比方说,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后使用它。

+0

呀,这个方法是我的意思是“猴子打补丁的具体轨道模块” - 这工作得很好,但如果Rails核心改变自己的模块名称,我的代码休息。这可能不是什么大问题,但我很好奇看看是否有其他解决方案。这就是说,忘记了alias_method_chain,谢谢你提醒我! – user208769

+0

P.S:已更新问题以删除该宝石示例。希望这种布局不那么令人困惑!谢谢。 – user208769

+0

风险始终存在!如果您担心'alias_method_chain',那么您不应该:从版本1.4.0(2007年)开始存在。如果你担心你的程序的其他部分,那么确保体面的测试覆盖率。 – jdoe

2

我真的会鼓励你考虑你的选项#2,以一种对调用者来说很明显的方式重写rails方法的行为。

您的新方法应该叫做awesome_stylesheet_link_tag,以便其他Rails开发人员可以读取您的代码并询问“链接标记有什么如此棒的事实?”。

作为一个较小的变化,你可以做覆盖,但通过:awesome => true作为一个参数,所以他们至少有一个线索,一些事情正在进行。

更改广泛使用的方法(如stylesheet_link_tag)的行为会造成潜在的未来误解,无需任何必要。

+0

感谢您的输入。虽然通常我都同意,在这种特殊情况下,我认为一致性是有道理的 - 我这样做是为了使用wicked_pdf,并使用完全相同的代码生成PDF或网页。虽然wicked_pdf在默认情况下会像您说的那样执行(wicked_pdf_stylesheet_link_tag),但它对我来说需要太多重复,而且我认为如果您生成PDF,功能可能会发生变化是可以接受的。但是你提出了一个很好的观点,并提供了一些便利的提示,所以谢谢。 – user208769

4

尝试使用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