这是我在编程多年中看到的最奇怪的错误。请忽略我在下面使用的奇怪习语,因为我正在使用开拓者,一个Rails的编程框架:
以下代码
let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }
it 'gets app details from Steam' do
VCR.use_cassette('app/get') do
p app.id
app_id = app.id
app = App::Get.(id: app_id).model
end
expect(app.app_type).to eq('game')
end
按预期工作。
以下代码
let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }
it 'gets app details from Steam' do
VCR.use_cassette('app/get') do
p app.id
app = App::Get.(id: app.id).model
end
expect(app.app_type).to eq('game')
end
抛出一个未定义的方法`id'对于nil:行中的NilClass
app = App::Get.(id: app.id).model
但行
p app.id
就在违规行显示正确的ID之前。
可能会发生什么?
答案 0 :(得分:1)
这一行:
let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model }
创建一个名为app
的方法,在调用时返回App
。 (它会记住结果,因此在初始调用后它将始终返回相同的App
。)
这一行:
app = App::Get.(id: app.id).model
创建名为app
的新本地变量,该方法与方法app
不明确。根据{{3}}:
在Ruby中,局部变量名和方法名几乎相同。如果 你还没有分配给ruby所假设的其中一个模棱两可的名字 你想打电话给一个方法。一旦你分配了名称ruby 将假设您希望引用局部变量。
解析器遇到局部变量时会创建局部变量 分配,而不是在分配发生时:
因此,当您尝试分配给app
时,会创建一个新的局部变量。评估分配的右侧时,app.id
会尝试在局部变量id
上调用app
(到目前为止为nil
)。
这是我能想到的最简单的代码,可以重现这个问题:
def a
"Hello"
end
p a.upcase # "HELLO"
a = a.upcase # undefined method `upcase' for nil:NilClass (NoMethodError)
有几种方法可以修复您的代码。我认为到目前为止最好的解决方案是:
app2 = App::Get.(id: app.id).model
方法已使用名称app
...不要通过创建具有相同名称的本地变量来隐藏它。
你也可以这样做:
app = App::Get.(id: app().id).model # the parens disambiguate
或您已有的解决方案:
app_id = app.id # capture the ID before the local variable gets created
app = App::Get.(id: app_id).model
但我认为这两者都比首先不创建命名冲突更糟糕。