一说编写元程序的语言称之为 元语言。被操纵的程序的语言称之为 目标语言。一门编程语言同时也是自身的元语言的能力称之为 反射或者 自反。——摘自维基百科 元编程条目
1
2
3
4
5
6
7
8
9
10
11
12
|
class
Rubyist
def
what_does_he_do
@person
=
'A Rubyist'
'Ruby programming'
end
end
an_object = Rubyist.
new
puts an_object.
class
# => Rubyist
puts an_object.
class
.instance_methods(
false
)
# => what_does_he_do
an_object.what_does_he_do
puts an_object.instance_variables
# => @person
|
1
2
3
4
5
6
|
obj =
Object
.
new
if
obj.respond_to?(
:program
)
obj.program
else
puts
"Sorry, the object doesn't understand the 'program' message."
end
|
1
2
3
4
5
6
7
8
|
class
Rubyist
def
welcome(*args)
"Welcome "
+ args.join(
' '
)
end
end
obj = Rubyist.
new
puts(obj.send(
:welcome
,
"famous"
,
"Rubyists"
))
# => Welcome famous Rubyists
|
1
2
3
4
5
6
7
8
9
|
class
Rubyist
end
rubyist = Rubyist.
new
if
rubyist.respond_to?(
:also_railist
)
puts rubyist.send(
:also_railist
)
else
puts
"No such information available"
end
|
1
2
3
4
5
6
7
8
9
|
class
Rubyist
private
def
say_hello(name)
"#{name} rocks!!"
end
end
obj = Rubyist.
new
puts obj.send(
:say_hello
,
'Matz'
)
|
1
2
3
4
5
6
7
8
|
class
Rubyist
define_method
:hello
do
|my_arg|
my_arg
end
end
obj = Rubyist.
new
puts(obj.hello(
'Matz'
))
# => Matz
|
1
2
3
4
5
6
7
8
|
class
Rubyist
def
method_missing(m, *args, &block)
puts
"Called #{m} with #{args.inspect} and #{block}"
end
end
Rubyist.
new
.anything
# => Called anything with [] and
Rubyist.
new
.anything(
3
,
4
) { something }
# => Called anything with [3, 4] and #<Proc:[email protected]:7>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
car = Car.
new
car.go_to_taipei
# go to taipei
car.go_to_shanghai
# go to shanghai
car.go_to_japan
# go to japan
class
Car
def
go(place)
puts
"go to #{place}"
end
def
method_missing(name, *args)
if
name.to_s =~ /^go_to_(.*)/
go(
$1
)
else
super
end
end
end
|
注意method_missing方法的效率不甚理想,对效率敏感的项目尽量要避免使用此方法。尽管 method_missing的确很强力。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class
Rubyist
def
method_missing(m, *args, &block)
puts
"Method Missing: Called #{m} with #{args.inspect} and #{block}"
end
def
hello
puts
"Hello from class Rubyist"
end
end
class
IndianRubyist < Rubyist
def
hello
puts
"Hello from class IndianRubyist"
end
end
obj = IndianRubyist.
new
obj.hello
# => Hello from class IndianRubyist
class
IndianRubyist
remove_method
:hello
# removed from IndianRubyist, but still in Rubyist
end
obj.hello
# => Hello from class Rubyist
class
IndianRubyist
undef_method
:hello
# prevent any calls to 'hello'
end
obj.hello
# => Method Missing: Called hello with [] and
|
1
2
|
str =
"Hello"
puts eval(
"str + ' Rubyist'"
)
# => "Hello Rubyist"
|
一般来说,能避免 eval 就尽量避免,因为 eval 有额外的“分析时”开销(将字符串作为源代码进行词法、文法分析),而这个“剖析时”却又是在程序“运行时”进行的。把不需要惰性求值的表达式预先进行及早求值,能避免一些分析时开销。如果可能的话,用 instance_exec,或 instance_eval 带块的形式,也比直接在字符串上求值好。——苏小脉在 如果用这种方式来构造一些复杂的对象呢?上的发言
Walter Webcoder有一个非常棒的想法:设计一个 Web算数页面。该页面是含有一个文本域以及按钮的简单 Web表单,并被各种各样的非常酷的数学链接和横幅广告包围,使得看起来丰富多彩。用户输入一个算术表达式到文本域中,并按下按钮,然后结果就会被显示出来。一夜之间,世界上所有计算器都变得无用了; Walter大大获利,然后他退休并把他的余生用于收集车牌号。Walter认为实现这样一个计算器很容易。他可以用 Ruby的 CGI库访问表单域中的内容,再用 eval方法把字符串当做表达式来求值。
1234567891011121314require
'cgi'
cgi =
CGI
:
:new
(
"html4"
)
# Fetch the value of the form field "expression"
expr = cgi[
"expression"
].to_s
begin
result = eval(expr)
rescue
Exception
=> detail
# handle bad expressions
end
# display result back to user...
Walter把这个应用程序放到网上才几秒钟,来自 Waxahachie的一个 12岁小孩在表单中输入了 system("rm"),随他的计算机上的文件一起, Walter的美梦一下子破灭了。Walter得到了一个重要的教训: 所有的外部数据都是有危险的。不要让它们靠近那些可能改动你的系统的接口。在这个案例中,表单中的内容是外部数据,而对eval的调用正是一个安全漏洞。—— Programming Ruby 中文第二版, Dave Thomas, Chad Fowler, Andy Hunt著
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Rubyist
def
initialize
@geek
=
"Matz"
end
end
obj = Rubyist.
new
# instance_eval可以操纵obj的私有方法以及实例变量
obj.instance_eval
do
puts
self
# => #<Rubyist:0x2ef83d0>
puts
@geek
# => Matz
end
|
1
2
3
4
5
6
7
8
9
10
|
class
Rubyist
end
Rubyist.instance_eval
do
def
who
"Geek"
end
end
puts Rubyist.who
# => Geek
|
1
2
3
4
|
class
Rubyist
@@geek
=
"Ruby's Matz"
end
puts Rubyist.class_eval(
"@@geek"
)
# => Ruby's Matz
|
1
2
3
4
5
6
7
8
9
|
class
Rubyist
end
Rubyist.class_eval
do
def
who
"Geek"
end
end
obj = Rubyist.
new
puts obj.who
# => Geek
|
备注当作用于类时, class_eval将会定义实例方法,而 instance_eval定义类方法。
1
2
3
4
5
6
|
class
Rubyist
@@geek
=
"Ruby's Matz"
end
Rubyist.class_variable_set(:
@@geek
,
'Matz rocks!'
)
puts Rubyist.class_variable_get(:
@@geek
)
# => Matz rocks!
|
1
2
3
4
5
6
7
8
9
10
11
|
class
Rubyist
@@geek
=
"Ruby's Matz"
@@country
=
"USA"
end
class
Child < Rubyist
@@city
=
"Nashville"
end
print Rubyist.class_variables
# => [:@@geek, :@@country]
puts
p Child.class_variables
# => [:@@city]
|
1
2
3
4
5
6
7
8
|
class
Rubyist
def
initialize(p1, p2)
@geek
,
@country
= p1, p2
end
end
obj = Rubyist.
new
(
'Matz'
,
'USA'
)
puts obj.instance_variable_get(:
@geek
)
# => Matz
puts obj.instance_variable_get(:
@country
)
# => USA
|
1
2
3
4
5
6
7
8
9
10
11
|
class
Rubyist
def
initialize(p1, p2)
@geek
,
@country
= p1, p2
end
end
obj = Rubyist.
new
(
'Matz'
,
'USA'
)
puts obj.instance_variable_get(:
@geek
)
# => Matz
puts obj.instance_variable_get(:
@country
)
# => USA
obj.instance_variable_set(:
@country
,
'Japan'
)
puts obj.inspect
# => #<Rubyist:0x2ef8038 @country="Japan", @geek="Matz">
|
1
|
puts Float.const_get(:
MIN
)
# => 2.2250738585072e-308
|
1
2
3
|
class
Rubyist
end
puts Rubyist.const_set(
"PI"
,
22
.
0
/
7
.
0
)
# => 3.14285714285714
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# Let us call our new class 'Rubyist'
# (we could have prompted the user for a class name)
class_name =
"rubyist"
.capitalize
Object
.const_set(class_name,
Class
.
new
)
# Let us create a method 'who'
# (we could have prompted the user for a method name)
class_name =
Object
.const_get(class_name)
puts class_name
# => Rubyist
class_name.class_eval
do
define_method
:who
do
|my_arg|
my_arg
end
end
obj = class_name.
new
puts obj.who(
'Matz'
)
# => Matz
|