簡體   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