`
hanqingwxf2008
  • 浏览: 51611 次
  • 性别: Icon_minigender_1
  • 来自: 青岛
社区版块
存档分类
最新评论

Evaluation in Ruby

    博客分类:
  • ruby
 
阅读更多

Ruby的evaluation是一个很重要的功能,它可以eval一个字符串或者一个block。在一些适宜的情况下使用它会得到一些“意外”的效果。

 

常用的eval

 

Ruby常用的evaluation有:class_eval (module_eval),instance_eval ,eval 。

 

这三种evaluation方法可以在不同的情况下使用:

 

1. class_eval

 

class_eval和module_eval是相同的,class_eval是module_eval的一个alias。

 

class_eval可以在一个mod的上下文中eval一个字符串或者一个block,常用于给一个类添加方法(实例方法):

 

Ruby代码  收藏代码
  1. class Thing  
  2. end  
  3.      
  4. a = %q{def hello() "Hello there!" end}  
  5. Thing.module_eval(a)  
  6. puts Thing.new.hello()  #=> "Hello there!"  
 

2. instance_eval

 

instance_eval可以在一个实例的上下文中eval一个字符串或者一个block:

 

Ruby代码  收藏代码
  1. class Klass  
  2.   def initialize  
  3.     @secret = 99  
  4.   end  
  5. end  
  6.   
  7. k = Klass.new  
  8. k.instance_eval { @secret }   #=> 99  

 

3. eval

 

eval是在当前上下文中eval一个字符串,如果指定一个binding,则在binding的上下文中eval。

 

Ruby代码  收藏代码
  1. def getBinding(str)  
  2.   return binding  
  3. end  
  4. str = "hello"  
  5. eval "str + ' Fred'"                      #=> "hello Fred"  
  6. eval "str + ' Fred'", getBinding("bye")   #=> "bye Fred"  
 

class_eval和instance_eval的不同 

 

首先注意到class_eval和instance_eval的不同主要在于执行上下文(context)的不同。class_eval在一个mod的上下文中执行,而instance_eval在一个实例的上下文中执行。

 

其次,他们的常用场景不同。class_eval的应用场景一般是“打开一个类”来做一些事情,比如增加方法,或者是include一个module。

 

Ruby代码  收藏代码
  1. String.class_eval do  
  2.   include ExtraMethods  
  3.   
  4.   def another_method  
  5.     ....  
  6.   end  
  7. end  
 

而instance_eval主要关注于一个实例。

 

Ruby代码  收藏代码
  1. class Paginator  
  2.   def initialize total_entires  
  3.     @total_entries = total_entires  
  4.     @page_index = 0  
  5.   end  
  6.   
  7.   def next  
  8.     @page_index += 1  
  9.   end  
  10. end  
  11. paginator = Paginator.new(100)  
  12. paginator.next  
  13. paginator.instance_eval "@page_index" #=> 1  
  14. paginator.instance_eval { @page_index } #=> 1  
 

当然,我们也同样可以通过class_eval给Paginator类增加一个方法来获取page_index。

 

在我们了解instance_eval和class_eval之前,很可能会误用它们,比如通过下面的方法给Paginator增加一个实例方法:

 

Ruby代码  收藏代码
  1. Paginator.instance_eval do  
  2.   def baz  
  3.     "baz"  
  4.   end  
  5. end  
 

结果:

 

Ruby代码  收藏代码
  1. Paginator.new.baz   #=> undefined method ‘baz’ for #<Foo:0x7dce8>  

 

但我们偶然发现:

 

Ruby代码  收藏代码
  1. Paginator.baz   #=> "baz"  
 

其实,如果我们了解ruby的对象模型,对这个结果并不会意外。我刚才已经说过:instance_eval关注于一个实例,它是在一个实例的上下文中执行的。Paginator类本身就是一个Class类的实例,所以Paginator.instance_eval做的就是给这个Class实例--Paginator--增加一个方法,也就是Paginator的类方法。

 

同样,我们可以通过instance_eval给任意类的实例增加方法,比如:

 

Ruby代码  收藏代码
  1. "good".instance_eval do   
  2.   def opposite  
  3.      "bad"  
  4.   end  
  5. end  
  6.   
  7. "good".opposite #=> "bad"  
 

这个opposite方法就是"good"实例的一个singleton method。

 

instance_eval和DSL

 

DSL的一个主要特点是可以在不同的上下文中执行,下面就是一个例子(窃取自Jay Field举的例子):

 

Ruby代码  收藏代码
  1. class SqlGenerator  
  2.   class << self  
  3.     def evaluate(&script)  
  4.       self.new.instance_eval(&script)  
  5.     end  
  6.   end  
  7.   
  8.   def multiply(arg)  
  9.     "select #{arg}"  
  10.   end  
  11.   
  12.   def two(arg=nil)  
  13.     "2#{arg}"  
  14.   end  
  15.   
  16.   def times(arg)  
  17.     " * #{arg}"  
  18.   end  
  19. end  
  20.   
  21. SqlGenerator.evaluate { multiply two times two }  
  22. #=> "select 2 * 2"  
  23.   
  24. class Calculator  
  25.   class << self  
  26.     def evaluate(&script)  
  27.       self.new.instance_eval(&script)  
  28.     end  
  29.   end  
  30.   
  31.   def multiply(arg)  
  32.     eval arg  
  33.   end  
  34.   
  35.   def two(arg=nil)  
  36.     "2#{arg}"  
  37.   end  
  38.   
  39.   def times(arg)  
  40.     " * #{arg}"  
  41.   end  
  42. end  
  43.   
  44. Calculator.evaluate { multiply two times two }  
  45. #=> 4  
  46.    
 

简单得介绍了一下ruby的evaluation,要了解更多,可以看下面这两篇文章:

 

http://www.infoq.com/articles/eval-options-in-ruby

 

http://blog.jayfields.com/2007/03/ruby-instanceeval-and-classeval-method.html

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics