天天看點

Advanced Rails -- Ruby 基本技術(6) -- 元程式設計技巧1

[b]Metaprogramming Techniques[/b]

前面已經講述了Ruby的一些基礎,後面是在Rails裡面常見的元程式設計的技術。

盡管例子都是用Ruby書寫的,這些技術大部分可以是對任何動态語言适用的。實際上,Ruby的元程式設計的文法是在像Lisp,Smalltalk和Perl裡借鑒的。

[i]運作時方法查找[/i]

我們經常需要的是建立一個根據一些運作時的資料變化的函數的接口。最突出的例子是Rails裡面的ActiveRecord的屬性的accessor方法。對ActiveReord的方法調用會在運作時被轉化成屬性的入口。在類—方法的層面上,ActiveRecord提供了相當好的便利:Person.find_all_by_user_id_and_active(42, true) 被轉換成特定的SQL查詢,當屬性不存在的時候會引發一個标準的NoMethodError異常。

這個魔幻之處在于Ruby的method_missing方法。當一個不存在的方法被對象調用的時候,Ruby在引發标準的NoMethodError異常前首先檢查對象類有沒有method_missing方法。method_missing第一個參數是被調用的方法的名字;剩下的參數就是被調用的方法的參數。任何傳遞給那個方法的block會直接傳給method_missing。一個萬昌的函數簽名如下:

這裡有使用method_missing的缺點:

它比正常的方法查找速度慢。一些簡單的測試說明了帶有method_missing方法的分派至少要比常會的分派在時間上多花費2到3倍。

因為被調用的方法實際上不存在——這種方法僅僅是在方法查找的最後一步解析的——是以不能向正常的方法一樣被文檔化和自省。

因為所有的動态的方法必須要通過method_missing方法,如果有很多的代碼需要添加不同的方面動态的方法,method_missing方法的函數體會變的非常大。

使用method_missing限制了以後API的相容性。一旦你依靠method_missing去處理沒有方法定義的一些有趣的事情,在以後的API版本當加入新方法可能會破壞使用者的預料。

在ActiveRecord裡面用的generate_read_methods功能是一一個比較好的另一種方式。ActiveRecord不是等待method_missing去解析調用,而是生成一些屬性setter和reader的實作,這些實作可以像正常的方法分派一樣調用。

總體上來說,這是一個強大的方法,Ruby這種動态性讓Ruby編寫方法當方法第一次調用的時候用優化的版本替換自己的方法成為可能。Rails的routing需要快速響應,就用到了這個功能;這個在這一節的後面有描述。

[i]Generative Programming: Writing Code On-the-Fly[/i]

一個對于其他的強大的技術是自産生程式設計——代碼寫代碼

這個技術簡單的說,就是寫一個shell腳本去自動化一些單調程式設計。比如,你可能需要産生對每個使用者測試裝置。

如果有一種語言沒有可腳本化的測試裝置,你必須手工來做。如果資料增加,這會變的雜亂,而且當有些很奇怪的原資料依賴的時候,這幾乎不太可能手工做。單一的自産生程式設計可以讓你在原資料裡産生測試夾具。盡管不是很理想,這會在很大程度上改進手工書寫。但是比較頭疼的是維護:你必須把這個腳本合并在你的建立的過程裡來保證這個夾具會在源資料變化時重新産生。

這個很少見,但在ROR裡是需要的。幾乎ROR所有的應用配置是可腳本化的,很大部分用了内部的DSL。在内部的DSL,在你可以使用Ruby全部的能力,不僅僅是庫作者給你指定的特定接口。

回到先前的例子,ERb能讓這個工作變的很容易。可在上面YAML檔案裡加入Ruby的代碼(這些代碼用ERb<% %>和<%= %>标記)和任何我們需要的邏輯:

對于這個小技巧,ActiveRecord's再簡單不過了:

自産生變成常用 Module#define_method 或者 class_eval 和def區建立運作中的方法。ActiveRecord 對于屬性的 accessors使用了這些技巧;generate_read_method噢共能定義了setter and reader方法,這些方法是ActiveRecord class的執行個體方法,能夠減少使用times method_missing (a relatively expensive technique) 需要次數。

繼續閱讀