首页 > 解决方案 > 堆栈级别太深,method_added ruby

问题描述

我创建了一个模块来在类中的方法调用之前挂钩方法:

module Hooks

def self.included(base)
 base.send :extend, ClassMethods
end


module ClassMethods
  # everytime we add a method to the class we check if we must redifine it
  def method_added(method)
    if @hooker_before.present? && @methods_to_hook_before.include?(method)
      hooked_method = instance_method(@hooker_before)
      @methods_to_hook_before.each do |method_name|
        begin
          method_to_hook = instance_method(method_name)
        rescue NameError => e
          return
        end
        define_method(method_name) do |*args, &block|
          hooked_method.bind(self).call
          method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
        end
      end
     end
   end

  def before(*methods_to_hooks, hookers)
   @methods_to_hook_before = methods_to_hooks
   @hooker_before = hookers[:call]
  end
 end
end

我已将该模块包含在我的一堂课中:

require_relative 'hooks'

class Block
  include Indentation
  include Hooks
  attr_accessor :file, :indent
  before :generate, call: :indent
  # after  :generate, call: :write_end

  def initialize(file, indent=nil)
    self.file = file
    self.indent = indent
  end

  def generate
    yield
  end
end

这个 Block 类是另一个类的父类,该类正在实现它自己的 generate 方法版本并且实际实现了。

当我的代码运行时,method_added 实际上是在某种无限循环中使用 method :generate 作为参数调用的。我不明白为什么 method_added 会陷入这个无限循环。你知道这段代码有什么问题吗?这是完整代码的 链接:github上的代码链接

标签: rubymodulehook

解决方案


你已经引起了无限递归,因为你在define_method里面打电话method_added。堆栈跟踪(很遗憾您没有提供)应该显示这一点。

解决此问题的一个稍微难看的解决方法可能是显式设置一个变量(例如@_adding_a_method)并将其用作 的保护子句method_added

module ClassMethods
  def method_added(method)
    return if @_adding_a_method

    if @hooker_before.present? && @methods_to_hook_before.include?(method)
      # ...

      @_adding_a_method = true
      define_method(method_name) do |*args, &block|
        # ...
      end
      @_adding_a_method = false

      # ...
    end
  end
end

但是,退后一步,我不确定这个模块想要实现什么。难道你不能用Module#prepend这个元编程来实现这个吗?

这段代码让我想起了旧的 Ruby 1.8/1.9 教程中关于高级元编程技术的内容;Module#prepend在大多数情况下,使此类变通办法变得多余。


推荐阅读