天天看點

深入了解alias, alias_method和alias_method_chain

對于alias, alias_method, alias_method_chain的深入了解是有益的,因為rails3的源碼裡很多地方使用了alias_method_chain的魔法。 有人評論說alias_method_chain使用的過多不好,具體怎麼不好,是後話了,這篇文章集中在了解這3個方法上面。

如果想轉載本文,請注明出處,謝謝!請尊重别人的勞動成果,為建構豐富web原創内容做貢獻!

1. alias

   Ruby裡的關鍵字,用于定義方法或者全局變量的别名。 例子:

   class A

     def m1

       puts "m1"

     end

     alias m2 m1

    end

=> nil

a = A.new

=> #<A:0xb7ef5234>

 a.m1

m1

a.m2

在使用的時候,注意原有的方法名在最後位置,用空格分開。

2. alias_method

作用和alias差不多,是Module的一個私有執行個體方法,隻能用于給方法起别名,并且參數隻能是字元串或者符号(alias後面跟的直接是方法名,不是字元串也不是符号)。例子:

class B

  def b

    p "b"

  end

  alias_method :c, :b

end

=> B

b = B.new

=> #<B:0xb7ee75bc>

b.c

"b"

b.b

注意,alias_method的參數必須是字元串或者是符号,并且用逗号分隔。

3. alias_method_chain

是ActiveSupport的一個公有執行個體方法。同樣接受兩個參數,可以是符号,也可以是字元串,但要注意一下第1個參數才是原始方法(alias_method的第2個參數是原始方法)。例子:

class A

  def m1

    puts 'm1'

  def m1_with_m2

    puts "do something befor m1"

    m1_without_m2

    puts "do something after m2"

  alias_method_chain :m1, :m2

=> A

=> #<A:0xb7bd9820>

a.m1

do something befor m1

do something after m2

上面的代碼用alias或者alias_method也能完成:

class A  

  def m1  

    puts 'm1'  

  alias m1_without_m2 m1  

  def m1_with_m2  

    puts 'do something else'  

    m1_without_m2  

  end  

  alias m1 m1_with_m2  

那麼其原理也一目了然了:

當調用m1的時候, m1_with_m2會執行,  在puts "do something befor m1"之後,執行m1_without_m2,這個時候是執行了真正的m1方法。 這樣就形成了一個類似于AOP的行為。

也可以說,對外把m1方法隐藏起來了,對類外部,實際上把m1_with_m2改頭換面已經成為了另一個方法,隻是我們不知道而已,因為它還叫m1.

再來看看alias_method_chain的源碼:

def alias_method_chain(target, feature) 

  # Strip out punctuation on predicates or bang methods since 

  # e.g. target?_without_feature is not a valid method name. 

  aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1 

  yield(aliased_target, punctuation) if block_given?      

  with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" 

  alias_method without_method, target 

  alias_method target, with_method       

  case 

    when public_method_defined?(without_method) 

      public target 

    when protected_method_defined?(without_method) 

      protected target 

    when private_method_defined?(without_method) 

      private target 

  end 

一個道理。

更實際的例子:

在一些rails比較老的系統裡,搜尋功能的日期選擇可能會用到date_select,這個方法會生成類似于這樣的頁面元素:

search_form[start_from(1i)]年

search_form[start_from(2i)]月

search_form[start_from(3i)]日

把這樣的參數傳回去,就無法查詢到對應的日期。這個時候我們需要在背景得到查詢條件之後來處理日期,比如:

get_conditions 這個方法假如是得到頁面查詢條件的,它傳回一個數組,這個時候我們可以定義:

def get_conditions_with_handle_date

  puts "你可以在get_conditions方法執行前幹點别的,如果你願意"

  get_conditions_without_handle_date

  puts "get_conditions執行完了,我們可以在其後幹點别的,比如說處理日期"

  conditions.reject!{|condition|condition[0] =~ /\([1-3]i\)/}   # 把條件數組裡的1i,2i,3i之類的去掉。

  conditions << ["? <= #{@model.table_name}.created_at", @search.start_from] if @search.start_from  #給搜尋對象裡添加正确的查詢日期條件

  conditions << ["#{@model.table_name}.created_at < ?", @search.end_to + 1.day] if @search.end_to   #給搜尋對象裡添加正确的查詢日期條件

  #然後實施魔法

  alias_method_chain :get_conditions, :handle_date

這樣我們就搞定了。

上一篇: Linux IP Alias