我有以下类:
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!