简体   繁体   English

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

[英]Ruby on Rails: alias_method_chain, what exactly does it do?

I've tried reading through various blog posts that attempt to explain alias_method_chain and the reasons to use it and not use it. 我已经尝试阅读各种博客文章,试图解释alias_method_chain以及使用它的原因而不使用它。 In particular, I took heed to: 特别是,我注意到:

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

and

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

I still do not see any practical use for alias_method_chain. 我仍然没有看到alias_method_chain的任何实际用途。 Would anyone be able to explain a few things. 任何人都可以解释一些事情。

1 - is it still used at all? 1 - 它还在使用吗?
2 - when would you use alias_method_chain and why? 2 - 你何时会使用alias_method_chain?为什么?

1 - is it still used at all? 1 - 它还在使用吗?

Apparently yes, alias_method_chain() is still used in Rails (as of version 3.0.0). 显然是的, alias_method_chain() 仍然在Rails中使用 (从3.0.0版开始)。

2 - when would you use alias_method_chain and why? 2 - 你何时会使用alias_method_chain?为什么?

( Note: the following is largely based on the discussion of alias_method_chain() in Metaprogramming Ruby by Paolo Perrotta, which is an excellent book that you should get your hands on. ) 注意: 以下内容主要基于Paolo Perrotta在Metaprogramming Ruby中alias_method_chain()的讨论,这是一本非常好的书,你应该开始使用。

Let's start with a basic example: 让我们从一个基本的例子开始:

class Klass
  def salute
    puts "Aloha!"
  end
end

Klass.new.salute # => Aloha!

Now suppose that we want to surround Klass#salute() with logging behavior. 现在假设我们想要使用日志记录行为来包围Klass#salute() We can do that what Perrotta calls an around alias : 我们可以做到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

We defined a new method called salute_with_log() and aliased it to salute() . 我们定义了一个名为salute_with_log()的新方法,并将其别名为salute() The code that used to call salute() still works, but it gets the new logging behavior as well. 用于调用salute()的代码仍然有效,但它也获得了新的日志记录行为。 We also defined an alias to the original salute() , so we can still salute without logging: 我们还定义了原始salute()的别名,所以我们仍然可以在没有记录的情况下致敬:

Klass.new.salute_without_log # => Aloha!

So, salute() is now called salute_without_log() . 因此, salute()现在称为salute_without_log() If we want logging, we can call either salute_with_log() or salute() , which are aliases of the same method. 如果我们想要记录,我们可以调用salute_with_log()salute() ,它们是同一方法的别名。 Confused? 困惑? Good! 好!

According to Perrotta, this kind of around alias is very common in Rails: 根据Perrotta的说法,这种别名在Rails中很常见:

Look at another example of Rails solving a problem its own way. 看看Rails以自己的方式解决问题的另一个例子。 A few versions ago, the Rails code contained many instances of the same idiom: an Around Alias (155) was used to add a feature to a method, and the old version of the method was renamed to something like method_without_feature() . 在几个版本之前,Rails代码包含许多相同成语的实例:使用Around Alias (155)向方法添加功能,并将方法的旧版本重命名为method_without_feature() Apart from the method names, which changed every time, the code that did this was always the same, duplicated all over the place. 除了每次都改变的方法名称之外,执行此操作的代码始终是相同的,并且遍布整个地方。 In most languages, you cannot avoid that kind of duplication. 在大多数语言中,您无法避免这种重复。 In Ruby, you can sprinkle some metaprogramming magic over your pattern and extract it into its own method... and thus was born alias_method_chain() . 在Ruby中,你可以在你的模式上撒上一些元编程魔法并将其提取到自己的方法中...因此诞生了alias_method_chain()

In other words, you provide the original method, foo() , and the enhanced method, foo_with_feature() , and you end up with three methods: foo() , foo_with_feature() , and foo_without_feature() . 换句话说,您提供原始方法foo()和增强方法foo_with_feature() ,最终得到三个方法: foo()foo_with_feature()foo_without_feature() The first two include the feature, while the third doesn't. 前两个包括功能,而第三个没有。 Instead of duplicating these aliases all around, alias_method_chain() provided by ActiveSupport does all the aliasing for you. 而不是复制这些别名,ActiveSupport提供的alias_method_chain()为您完成所有别名。

I'm not sure if it's gone out of style with Rails 3 or not, but it is still actively used in versions before that. 我不确定它是否已经过时了Rails 3,但它仍然在之前的版本中被积极使用。

You use it to inject some functionality before (or after) a method is called, without modifying any place that calls that method. 您可以在调用方法之前(或之后)使用它来注入某些功能,而无需修改调用该方法的任何位置。 See this example: 看这个例子:

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

That's an addition to ActionMailer to allow swapping out of the SMTP settings on each call to deliver! 这是ActionMailer的一个补充,允许在每次调用时交换SMTP设置来deliver! . By calling alias_method_chain you are able to define a method deliver_with_switchable_smtp! 通过调用alias_method_chain您可以定义方法deliver_with_switchable_smtp! in which you do your custom stuff, and call deliver_without_switchable_smtp! 你做自定义的东西,并调用deliver_without_switchable_smtp! from there when you're done. 当你完成的时候从那里开始。

alias_method_chain aliases the old deliver! alias_method_chain别名旧的deliver! to your new custom method, so the rest of your app doesn't even know deliver! 到你的新自定义方法,所以你的应用程序的其余部分甚至不知道deliver! now does your custom stuff too. 现在也做你的自定义的东西。

is it used at all? 是用的吗?

Seems so . 似乎是这样 It's a common practice among Rails developers 这是Rails开发人员的常见做法

when would you use alias_method_chain and why? 你何时会使用alias_method_chain?为什么?

Despite the warnings, alias_method_chain is still the main strategy used when injecting functionality to an existing method, at least was in Rails 2.x and is followed by many people extending it. 尽管有这些警告,alias_method_chain仍然是向现有方法注入功能时使用的主要策略,至少在Rails 2.x中,并且后来有很多人在扩展它。 Yehuda ought to remove alias_method_chain from rails 3.0 to say from his posts and comments in Rails tickets. Yehuda应该从rails 3.0中删除alias_method_chain来说明他在Rails门票中的帖子和评论。 It is still used by many extensions that add custom behavior at certain points of the execution, such as loggers, error reporters, benchmarking, data injection, etc. 它仍然被许多扩展使用,它们在执行的某些点添加自定义行为,例如记录器,错误报告器,基准测试,数据注入等。

IMO, the best alternative is to include a module, thus you have decoration over delegation. IMO,最好的选择是包含一个模块,因此你可以装饰而不是委托。 (For example, follow example 4 in this post ). (例如,按照实施例4中此信息 )。 That way you can alter the objects even individually if you'd like, without polluting the class' methods. 这样,如果您愿意,您甚至可以单独更改对象,而不会污染类的方法。 The downside to this is that the method lookup chain increases for each module you inject, but this is what modules are for anyway. 这样做的缺点是方法查找链会为您注入的每个模块增加,但这就是模块的用途。

Very interesting question, will keep a look on what other people think about it. 非常有趣的问题,将关注其他人的想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM