method_missing是ruby元程式設計(metaprogramming)常用的手法。基本思想是通過實現呼叫不存在的方法,以便進行**。典型的例子是:activerecord的動態查詢(dynamic finder)。例如:我們有email屬性那麼就可以呼叫user.find_by_email('[email protected]'),雖然, activerecord::base並沒有乙個叫做find_by_email的方法。
respond_to? 並不如method_missing出名,常用在當需要確認乙個回饋物件需要確認,以便不會因為沒有反饋物件,而導致後面的呼叫出現錯誤。
下面是乙個應用這兩者的例子:
示例我們有類legislator class,現在,想要給它加乙個find_by_first_name('john')的動態呼叫。實現find(:first_name => 'john')的功能。
複製** **如下:
class legislator
#假設這是乙個真實的實現
def find(conditions = {})
end#在本身定義畢竟這是他的方法
def self.method_missing(method_sym, *arguments, &block)
# the first argument is a symbol, so you need to_s it if you want to pattern match
if method_sym.to_s =~ /^find_by_(.*)$/
find($1.to_sym => arguments.first)
else
super
endend
end那麼這個時候呼叫
複製** **如下:
legislator.respond_to?(:find_by_first_name)
將會提示錯誤,那麼繼續
複製** **如下:
class legislator
# 省略
# it's important to know object defines respond_to to take two paramgoohxslweters: the method to check, and whether to include private methods
# def self.respond_to?(method_sym, include_private = false)
if method_sym.to_s =~ /^find_by_(.*)$/
true
else
super
endend
end正如**注釋所述respond_to?需要兩個引數,如果,你沒有提供將會產生argumenterror。
相關反射 dry
如果我們注意到了這裡有重複的**。我們可以參考activerecord的實現封裝在activerecord::dynamicfindermatch,以便避免在method_missing和respond_to?中重複。
複製** **如下:
class legislatordynamicfindermatch
attr_accessor :attribute
def initialize(
if method_sym.to_s =~ /^find_by_(.*)$/
@attribute = $1.to_sym
endend
def match?
@attribute != nil
endendclass legislator
def self.method_missing(method_sym, *arguments, &block)
match = legislatordynamicfindermatch.new(method_sym)
if match.match?
find(match.attribute => arguments.first)
else
super
endend
def self.respond_to?(method_sym, include_private = false)
if legislatordynamicfindermatch.new(method_sym).match?
true
else
super
endend
end快取 method_missing
重複多次的method_missing可以考慮快取。
另外乙個我們可以向activerecord 學習的是,當定義mwww.cppcns.comethod_missing的時候,傳送 now-defined方法。如下:
複製** **如下:
class legislator
def self.method_missing(method_sym, *arguments, &block)
= legislatordynamicfindermatch.new(method_sym)
if match.match?
define_dynamic_finder(method_sym, match.attribute)
send(method_sym, arguments.first)
else
super
endend
protected
def self.define_dynamic_finder(finder, attribute)
class_eval <
def self.#(#) # def self.find_by_first_name(first_name)
find(:# => #) # find(:first_name => first_name)
end # end
ruby
endend測試
測試部分如下:
複製** **如下:
describe legislatordynamicfindermatch do
describe 'find_by_first_name' do
before do
& @match = legislatordynamicfindermatch.new(:find_by_first_name)
endit 'should h**e attribute :first_name' do
@match.attribute.should == :first_name
endit 'should be a match' do
@match.should be_a_match
endend
describe 'zomg' do
before do
@match = legislatordynamicfindermatch(:zomg)
endit 'should h**e nil attribute' do
@match.attribute.should be_nil
endit 'should not be a match' do
@match.should_not be_a_match
endend
end下面是 rspec 例子:
複製** **如下:
describe legislator, 'dynamic find_by_first_name' do
it 'should call find(:first_name => first_name)' do
legislator.should_receive(:find).with(:first_name => 'john')
legislator.find_by_first_name('john')
end
end
本文標題: ruby元程式設計之建立自己的動態方法
本文位址:
ruby 建立自己的Ruby語言gem軟體包
準備用ruby寫一套測試自動化框架,但是好久沒有看這個東西了!也忘了很多東西!所以開此blog記錄我的學習一點一滴 今天看了gem,寫寫自己的心得體會,如何來製作我的gem檔案 首先我的機器上已經安裝了ruby1.8.5,我先查了一下我的gem版本 1 gem v 0.9.0 gem install...
物件導向的程式設計之建立物件
物件的定義 無序屬性的集合,屬性的值可以是基本值 物件或者函式.每個物件都是基於乙個應用型別建立的,這個引用型別可以是內建的 例如objectarraymath 也可以是使用者自定義的.所有的物件都是繼承自object的,因此我們可以從object著手建立物件.通過new 關鍵字建立物件 var p...
Shell程式設計之庫的建立與使用
庫的建立方法與shell指令碼一樣,只不過庫沒有實際的執行起始點。在定義庫時需要做的就是在其中定義函式以用來被其他shell指令碼呼叫。庫中的函式可以呼叫其所在庫的其他函式,也可呼叫其他庫中的函式。庫通常沒有副檔名,庫也不應以 開頭 因為它們不是被作業系統呼叫執行,而是被其他的shell呼叫 將庫包...