元编程 #
元编程类似 Java 的反射。
修改方法 #
Ruby 元编程支持动态增加方法,重定义方法和删除方法。
class EmptyClass
end
# 添加方法
class EmptyClass
def everything_changes
'Wait, what? You just added a method to me!'
end
end
puts EmptyClass.new.everything_changes
# 重定义方法
class EmptyClass
def everything_changes
'Redefine everything changes!'
end
end
puts EmptyClass.new.everything_changes
动态方法调用 #
动态方法调用主要依靠 send()
方法,该方法第一个参数为需要调用的方法名。
class Glider
def lift
'do lift'
end
def turn(dir)
"do turn #{dir}"
end
end
class Nomad
def initialize(glider)
@glider = glider
end
def do(action, args = nil)
if args==nil
@glider.send(action)
else
@glider.send(action, args)
end
end
end
nomad = Nomad.new(Glider.new)
puts nomad.do('lift')
puts nomad.do('turn', 'left')
方法缺失 #
动态方法调用时很可能调用不存在的方法,这是就会发生 method missing
的异常。
此时可以通过定义 method_missing()
方法来处理所有不存在的方法的调用。
class Spy
def method_missing(method_name, *args, &block)
puts "#{method_name} was called with #{args} and #{block}"
end
end
spy = Spy.new
spy.hello('hello', 'world') { |n| puts n } # => hello was called with ["hello", "world"] and #<Proc:
send 与代理模式 #
send()
结合方法缺失可以实现基本的代理模式。
class Wrapper
def initialize(agent)
@agent = agent
end
def method_missing(method_name, *args, &block)
@agent.send(method_name, *args)
end
end
wrapper= Wrapper.new([10, 20, 30])
puts wrapper.max # => 30
另一种定义方法的方式 #
class Doctor
define_method('perform_rhinoplasty') do |argument|
"performing rhinoplasty on #{argument}"
end
define_method('perform_checkup') do |argument|
"performing checkup on #{argument}"
end
define_method('perform_interpretive_dance') do |argument|
"performing interpretive dance on #{argument}"
end
end
doctor = Doctor.new
puts doctor.perform_rhinoplasty("nose") # => performing rhinoplasty on nose
puts doctor.perform_checkup("throat") # => performing checkup on throat
puts doctor.perform_interpretive_dance("in da club") # => performing interpretive dance on in da club
这种方式最大的特点就是可以通过 each
语句同时定义多个方法。以上示例可以写成以下形式:
class SuperDoctor
["rhinoplasty", "checkup", "interpretive_dance"].each do |action|
define_method("perform_#{action}") do |argument|
"performing #{action.gsub('_', ' ')} on #{argument}"
end
end
end
Inspect Method #
class Mock
def add(x, y)
x+y
end
private
def info
'info'
end
end
mock = Mock.new
method_add = mock.method(:add)
p method_add
p method_add.parameters
# the object on which the method is bound
p method_add.receiver
# the class that object belongs to
p method_add.owner
p mock.methods
p mock.public_methods
method(sym)
用于获得对象拥有的方法
parameters()
用于获得方法对象的参数
receiver()
用于获得该方法绑定的对象
owner()
用于获得定义该方法的类
常量引用 #
const_get()
可以用于获得已定义的常量对象。不属于任何类的常量存在于 Module
对象中。
class Mock
Foo = 'foo'
end
CONSTANT_A = 100
puts CONSTANT_A
puts Module.const_get('CONSTANT_A')
puts Mock.const_get('Foo')
由于类名通常也是常量,所以也可以通过这种方式创建对象
puts Module.const_get('Mock').new.add(2, 3)
成员变量引用 #
类似常量,成员变量使用 instance_variable_get()
进行引用。
p mock.instance_variable_get('@sum')
LifeCycle Hook #
元编程中可能需要监控方法的声明周期,这时需要使用 hook 方法。
method_added #
method_added
用于当类中增加了新的实例方法时被调用。
class Dojo
@@method_added = []
def self.method_added(method_name)
puts "a new method '#{method_name}' has added"
@@method_added << method_name
end
def self.all_added_methods
"class contains #{@@method_added}"
end
def foo
end
def bar
end
end
puts Dojo.all_added_methods
以上结果输出
a new method 'foo' has added
a new method 'bar' has added
class contains [:foo, :bar]
singleton_method_added #
singleton_method_added
用于监控单例方法的添加。
d1 = Dojo.new
def d1.singleton_method_added(method_name)
puts "a new singleton method '#{method_name}' has added"
@singleton_methods_added ||= []
@singleton_methods_added << method_name
end
def d1.all_added_singleton_methods
"instance contains #{@singleton_methods_added}"
end
def d1.inc(x)
x+ 1
end
puts d1.inc(3)
puts d1.all_added_singleton_methods
以上输出
a new singleton method 'singleton_method_added' has added
a new singleton method 'all_added_singleton_methods' has added
a new singleton method 'inc' has added
4
instance contains [:singleton_method_added, :all_added_singleton_methods, :inc]
其它方法 #
与之类似还有定义方法的删除的 methods_removed
和方法未定义的 methods_undefined
以及这两个的单例版本。
模块或类的引入 #
included()
用于监控一个模块或类的引用情况。
module SparringArea
@@included_into = []
def self.included_into
@@included_into
end
def self.included(class_or_module)
puts "class_or_module '#{class_or_module}' is included"
@@included_into << class_or_module
end
end
include SparringArea
puts SparringArea.included_into
对象的扩展 #
extended()
用于监控对象从某一个模块中进行扩展。
class Dojo
end
module SparringArea
@@extended_objects = []
def self.extended_objects
@@extended_objects
end
def self.extended(object)
puts "object '#{object}' is extended"
@@extended_objects << object
end
end
dojo1 = Dojo.new
dojo2 = Dojo.new
dojo1.extend(SparringArea)
dojo2.extend(SparringArea)
继承 #
inherited()
用于监控一个类是否被继承。
class Room
@@subclasses = []
def self.subclasses
@@subclasses
end
def self.inherited(subclass)
puts "subclass '#{subclass}' inherits Room"
@@subclasses << subclass
end
end
class SmallRoom < Room
end