代码之家  ›  专栏  ›  技术社区  ›  Xaree Lee

Ruby:在需要子类文件时如何捕获和解析未定义的方法

  •  0
  • Xaree Lee  · 技术社区  · 4 年前

    我需要解析一个DSL ruby文件。看起来像

    # file B.rb
    class B < A
      hello "John"
    end
    
    hello "Mary"
    

    它调用函数 hello() 类内外都有一个字符串参数 B .

    我试着 require 文件 B.rb 并解决缺失的方法:

    # file A.rb
    
    # define a class A for subclassing, before loading B.rb
    class A
    end
    
    # Try to catch the missing methods
    def method_missing(name, *args, &block)
      puts "tried to handle unknown method %s" % name # name is a symbol
    end
    
    require("./B.rb")
    

    运行此代码后 method_missing 导致无限循环。

    如何在类内外捕获和解析未定义的方法 B ? 我想知道DSL文件中调用了哪些方法和参数。

    0 回复  |  直到 4 年前
        1
  •  2
  •   max pleaner    4 年前

    我在评论中暗示了这种方法,但认为详细说明可能有用。

    首先,我们需要定义 method_missing 在某些事情上,我们无法在顶层定义它,因为我们会收到这个警告

    重新定义对象#method_missing可能会导致无限循环

    相反,我们将在 class A (在班级层面)。

    # in a.rb
    
    class A
      def self.method_missing(name, *args, &block)
        puts "tried to handle unknown method %s" % name # name is a symbol
      end
    end
    

    现在我们可以使用 class_eval 评估 b.rb 在范围内 A类 像这样:

    # also in a.rb
    
    A.class_eval(File.read("./b.rb"))
    

    现在“顶层” hello "Mary" 调用实际上是在类范围内执行的 A (其中 方法缺失 定义处于活动状态)。此外,由于 class B 继承自 A. ,它也有 方法缺失 在类级别上定义,因此 hello "John" 电话也会打过来。

    所以运行脚本,我看到了 tried to handle unknown method hello 正如预期的那样,打印了两次。

    我应该注意到,我认为以这种方式“忽略”错误不是一个好主意。。。最有可能的是停止程序的执行。但也许你心里有什么需要它的东西,我不能确定。

        2
  •  2
  •   rohit89    4 年前

    这条线在你的 method_missing ,

    puts "tried to handle unknown method %s" % name # name is a symbol
    

    结果呼叫 to_ary 它没有定义,最终会被调用 方法缺失 再一次。因此,你的无限循环。

    def method_missing(name, *args, &block)
      p name
      puts "tried to handle unknown method %s" % name # name is a symbol
    end
    
    # =>
    ...
    ...
    :to_ary
    :to_ary
    :to_ary
    /System/Library/.../kernel_require.rb:55: stack level too deep (SystemStackError)
    

    如果你想这样做,修复很简单。使用字符串插值。

    puts "tried to handle unknown method #{name.to_s}" 
    
    # =>
    Tried to handle unknown method hello
    Tried to handle unknown method hello