如何为特定方法启用猴子补丁?

时间:2015-10-07 00:22:41

标签: ruby-on-rails ruby

我正在使用gem,出于某种原因,在我的某些代码使用之前,需要对其中一个方法进行修补。

问题出在这里,我怎么能只为我的一些代码启用这个补丁呢。对于类中的某些方法,我需要启用此补丁;一些我想禁用这个补丁。

怎么做?

class FromGem
    def BlahBlah
       #patch here
    end
end

class A 
   def Test1
         #need patch
   end 

   def Test2
         # don't need patch
   end
end

3 个答案:

答案 0 :(得分:6)

这就是改进的目的。

说,我们有以下第三方代码:

class FromGem
  def say_hello
    'Hello'
  end
end

FromGem.new.say_hello
# => 'Hello'

我们想把它扩展为说“Hello World”,我们会做这样的事情:

module ExtendFromGem
  def say_hello
    super + ' World'
  end
end

class FromGem
  prepend ExtendFromGem
end

FromGem.new.say_hello
# => 'Hello World'

这只是扩展行为的标准方式,当然,这仍然是全球性的。如果我们想限制我们的猴子补丁的范围,我们将需要使用优化:

module ExtendedFromGem
  module ExtendFromGem
    def say_hello
      super + ' World'
    end
  end

  refine FromGem do
    prepend ExtendFromGem
  end
end

FromGem.new.say_hello
# => 'Hello'
# We haven't activated our Refinement yet!

using ExtendedFromGem

FromGem.new.say_hello
# => 'Hello World'
# There it is!

现在,我们想要写的是:

class A 
  def test1
    using ExtendedFromGem
    FromGem.new.say_hello
  end

  def test2
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

不幸的是,这不起作用:优化仅适用于脚本范围,在这种情况下,优化仅在调用using后才能激​​活,或者它们在模块范围内工作,在在整个模块正文中它们是活动的,甚至在调用using之前,所以我们可以做的是,这(IMO,这更清洁):

class A 
  using ExtendedFromGem

  def test1
    FromGem.new.say_hello
  end
end

class A 
  def test2
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

或者这个:

class A 
  def test2
    FromGem.new.say_hello
  end
end

using ExtendedFromGem

class A 
  def test1
    FromGem.new.say_hello
  end
end

A.new.test1
# => 'Hello World'

A.new.test2
# => 'Hello'

Etvoilà:test1看到了细化,test2没有。

答案 1 :(得分:1)

如果您想在一个地方更改行为,那么您不需要做任何事情,只需使用或不使用gem方法编写几行符合您要求的代码

如果您需要在多个位置修改行为,则创建一个实现已更改行为的方法。在这种情况下;你将使用适配器设计模式;
步骤进行:

  • 您创建了使用Original类行为的AdaptorClass,为您提供所需的新行为。
  • 现在,您使用Adapter的行为而不是原始类来完成您的工作。
  

尽量不要修改原始类;遵循开放式原则

class A
  def Test1
    # need patch
    # you use GemAdapter rather than FromGem
  end

  def Test2
    # don't need patch
    # use FromGem class
  end

  def Test3
    # need patch
    # you use GemAdapter rather than FromGem
  end
end

class GemAdapter
  def new_behavior
    gem_instance = FromGem.new
    # implement your new behavior you desire
  end
end

enter image description here

答案 2 :(得分:1)

这也可以是一个解决方案

  

class <<用于修改特定实例;元编程的一部分

class A
  def print
    p 'Original method'
  end
end

class B
  def initialize
    @new_instance_of_a = A.new
  end
  def my_method
    modifiable_a = A.new

    # modifying the instance of class A i.e. modifiable_a 
    class << modifiable_a 
      def print
        p 'Monkey patch'
      end
    end

    modifiable_a .print
  end

  def normal_method
    @new_instance_of_a.print
  end
end

然而,修改local_instance并没有多大意义。如果这个修改过的实例可以在更多地方使用,那么使用这种方法是值得的