6

假设我们有 A、B、C 类。

A
 def self.inherited(sub)
   # meta programming goes here
   # take class that has just inherited class A
   # and for foo classes inject prepare_foo() as 
   # first line of method then run rest of the code
 end

 def prepare_foo
   # => prepare_foo() needed here
   # some code
 end

end

B < A
  def foo
    # some code
  end
end

C < A
  def foo
    # => prepare_foo() needed here
    # some code
  end
end

如您所见,我正在尝试foo_prepare()对每个foo()方法注入调用。

怎么可能呢?

此外,我一直在考虑以我会运行的方式覆盖send类,而不是让(super) 来执行其余的方法。class Afoo_preparesend

你们怎么看,解决这个问题的最佳方法是什么?

4

3 回答 3

8

这是给你的解决方案。尽管它基于模块包含而不是从类继承,但我希望您仍然会发现它很有用。

module Parent
  def self.included(child)
    child.class_eval do
      def prepare_for_work
        puts "preparing to do some work"
      end
  
      # back up method's name
      alias_method :old_work, :work
  
      # replace the old method with a new version, which has 'prepare' injected
      def work
        prepare_for_work
        old_work
      end
    end
  end
end

class FirstChild
  def work
    puts "doing some work"
  end

  include Parent # include in the end of class, so that work method is already defined.
end

fc = FirstChild.new
fc.work
# >> preparing to do some work
# >> doing some work
于 2012-09-20T10:37:47.320 回答
4

我推荐Sergio 的解决方案(已接受)。这是我所做的符合我的需求。

class A
  def send(symbol,*args)
    # use array in case you want to extend method covrage
    prepare_foo() if [:foo].include? symbol
    __send__(symbol,*args)
  end
end

或者

class A
  alias_method :super_send, :send           

  def send(symbol,*args)
    prepare_foo() if [:foo].include? symbol
    super_send(symbol,*args)
  end
end
于 2012-09-20T10:57:09.480 回答
2

从 Ruby 2.0 开始,您可以使用 'prepend' 来简化 Sergio 的解决方案:

module Parent
  def work
    puts "preparing to do some work"
    super
  end
end

class FirstChild
  prepend Parent

  def work
    puts "doing some work"
  end
end

fc = FirstChild.new
fc.work

这允许模块在不需要 alias_method 的情况下覆盖类的方法。

于 2014-10-27T14:55:49.500 回答