2009-08-03 7 views
0

ich eine Klasse ähnlich der folgenden haben:Catch-22: Schienen db Migration läuft nicht, wenn Klassenkonstanten db-Anrufe

class FruitKinds < ActiveRecord::Base 
    Apple = FruitKinds.find(:all).find { |fk| 
    fk.fruit_name == :apple.to_s 
    } 

    # ... other fruits 

    # no methods 
end 

Apple und andere spezifische Früchte werden als Standardwerte an anderer Stelle in meinem häufig verwendete Anwendung, so will ich ein handliches Mittel, auf sie in einer aufzählbaren, statischen Weise zu verweisen.

Allerdings gibt es ein Problem. Es gibt eine Datenbankmigration, um die Tabelle zu erstellen und sie mit der speziellen Fruits wie Apple zu füllen. Wenn die Datenbankmigration ausgeführt wird, um FruitKinds zu initialisieren, kann rake nicht gestartet werden, da zuerst FruitKinds geladen wird. Dies ruft dann die Datenbank auf, was natürlich fehlschlägt, da die FruitKinds-Tabelle noch nicht vorhanden ist.

Die Problemumgehung besteht darin, die FruitKinds :: * Felder während der Migration auskommentieren, aber das ist schrecklich und hacky. Was ist der beste Weg, dies zu tun?

Antwort

1

Das ist eine ziemlich häufige Sache, zu dem Punkt, dass ich jetzt jeden Datenbankzugriff auf der Klassendefinitionsebene als Antipattern betrachte. Die Alternative ist, es zu einem faulen Eigentum zu machen. Der einfachste Weg, dies zu tun, ist einfach, es zu einer Methode auf Klassenebene anstatt einer Konstante zu machen. Beachten Sie, dass Sie aktivierten Methoden in Ruby haben, so können Sie es wie eine Konstante aussehen, wenn Sie wollen:

class FruitKinds < ActiveRecord::Base 
    def self.Apple 
    @apple ||= FruitKinds.find(:all).find { |fk| 
     fk.fruit_name == :apple.to_s 
    } 
    end 

    # ... other fruits 

    # no methods 
end 

Oder wenn Sie Lust bekommen möchten, können Sie const_missing verwenden, um dynamisch die Konstante das erste Mal, es zu schaffen wird zugegriffen.

Als Randbemerkung, das ist über die ineffiziente Art und Weise möglich, einen Datensatz nach Namen zu finden ;-)

+0

Konntest du nicht selbst schreiben.Apple so dass es die db maximal einmal trifft? Sie könnten den Wert irgendwo in einem Feld speichern und einen attr_accessor haben. –

+0

Ich habe Memoisierung als Übung für den Leser hinterlassen, aber da - ich habe den obigen Code geändert, um den Wert in einer Klassenvariablen zu speichern. – Avdi

+0

BTW, einen Grund, warum Sie nicht nur 'FruitKinds.find_first_by_fruit_name (" apple ")' für den Fund verwenden? – Avdi

0

Wie Avdi erwähnt, werden Sie mit der Datenbank interagieren vermeiden wollen, wenn die Klasse geladen wird. Wenn Sie Datenbankdatensätze im lokalen Speicher zwischenspeichern möchten, sollten Sie die in Rails 2.2 hinzugefügte Memoization-Funktion verwenden. See my post here für Details.

Verwandte Themen