Ruby 嵌套const_missing method_missing堆叠太深



我有以下类:

module APIWrapper
  include HTTParty
  BASE_URI = 'https://example.com/Api'
  def self.const_missing(const_name)
    anon_class = Class.new do
      def self.method_missing method_name, *params
        params = {
          'Target' => const_name.to_s,
          'Method' => method_name.to_s,
        }
        APIWrapper.call_get params
      end
    end
  end
  def self.call_get(params)
    get(APIWrapper::BASE_URI, {:query => params})
  end
  def self.call_post(params)
    post(APIWrapper::BASE_URI, params)
  end
end

我希望能够像这样调用我的包装器:

APIWrapper::User::getAll

我收到堆栈级别太深的错误:

1) Error:
test_User_getAll(APITest):
SystemStackError: stack level too deep
api_test.rb:16

我做错了什么?

使用关键字 def 后,将创建一个新作用域,因此这里的问题是const_name变量不再在 method_missing 方法主体的作用域中。

您可以使用如下所示的块将变量保留在范围内:

def self.const_missing(const_name)                                                                                                                             
  anon_class = Class.new do                                                                                                                                    
    define_singleton_method(:method_missing) do |method_name, *params|                                                                                                 
      params = {                                                                                                                                             
        'Target' => const_name.to_s,                                                                                                                         
        'Method' => method_name.to_s,                                                                                                                        
      }                                                                                                                                                      
      APIWrapper.call_get params                                                                                                                                                                                                                                                                                   
    end                                                                                                                                                        
  end                                                                                                                                                          
end                                                                                                                                                            

您可能还希望将常量设置为刚刚创建的匿名类:

anon_class = Class.new do
  ...
end
const_set const_name, anon_class

问题是const_name递归调用method_missing 。将块传递给Class.new块时,将在类的作用域中计算该块。 (请参阅文档)

方法看不到任何局部外部变量。在您的情况下,它是const_name,它以递归方式触发method_missing。

name = "Semyon"
def greet
  puts "hello, #{name}!"
end
greet # undefined local variable or method ‘name’

您可以使用 const_set 在 Ruby 中命名匿名模块(和类),从那里您可以轻松看到名称。我也不建议为每个类定义新方法,这就是模块的用途。这是我简短的自包含示例:

module Greeting
  module Base
    def method_missing(word)
      Greeting.greet word, self.name.split("::").last
    end
  end
  def self.greet(word, name)
    puts "#{word}, #{name}!"
  end
  def self.const_missing(name)
    const_set name, Module.new.extend(Base)
  end
end
Greeting::Semyon.hello # hello, Semyon!

最新更新