如果字串實際上是一段程式碼,Ruby可以使用eval方法執行。例如:
>> eval("a = 1; puts a")
1
=> nil
>> eval("def some; puts 'some'; end")
=> nil
>> eval("some")
some
=> nil
>>
1
=> nil
>> eval("def some; puts 'some'; end")
=> nil
>> eval("some")
some
=> nil
>>
在哪個環境下執行eval,預設就會取得哪個環境中的區域變數。例如:
>> STATEMENT = "puts a"
=> "puts a"
>> a = 10
=> 10
>> eval(STATEMENT)
10
=> nil
>> def some
>> a = 20
>> eval(STATEMENT)
>> end
=> nil
>> some
20
=> nil
>>
=> "puts a"
>> a = 10
=> 10
>> eval(STATEMENT)
10
=> nil
>> def some
>> a = 20
>> eval(STATEMENT)
>> end
=> nil
>> some
20
=> nil
>>
eval的第二個參數可以接受Binding實例,代表執行時區域變數綁定的環境,可以使用binding方法取得當時執行時環境的Binding實例,指定了Binding實例,就可以取得Binding實例綁定的區域變數。例如:
>> def other(b)
>> a = 30
>> eval(STATEMENT, b)
>> end
=> nil
>> other(binding)
10
=> nil
>>
>> a = 30
>> eval(STATEMENT, b)
>> end
=> nil
>> other(binding)
10
=> nil
>>
物件有個instance_eval,可以將指定的程式區塊,視作為實例方法環境中執行的程序。例如:
>> class Some
>> def initialize(value)
>> @value = value
>> end
>> end
=> nil
>> s1 = Some.new(10)
=> #<Some:0x87eca0 @value=10>
>> s2 = Some.new(20)
=> #<Some:0x288fc48 @value=20>
>> s1.instance_eval { puts @value }
10
=> nil
>> s2.instance_eval { puts @value }
20
=> nil
>>
>> def initialize(value)
>> @value = value
>> end
>> end
=> nil
>> s1 = Some.new(10)
=> #<Some:0x87eca0 @value=10>
>> s2 = Some.new(20)
=> #<Some:0x288fc48 @value=20>
>> s1.instance_eval { puts @value }
10
=> nil
>> s2.instance_eval { puts @value }
20
=> nil
>>
所以若你也知道物件內部實作,而你也願意,就可以偷出private來執行。例如:
>> class Some
>> private
>> def private_mth
>> puts "private mth"
>> end
>> end
=> nil
>> s3 = Some.new(30)
=> #<Some:0x2795250 @value=30>
>> s3.instance_eval { private_mth }
private mth
=> nil
>> s3.private_mth
NoMethodError: private method `private_mth' called for #<Some:0x2795250 @value=30>
from (irb):34
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
>> private
>> def private_mth
>> puts "private mth"
>> end
>> end
=> nil
>> s3 = Some.new(30)
=> #<Some:0x2795250 @value=30>
>> s3.instance_eval { private_mth }
private mth
=> nil
>> s3.private_mth
NoMethodError: private method `private_mth' called for #<Some:0x2795250 @value=30>
from (irb):34
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
有個與instance_eval類似的方法是instance_exec,這個方法可以帶有引數,指定的引數會成為區塊參數的值。例如:
>> s3.instance_exec("arg") { |p| private_mth; puts p }
private mth
arg
=> nil
>>
private mth
arg
=> nil
>>
類別有個class_eval方法,可以將指定的程式區塊置於類別環境中執行。例如:
>> Some.class_eval do
?> def self.class_mth
>> puts "class_mth"
>> end
>> end
=> nil
>> Some.class_mth
class_mth
=> nil
>>
?> def self.class_mth
>> puts "class_mth"
>> end
>> end
=> nil
>> Some.class_mth
class_mth
=> nil
>>
有了class_eval方法,你可以開啟任何類別追加定義,即使是單例類別或匿名類別。例如:
>> o = Object.new
=> #<Object:0x2699340>
>> o.singleton_class.class_eval do
?> def some
>> puts "some"
>> end
>> end
=> nil
>> o.some
some
=> nil
>> x = Class.new {
?> def other
>> puts "other"
>> end
>> }.new
=> #<#<Class:0x2549f70>:0x2549ef8>
>> x.other
other
=> nil
>> x.class.class_eval do
?> def xyz
>> puts "xyz"
>> end
>> end
=> nil
>> x.xyz
xyz
=> nil
>>
=> #<Object:0x2699340>
>> o.singleton_class.class_eval do
?> def some
>> puts "some"
>> end
>> end
=> nil
>> o.some
some
=> nil
>> x = Class.new {
?> def other
>> puts "other"
>> end
>> }.new
=> #<#<Class:0x2549f70>:0x2549ef8>
>> x.other
other
=> nil
>> x.class.class_eval do
?> def xyz
>> puts "xyz"
>> end
>> end
=> nil
>> x.xyz
xyz
=> nil
>>
由於程式區塊可以使用區塊外的區域變數,所以instance_eval、class_eval方法,開啟了將區域變數帶入實例環境或類別環境的一種方式。例如:
>> a = 10
=> 10
>> class Some
>> private
>> def some(p)
>> puts p
>> end
>> end
=> nil
>> s = Some.new
=> #<Some:0x2821230>
>> s.instance_eval { some(a) }
10
=> nil
>> class Other
>> puts a
>> end
NameError: undefined local variable or method `a' for Other:Class
from (irb):17:in `<class:Other>'
from (irb):16
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> class Other; end
=> nil
>> Other.class_eval { puts a }
10
=> nil
>>
=> 10
>> class Some
>> private
>> def some(p)
>> puts p
>> end
>> end
=> nil
>> s = Some.new
=> #<Some:0x2821230>
>> s.instance_eval { some(a) }
10
=> nil
>> class Other
>> puts a
>> end
NameError: undefined local variable or method `a' for Other:Class
from (irb):17:in `<class:Other>'
from (irb):16
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> class Other; end
=> nil
>> Other.class_eval { puts a }
10
=> nil
>>
不過要注意,只要是def,一定是開啟一個新的作用範圍。所以:
>> a = 10
=> 10
>> class Some; end
=> nil
>> Some.class_eval do
?> def some
>> puts a
>> end
>> end
=> nil
>> Some.new.some
NameError: undefined local variable or method `a' for #<Some:0x26c9940>
from (irb):5:in `some'
from (irb):8
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
=> 10
>> class Some; end
=> nil
>> Some.class_eval do
?> def some
>> puts a
>> end
>> end
=> nil
>> Some.new.some
NameError: undefined local variable or method `a' for #<Some:0x26c9940>
from (irb):5:in `some'
from (irb):8
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
上例僅僅是等同以下寫法而已:
a = 10
class Some
def some
puts a
end
end
Some.new.some
class Some
def some
puts a
end
end
Some.new.some
然而,你可以使用define_method來動態建立實例方法。例如:
>> a = 10
=> 10
>> class Some; end
=> nil
>> Some.class_eval do
?> define_method("some") { puts a }
>> end
=> #<Proc:0x25f7328@(irb):4 (lambda)>
>> Some.new.some
10
=> nil
>>
=> 10
>> class Some; end
=> nil
>> Some.class_eval do
?> define_method("some") { puts a }
>> end
=> #<Proc:0x25f7328@(irb):4 (lambda)>
>> Some.new.some
10
=> nil
>>
以上卻是標準class建立類別時無法作到的事情:
>> a = 10
=> 10
>> class Some
>> define_method("some") { puts a }
>> end
=> #<Proc:0x25fec30@(irb):6 (lambda)>
>> Some.new.some
NameError: undefined local variable or method `a' for #<Some:0x25686d8>
from (irb):6:in `block in <class:Some>'
from (irb):8
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
=> 10
>> class Some
>> define_method("some") { puts a }
>> end
=> #<Proc:0x25fec30@(irb):6 (lambda)>
>> Some.new.some
NameError: undefined local variable or method `a' for #<Some:0x25686d8>
from (irb):6:in `block in <class:Some>'
from (irb):8
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>