代码之家  ›  专栏  ›  技术社区  ›  David Geismar

使用添加了ruby的方法\的堆栈级别太深

  •  1
  • David Geismar  · 技术社区  · 6 年前

    我创建了一个模块,用于在类中调用方法之前钩住方法:

    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
    

    此块类是另一个类的父类,该类正在实现其自己版本的generate方法,并且实际已实现。

    当我的代码运行时,方法_added实际上是在某种无限循环中使用方法:generate as argument调用的。我不明白为什么添加的方法被捕获在这个无限循环中。你知道这个代码有什么问题吗? 以下是完整代码的链接: link to code on github

    1 回复  |  直到 6 年前
        1
  •  6
  •   Tom Lord    6 年前

    因为您正在调用,所以导致了无限递归 define_method 在…内 method_added . 堆栈跟踪(很遗憾,您没有提供)应该显示这一点。

    解决这个问题的一个稍微难看的解决方法是显式地设置一个变量(例如。 @_adding_a_method 添加方法(u) :

    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教程中的内容; 模块#前置 在大多数情况下,这样的解决办法是多余的。