Ruby on Rails:alias_method_chain,它究竟做了什么?

时间:2010-09-12 17:43:19

标签: ruby-on-rails ruby

我已经尝试阅读各种博客文章,试图解释alias_method_chain以及使用它的原因而不使用它。特别是,我注意到了:

http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain

http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/

我仍然没有看到alias_method_chain的任何实际用途。任何人都可以解释一些事情。

1 - 它是否仍在使用?
2 - 什么时候使用alias_method_chain?为什么?

4 个答案:

答案 0 :(得分:104)

  

1 - 它还在使用吗?

显然是的,alias_method_chain()still used in Rails(从版本3.0.0开始)。

  

2 - 你什么时候用?   alias_method_chain和为什么?

注意: 以下内容主要基于Paolo Perrotta对Metaprogramming Ruby alias_method_chain()的讨论,你应该得到的优秀书籍。

让我们从一个基本的例子开始:

class Klass
  def salute
    puts "Aloha!"
  end
end

Klass.new.salute # => Aloha!

现在假设我们想要使用日志记录行为来包围Klass#salute()。我们可以做到Perrotta称之为别名的

class Klass
  def salute_with_log
    puts "Calling method..."
    salute_without_log
    puts "...Method called"
  end

  alias_method :salute_without_log, :salute
  alias_method :salute, :salute_with_log
end

Klass.new.salute
# Prints the following:
# Calling method...
# Aloha!
# ...Method called

我们定义了一个名为salute_with_log()的新方法,并将其别名为salute()。用于调用salute()的代码仍然有效,但它也会获得新的日志记录行为。我们还为原始salute()定义了一个别名,因此我们仍然可以在不记录的情况下致敬:

Klass.new.salute_without_log # => Aloha!

因此,salute()现在称为salute_without_log()。如果我们想要记录,我们可以调用salute_with_log()salute(),它们是同一方法的别名。困惑?好!

根据Perrotta的说法,这种别名在Rails中很常见:

  

再看一下Rails的另一个例子   以自己的方式解决问题。一些   版本之前,包含Rails代码   同一个成语的许多例子:a   围绕别名(155)用于添加   一个方法的功能,和旧的   该方法的版本已重命名为   就像是   method_without_feature()。除了   方法名称,每次都改变了   时间,这样做的代码是   总是一样的,全都重复   这个地方。在大多数语言中,你   无法避免那种重复。   在Ruby中,你可以撒上一些   元编程魔法超过你的   模式并将其提取到自己的模式中   方法......因而诞生了   alias_method_chain()

换句话说,您提供了原始方法foo()和增强型方法foo_with_feature(),最终您使用了三种方法:foo()foo_with_feature(),和foo_without_feature()。前两个包括功能,而第三个没有。 <{3}}不是为了复制这些别名,而是为你做所有的别名。

答案 1 :(得分:7)

答案 2 :(得分:5)

我不确定它是否已经过时了Rails 3,但它仍然在之前的版本中被广泛使用。

您可以在调用方法之前(或之后)使用它来注入某些功能,而无需修改调用该方法的任何位置。见这个例子:

module SwitchableSmtp
  module InstanceMethods
    def deliver_with_switchable_smtp!(mail = @mail)
      unless logger.nil?
        logger.info  "Switching SMTP server to: #{custom_smtp.inspect}" 
      end
      ActionMailer::Base.smtp_settings = custom_smtp unless custom_smtp.nil?
      deliver_without_switchable_smtp!(mail = @mail)
    end
  end
  def self.included(receiver)
    receiver.send :include, InstanceMethods
    receiver.class_eval do
      alias_method_chain :deliver!, :switchable_smtp
    end
  end
end

这是ActionMailer的一个补充,允许在每次调用deliver!时换掉SMTP设置。通过致电alias_method_chain,您可以定义一个方法deliver_with_switchable_smtp!,您可以在其中执行自定义内容,并在完成后从那里调用deliver_without_switchable_smtp!

alias_method_chain将旧版deliver!别名为您的新自定义方法,因此您的应用的其余部分甚至不知道deliver!现在也会自定义您的自定义内容。

答案 3 :(得分:3)

  

是否完全使用?

Seems so。这是Rails开发人员的常见做法

  

你何时会使用alias_method_chain?为什么?

尽管有警告,alias_method_chain仍然是向现有方法注入功能时使用的主要策略,至少在Rails 2.x中,并且后来有很多人在扩展它。 Yehuda应该从rails 3.0中删除alias_method_chain来说明他在Rails门票中的帖子和评论。它仍然被许多扩展使用,它们在执行的某些点添加自定义行为,例如记录器,错误报告器,基准测试,数据注入等。

IMO,最好的替代方案是包含一个模块,因此你可以装饰而不是委托。 (例如,按照this post中的示例4)。这样,如果您愿意,您甚至可以单独更改对象,而不会污染类的方法。这样做的缺点是方法查找链会为您注入的每个模块增加,但这就是模块的用途。

非常有趣的问题,将会关注其他人的想法。