简体   繁体   English

红宝石猴即时修补

[英]ruby monkey patching on the fly

Is there a way to implement monkey patching while an object is being instantiated?有没有办法在实例化对象时实现猴子补丁?

When I call:当我打电话时:

a = Foo.new

Prior to the instance being instantiated, I would like to extend the Foo class based on information which I will read from a data store.在实例化实例之前,我想根据我将从数据存储中读取的信息扩展 Foo 类。 As such, each time I call Foo.new , the extension(s) that will be added to that instance of the class would change dynamically.因此,每次我调用Foo.new ,将添加到该类实例的扩展Foo.new动态更改。

tl;dr: Adding methods to an instance is possible. tl;dr:可以向实例添加方法。

Answer: Adding methods to an instance is not possible.答:无法向实例添加方法。 Instances in Ruby don't have methods. Ruby 中的实例没有方法。 But each instance can have a singleton class, where one can add methods, which will then be only available on the single instance that this singleton class is made for.但是每个实例都可以有一个单例类,可以在其中添加方法,然后这些方法只能在为该单例类创建的单个实例上可用。

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

are just some of the ways to define a singleton method for an object.只是为对象定义单例方法的一些方法。

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

This takes another approach, it will insert the module between the singleton class and the real class in the inheritance chain.这采用了另一种方法,它将在继承链中的单例类和真实类之间插入模块。 This way, too, the module is available to the instance, but not on the whole class.这样,模块也可用于实例,但不能用于整个类。

Sure you can!你当然可以!

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!

Prior to the instance being instantiated, I would like to extend在实例化实例之前,我想扩展

Given a class Foo which does something within its initialize method:给定一个类Foo ,它在其initialize方法中执行某些操作:

class Foo
  attr_accessor :name

  def initialize(name)
    self.name = name
  end
end

And a module FooExtension which wants to alter that behavior:还有一个模块FooExtension想要改变这种行为:

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

You could patch it via prepend :您可以通过prepend修补它:

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

Foo.prepend(FooPatcher)

Or you could extend even before calling initialize by providing your own new class method:或者您甚至可以在调用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

Both variants produce the same result:两种变体产生相同的结果:

$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