Rails 3:a​​lias_method_chain还在使用吗?

mik*_*son 34 ruby ruby-on-rails

我只是阅读有关宝石/插件开发的Rails 3和对面跑去这篇文章,指出alias_method_chain不再使用.我可以看到该方法仍然存在于activesupport-3.0.0/lib/active_support/core_ext/module/aliasing.rb中.

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

仍然反映出在Rails 3的是要修改的ActiveRecord宝石/插件的最佳实践?

edg*_*ner 57

不,它已被模块中的方法覆盖和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
Run Code Online (Sandbox Code Playgroud)

Railscasts的 Ryan Bates 谈到了如何在Rails路由代码中使用它.我建议观看它,以及他的其他截屏视频.他们有能力将针织祖母变成Rails大师.

PS:为了纠正我原来答案中的一个基本错误,我会去Peeja.谢谢.

  • 我不相信这是正确的.根据我的理解和这个实验,模块的方法永远不会被调用:https://gist.github.com/664352 (5认同)

Pee*_*eja 19

通常,模块永远不能覆盖它所包含的类中的方法.这是因为模块包含就像子类一样.超类也不能覆盖它的子类的方法,也不期望它.

当一个模块包含在一个类中时,该模块就会在该类的祖先链中的类之后插入.调用 super将调用模块的实现.

class Something
  module PreExtension; end
  module PostExtension; end

  include PreExtension
  include PostExtension
end

Something.ancestors # => [Something, Something::PostExtension, Something::PreExtension, Object, Kernel]
Run Code Online (Sandbox Code Playgroud)

每当在a上调用一个方法时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]
Run Code Online (Sandbox Code Playgroud)

这就是为什么alias_method_chain首先存在的原因.如果将基本代码放在模块中不是一个选项,我不知道如何完成等效的alias_method_chain.