动态扩展现有方法或覆盖ruby中的send方法

时间:2012-09-20 10:27:43

标签: ruby metaprogramming

假设我们有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覆盖class A类,以便运行foo_prepare,而不是让send(超级)来完成其余方法

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

3 个答案:

答案 0 :(得分:7)

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

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

答案 1 :(得分:4)

我推荐Sergio's解决方案(已接受)。这是我做的,符合我的需要。

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

答案 2 :(得分:2)

从Ruby 2.0开始,你可以使用&#39; prepend&#39;简化塞尔吉奥的解决方案:

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。