2012-07-10 58 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