Ruby混合和继承注入

4
我需要在父类的每个子类中注入回调函数。因此,带有回调函数的方法必须首先被调用,然后再调用所有后续链:

可以通过alias_method(或alias_method_chain)实现:

module ChildMod1
  def save
    puts "save ChildMod1"
    super
  end
end

module ChildMod2
  def save
    puts "save ChildMod2"
    super
  end
end

class Parent
  def save
    puts "save Parent"
  end
end
class Child < Parent
  include ChildMod1
  include ChildMod2

  def save
    puts "save Child"
    super
  end

  alias_method :old_save, :save
  module_eval <<-R
      def save
        puts "save Callback"
        old_save
      end
    R
end
c = Child.new
c.save

输出

save Callback
save Child
save ChildMod2
save ChildMod1
save Parent

但是通过继承实现这一点是否可能?例如在ChildMod1或ChildMod2中。我希望能在一个模块空间内执行代码,以获得继承的所有优势。
module ChildMod1
  def save
    puts "save ChildMod1"
    super
  end
end

module ChildMod2
  def save
    puts "save ChildMod2"
    super
  end
end

class Parent
  def save
    puts "save Parent"
  end
end
class Child < Parent
  include ChildMod1
  include ChildMod2

  def save
    puts "save Child"
    super
  end

  module_eval <<-R
      def save
        puts "save Callback"
        super
      end
    R

end

c = Child.new
c.save

输出

save Callback
save ChildMod2
save ChildMod1
save Parent

正如您所看到的,它只覆盖了Child。

更新 wdebeaum的解决方案很好,但是如果我需要通过module_eval或类似方法动态地创建许多方法,并在类内部重新定义它们呢?我不能为它们创建单独的模块。

class TestEval
  def redefine_me
    puts "Test method"
    super # I expect that it will call Eval method, but module_eval just overwrite it
  end

  module_eval <<-R
      def redefine_me
        puts "Eval method"
      end
    R
end

更新2:使用单例模式,我得到了错误的链式评估顺序Eval => Test,而不是正确的Test => Eval。

class TestEval
  def initialize
    class << self
      def redefine_me
        puts "Eval method"
        super
      end
    end
  end
  def redefine_me
    puts "Test method"
  end
end

TestEval.new.redefine_me

假设我有一个类方法"field",它会向Datastream添加一些实例方法(例如添加setter和getter方法),我想重新定义其中的一个方法,就像这样:
class Datastream 
  field :name 
  def name=(value) 
    puts "redefined!"
    super
  end 
end

顺便提一下,我在这个问题之后创建了一个 gem https://github.com/AlexParamonov/inheritance_module_eval,只是忘记在这里添加了。 - Alexander Paramonov
1个回答

2
您可以将回调方法放在自己的模块中,并重写父类的初始化方法来扩展该模块(如果需要,使用alias_method)。这将通过将其链接到每个子类实例的单例类来将回调方法放在子类方法之前。只需从第二个代码示例中删除module_eval部分,并在c = Child.new之前添加此内容即可。
module Callback
  def save
    puts "save Callback"
    super
  end
end

class Parent
  alias_method :old_initialize, :initialize
  def initialize
    old_initialize
    extend Callback
  end
end

输出:

save Callback
save Child
save ChildMod2
save ChildMod1
save Parent

不错,我已经给你点赞了。你的解决方案很好,但是如果我需要通过module_eval或类似方法动态创建大量方法,并在类内重新定义它们怎么办?我不能为它们创建单独的模块。我会尽快更新帖子并提供新的示例。 - Alexander Paramonov
为什么你不能创建一个单独的模块呢?如果你只是想动态地创建方法,你可以使用Module.new来动态地创建模块,然后像你一直做的那样,在该模块上使用module_eval。如果你真的无法创建新的模块,那么你可以尝试将方法添加到每个实例的单例类中,而不是扩展一个模块。但对我来说,这似乎是为了很少的原因而额外的工作。 - wdebeaum
关于我的回调问题,您提供了正确的解决方案。更新2部分的问题非常相似,您能否为此提出建议? - Alexander Paramonov
等等,我以为你想要动态添加的方法先被调用?为什么你说在那个例子中“Eval => Test”是错误的? - wdebeaum
是的,其实我正在寻找一种方法,可以像在模块中创建方法那样。因为我有两个问题:如何定义回调(你给出了很好的答案),以及如何使用module_eval创建继承结构,例如“include Module”支持的内容,如在UPDATE2中所示。 - Alexander Paramonov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接