2012-07-10 56 views
1

$1.to_sym => args[0]($1.to_sym,*args,&block)在以下代碼行中做了什麼?ruby​​中的動態方法定義

class Table 
    def method_missing(id,*args,&block) 
    return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/ 
    return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/ 
    super 
    end 
    # ... 
end 

語境:
Ruport是一個Ruby報表庫。您可以使用Ruport ::數據表::類來創建表格數據,並將其轉換爲不同的格式文本,例如:

require 'ruport' 
table = Ruport::Data::Table.new :column_names => ["country" , "wine" ], :data => [["France" , "Bordeaux" ], ["Italy" , "Chianti" ], ["France" , "Chablis" ]] 
puts table.to_text 

⇒  
+--------------------+ 
| country | wine | 
+--------------------+ 
| France | Bordeaux | 
| Italy | Chianti | 
| France | Chablis | 
+--------------------+ 

比方說,你只選擇了法國葡萄酒並將其轉換爲逗號分隔值:

found = table.rows_with_country("France") 
found.each do |row| 
    puts row.to_csv 
end 

⇒ 
France, Bordeaux 
France, Chablis 

剛剛做的是在Ruport :: Data :: Table上調用名爲rows_with_country()的方法。但是這個班的作者怎麼知道你會有一個名爲country的專欄?事實是,作者不知道這一點。如果你看看Ruport,你會發現rows_with_country()和to_csv()是Ghost方法。 Ruport :: Data :: Table類有點像上面定義的那樣。

對rows_with_country()的調用變爲調用更傳統的 方法rows_with(:country),它將列名稱作爲參數-define- 。另外,對to_csv()的調用變成對as(:csv)的調用。如果方法 名稱不以這兩個前綴中的任何一個開頭,則Ruport將返回 以引發NoMethodError的Kernel#method_missing()。 (這就是 super關鍵字是。)

+0

簡短回答:它將第一個匹配組並將其轉換爲符號,在本例中爲「rows_with_」或「to_」尾隨值,並將其作爲方法參數傳遞,或將其用作散列鍵第一個參數爲method_missing。 – 2012-07-10 13:05:33

回答

2

讓我們來看看method_missing

def method_missing(id,*args,&block) 
    return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/ 
    return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/ 
    super 
end 

的必要的背景:method_missing被稱爲對象上沒有明確定義請求的方法的時候。 id將是一個叫做方法的符號; args將是參數的數組,並且block將是Proc如果有塊或nil

return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/ 

執行真正開始在端部,在if:它檢驗狀態

id.to_s =~ /to_(.*)/ 

這基本上不會對所調用的方法的字符串匹配正則表達式的。 x =~ y返回x的整數偏移量,其中y匹配,如果有的話,否則爲零。例如: -

> "abc" =~ /a/ 
=> 0 
> "abc" =~ /.$/ 
=> 2 
> "abc" =~ /\d/ 
=> nil 

記住0被視爲真理布爾條件,所以if只會考慮如果被調用函數的名稱與to_開始是真實的。方法名稱的其餘部分由(.*)捕獲。

現在,我們沒有明確地保存捕獲組,但是Ruby借用Perl,第一個捕獲組的內容將保存在$1,第二個保存在$2等中:

> "abc" =~ /a(.)(.)/ 
=> 0 
> $1 
=> "b" 
> $2 
=> "c" 

現在,回到這行:

return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/ 

因此,如果調用的方法的名稱是形式to_XYZ的,它會調用as()法設置爲:XYZ第一個參數,以及來自附加調用的其餘參數,以及通過的塊(如果有的話)。

要繼續:

return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/ 

這基本上是一樣的:如果方法名是一樣rows_with_ABC,然後調用rows_with()用哈希{:ABC => args[0]},其中args[0]是考慮到缺少方法調用的第一個參數。

+0

什麼是散列{:ABC => args [0]} – shailesh 2012-07-10 16:21:40

+1

@shaileish:它只是一個散列值,具有':ABC'映射到'args [0]'的單個鍵值映射。換句話說,如果您按照示例調用'table.rows_with_country(「France」)',則它將通過'method_missing'轉換爲調用'table.rows_with({:country =>「France」})''。 – Ashe 2012-07-10 23:10:04