繁体   English   中英

红宝石猴即时修补

[英]ruby monkey patching on the fly

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

当我打电话时:

a = Foo.new

在实例化实例之前,我想根据我将从数据存储中读取的信息扩展 Foo 类。 因此,每次我调用Foo.new ,将添加到该类实例的扩展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">

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM