2016-07-31 17 views
0

Dies ist der seltsamste Fehler, den ich in meinen Jahren der Programmierung gesehen habe. Bitte ignorieren Sie die fremden Idiome ich unten bin mit, wie ich bin mit Vorreiter, ein Planungsrahmen für Rails:Variable ist sowohl Null als auch nicht Null innerhalb von rspec Test

Der folgende Code

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 

funktioniert wie erwartet.

Der folgende Code

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 

löst eine nicht definierte Methode `id‘ für nil: NilClass in der Linie

app = App::Get.(id: app.id).model 

jedoch die Linie

p app.id 

, die kurz vor dem störende Zeile zeigt die richtige ID an.

Was könnte möglicherweise passieren?

Antwort

1

Diese Zeile:

let (:app) { App::Create.(app: {name: "Half-Life", steam_id: "70"}).model } 

schafft eine Methode namens app, die ein App zurückgibt, wenn aufgerufen. (Es memoizes das Ergebnis, so wird es die gleichen App nach dem ersten Aufruf immer wieder zurückkehren.)

Diese Zeile:

app = App::Get.(id: app.id).model 

schafft ein neue lokale Variable genannt app, die mit dem Verfahren nicht eindeutig ist app . Nach the Ruby docs on assignment:

In Ruby lokalen Variablennamen und Methodennamen sind nahezu identisch. Wenn Sie keinem dieser zweideutigen Namen zugewiesen haben, wird ruby ​​ annehmen, dass Sie eine Methode aufrufen möchten. Sobald Sie den Namen zugewiesen haben, nimmt ruby ​​ an, dass Sie auf eine lokale Variable verweisen möchten.

Die lokale Variable erstellt wird, wenn der Parser die Zuordnung trifft, nicht, wenn die Zuordnung erfolgt:

Also im Moment, wenn Sie versuchen, app, eine neue lokale Variable zuweisen wird erstellt. Wenn die rechte Seite der Zuweisung ausgewertet wird, versucht app.id, id auf der lokalen Variablen app aufzurufen (die bisher nil ist).

Hier ist der einfachste Code, den ich davon reproduziert das Problem denken kann:

def a 
    "Hello" 
end 

p a.upcase # "HELLO" 
a = a.upcase # undefined method `upcase' for nil:NilClass (NoMethodError) 

Es gibt ein paar Möglichkeiten, um Ihren Code zu beheben. Ich denke, die beste Lösung ist bei weitem:

app2 = App::Get.(id: app.id).model 

Der Name app wurde bereits durch ein Verfahren verwendet, ...Verstecken Sie es nicht, indem Sie eine lokale Variable mit demselben Namen erstellen.

Sie könnten auch dies tun:

app = App::Get.(id: app().id).model # the parens disambiguate 

oder die Lösung, die Sie bereits haben:

app_id = app.id # capture the ID before the local variable gets created 
app = App::Get.(id: app_id).model 

Aber ich denke, diese beiden sind schlimmer als einfach nicht die Namenskollision in erster Linie die Schaffung .

+0

Danke, das macht sehr viel Sinn. –