`

ruby的动态性 eval 家族

 
阅读更多
eval
将字符串作为代码来求值,最直接,也是最危险的方法。
编写一个让别人在运行时键入方法名的方法:

print "Method name:"
m = gets.chomp
eval("def #{m}; puts 'Hi!'; end")
eval(m)

如果在运行时,输入abc,那么eval求值的字符串是:
def abc
  puts 'Hi!'
end

eval的危险性

eval很强大,但是它也潜在着危险,这个有点像sql注入

假如,上面我们输入的不是hi而是下面的内容

hi; end; system("rm -rf /*"); #

eval求值以后是这样的

def hi; end; system("rm -rf /*"); # puts  'Hello'; end

求值的结果是:#后面的所有内容会被作为注释忽略掉。使用system命令试图删除系统所有文件

Ruby有一个全局变量$SAFE(取值范围是0到4)、以获取对如非法写文件这一类危险的防护。

instance_eval
该方法是将self变为instance_eval调用的接收者,对字符串或代码块进行求值。

p self
a = []
a.instance_eval {p self}
输出结果:
main
[]

instance_eval常常于用访问对象的私有数据(特别是实例变量)。

class C
def initialize
    @x = 1
end
end

c = C.new
c.instance_eval { puts @x }


class_eval
从本质上f讲,class_eval(即model_eval)可进入类定义体中:

c = Class.new
c.class_eval do
def some_method
    puts "Created in class_eval"
end
end
c = C.new
c.some_method

class_eval还可以做一些class关键字不能做的事:
*在类定义的上下文中对字符串求值
*为匿名类(不包单例类)打开类定义
*获取外围作用域中变量的访问权

用class关键字打开一个类时,就打开了一个新的局部变量作用域。但是,使用class_eval的代码块,可以看到外围使用域中的变量。
>> var = "initialize variable"
=> "initialized variable"
>> class C
>> puts var
>> end
NameError: undefined local variable or method 'var' for C:Class
    from (irb):3
>>C.class_eval{puts var}
initialized variable

var在标准的类定义体的作用域之外,但是在传递给class_eval的代码块的作用域之内。

当在class_eval的块中定义一个实例方法时,又有所不同:
>> C.class_eval{def talk; puts var; end}
=> nil
>>C.new.talk
NameError: undefined local variable or method 'var' for #<C:0x355ba2>
和任何def一样,在代码块中的def打开了一个新的作用域,所以变量var不能访问了。

如果想要把外部作用域的变量硬塞到实例方法中,可以使用define_method方法。

>>C.class_eval {define_method('talk'){puts var}}
>>C.new.talk
initialized variable

define_method是Module类的实例方法,所以Module或Class的任意实例都可以调用该方法。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics