红宝石猴子在飞行中修补



有没有办法在实例化对象时实现猴子修补?

当我打电话时:

a = Foo.new

在实例化实例之前,我想根据将从数据存储中读取的信息扩展 Foo 类。因此,每次我调用Foo.new时,将添加到该类实例的扩展都会动态更改。

tl;dr:可以向实例添加方法。

答:无法向实例添加方法。Ruby 中的实例没有方法。但是每个实例都可以有一个单例类,可以在其中添加方法,然后这些方法将仅在创建此单例类的单个实例上可用。

class Foo
end
foo = Foo.new
def foo.bark
puts "Woof"
end
foo.bark
class << foo
def chew
puts "Crunch"
end
end
foo.chew
foo.define_singleton_method(:mark) do
puts "Widdle"
end
foo.mark

只是为对象定义单一实例方法的一些方法。

module Happy
def cheer
puts "Wag"
end
end
foo.extend(Happy)
foo.cheer

这采用了另一种方法,它将在继承链中的单例类和实数类之间插入模块。这样,模块也可用于实例,但不能用于整个类。

当然可以!

method_name_only_known_at_runtime = 'hello'
string_only_known_at_runtime = 'Hello World!'
test = Object.new
test.define_singleton_method(method_name_only_known_at_runtime) do
puts(string_only_known_at_runtime)
end
test.hello
#> Hello World!

在实例化之前,我想扩展

给定一个类Foo,它在其initialize方法中执行某些操作:

class Foo
attr_accessor :name
def initialize(name)
self.name = name
end
end

FooExtension一个模块想要改变这种行为:

module FooExtension
def name=(value)
@name = value.reverse.upcase
end
end

你可以通过以下方式修补它prepend

module FooPatcher
def initialize(*)
extend(FooExtension) if $do_extend # replace with actual logic
super
end
end
Foo.prepend(FooPatcher)

或者,您甚至可以在调用initialize之前通过提供自己的new类方法来extend

class Foo
def self.new(*args)
obj = allocate
obj.extend(FooExtension) if $do_extend # replace with actual logic
obj.send(:initialize, *args)
obj
end
end

两种变体产生相同的结果:

$do_extend = false
Foo.new('hello')
#=> #<Foo:0x00007ff66582b640 @name="hello">
$do_extend = true
Foo.new('hello')
#=> #<Foo:0x00007ff66582b280 @name="OLLEH">

最新更新