【问题标题】:Dynamically extend existing method or override send method in ruby动态扩展现有方法或覆盖 ruby​​ 中的发送方法
【发布时间】:2012-09-20 10:27:42
【问题描述】:

假设我们有 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() 方法中的每一个。

如何做到这一点?

此外,我一直在考虑覆盖 class A 中的 send 类,这样我就可以运行 foo_prepare 而不仅仅是让 send(超级)来完成其余的方法。

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

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

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

    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
    

    【讨论】:

    • 非常感谢非常干净和 oop 的解决方案。我为我选择了更简单的修复方法,但我会毫不犹豫地记住这一点,以备将来使用。
    【解决方案2】:

    我推荐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
    

    【讨论】:

      【解决方案3】:

      从 Ruby 2.0 开始,您可以使用“前置”来简化 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 的情况下覆盖类的方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多