【问题标题】:Rails 3: alias_method_chain still used?Rails 3:a​​lias_method_chain 还在使用吗?
【发布时间】:2011-04-11 00:34:21
【问题描述】:

我刚刚阅读了有关 Rails 3 的 Gems/Plugin 开发的信息,遇到了 this post,它说不再使用 alias_method_chain。我可以在activesupport-3.0.0/lib/active_support/core_ext/module/aliasing.rb中看到该方法。

我还应该在 Rails 3 中使用 alias_method_chain 吗?

this 是否仍然反映了 Rails 3 中想要修改 ActiveRecord 的 gem/插件的最佳实践?

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    不,它已被巧妙地使用模块中的方法覆盖和super 关键字所取代。

    基本上,您在包含的模块中定义原始函数,并在另一个包含的模块中覆盖它。当您在覆盖函数中调用super 时,它会调用原始函数。但是有一个问题。您必须在包含基本模块之后包含扩展模块,并且按照您希望链接发生的顺序。

    class Something
      module Base  
        def my_method
          # (A) original functionality
        end
      end
    
      module PreExtension
        def my_method
          # (B) before the original
          super # calls whatever was my_method before this definition was made
        end
      end
    
      module PostExtension
        def my_method
          super # calls whatever was my_method before this definition was made
          # (C) after the original
        end
      end
    
      include Base # this is needed to place the base methods in the inheritance stack
      include PreExtension # this will override the original my_method
      include PostExtension # this will override my_method defined in PreExtension
    end
    
    s = Something.new
    s.my_method 
    #=> this is a twice extended method call that will execute code in this order:
    #=> (B) before the original
    #=> (A) the original
    #=> (C) after the original
    

    Railscasts 的 Ryan Bates 谈到了 how this is used in the Rails Routing code。我建议观看它,以及他的其他截屏视频。他们有能力将编织祖母变成 Rails 大师。

    PS:感谢 Peeja 纠正了我原始答案中的一个基本错误。谢谢。

    【讨论】:

    • 甜蜜!谢谢。我已经看过大部分我还没有看过的 Railscast。
    • 我不认为这是正确的。根据我的理解和这个实验,模块的方法永远不会被调用:gist.github.com/664352
    【解决方案2】:

    一般来说,模块永远不能覆盖类中的方法 它包含在其中。这是因为模块包含仅适用于 像子类化。超类不能覆盖其子类' 方法,你也不希望它。

    当一个模块被包含在一个类中时,该模块被插入 就在类的祖先链中的类之后。打电话 class 中的super 将调用 module 的实现。

    class Something
      module PreExtension; end
      module PostExtension; end
    
      include PreExtension
      include PostExtension
    end
    
    Something.ancestors # => [Something, Something::PostExtension, Something::PreExtension, Object, Kernel]
    

    每当在 Something 上调用方法时,Ruby 都会查看 这个列表按顺序调用它找到的第一个实现。 如果实现调用super,它会继续查找并找到 下一个。

    这意味着后面包含的模块优先于 较早包含的模块,并且可以调用super 获取较早的 模块的实现。这是因为包含的模块是 插入到祖先链中直接在类之后。这 是提到的路由代码 edgerunner 是如何工作的。那个代码 将所有内容放在模块中,如下所示:

    class SomethingNew
      module Base
        def my_method
          puts "(A)"
        end
      end
    
      module Extension
        def my_method
          puts "(B)"
          super
        end
      end
    
      include Base
      include Extension
    end
    
    SomethingNew.new.my_method
    # Output:
    # >> (B)
    # >> (A)
    
    SomethingNew.ancestors # => [SomethingNew, SomethingNew::Extension, SomethingNew::Base, Object, Kernel]
    

    这就是为什么 alias_method_chain 首先存在的原因。如果不能将基本代码放入模块中,我不确定如何完成 alias_method_chain 的等效操作。

    【讨论】:

    • 这是一个很好的答案,只是希望你能回答上一个问题
    • 其实我相信没有别的办法了。这就是 Rails 3 使用module Base 模式的原因。这是 wycats 在 Rails 3 中的一大推动力。如果你仔细想想,包含的模块(或超类)应该能够违背其意愿更改基类的实现是没有意义的。
    • 嗯,有时您想扩展您无法触及的代码。没有alias_method_chain,我看不到解决方案。
    • 没有。但这被认为是黑客攻击。在紧要关头很有用,玩起来很有趣,但不是你应该依赖于一个严肃的库的东西(因此不再是 Rails 使用的东西)。如果您仍然喜欢alias_method_chain 的便利性,请查看facets gem,它仍然提供它:rubyworks.github.com/facets/doc/api/core/Module.html
    【解决方案3】:

    我发现 alias_method_chain 在 Rails 3.0.0 中不再存在。 http://api.rubyonrails.org/ 不报告它,rails console 报告它是 undefined local variable or method

    另请参阅 - https://rails.lighthouseapp.com/projects/8994/tickets/285-alias_method_chain-limits-extensibility#ticket-285-20

    更新:@ecoologic 在 cmets 中指出,alias_method_chain 仍然存在于 Rails 3.1.1 中。

    【讨论】:

    • 不客气,我的上一个。评论听起来有点粗鲁,对不起,这不是我的意图
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-06
    • 2013-08-11
    • 1970-01-01
    • 2011-08-02
    • 2018-07-01
    • 2023-04-04
    相关资源
    最近更新 更多