Ruby嵌套的const_missing method_missing堆栈太深了

时间:2013-09-09 22:03:21

标签: ruby 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

我做错了什么?

3 个答案:

答案 0 :(得分:3)

使用关键字def后,会创建一个新范围,因此这里的问题是const_name变量不再位于method_missing方法体内的范围内。 / p>

您可以使用以下块来保持变量的范围:

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

答案 1 :(得分:0)

问题是const_name递归调用method_missing。将块传递给Class.new时,将在类的范围内计算块。 (见docs

答案 2 :(得分:0)

方法没有看到任何局部外部变量。在你的情况下,它是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!