0

我有以下课程:

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

我究竟做错了什么?

4

3 回答 3

3

使用关键字后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
于 2013-09-09T22:55:08.073 回答
0

问题是const_name递归调用method_missing. 当您将一个块传递给该块时,Class.new该块在类的范围内进行评估。(请参阅文档

于 2013-09-09T22:41:21.607 回答
0

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

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

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

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!
于 2013-09-09T23:34:57.753 回答