2012-05-25 4 views
5

Ich versuche, ein ActiveRecord-Modell (Vote) zu erweitern, das ein Edelstein (https://github.com/peteonrails/vote_fu) für meine Anwendung bereitstellt. (Dh, es gibt keine vote.rb in app/models)Erneutes Öffnen eines ActiveRecord-Modells, das von einem Juwel bereitgestellt wird

Mein erster Ansatz eine Datei lib/extend_vote.rb genannt zu erstellen war, dass der Code enthält:

Vote.class_eval do 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    # something.. 
    end 
end 

Dies funktioniert, wenn die erste Abstimmung erstellt wird, aber wenn ich versuche, Erstellen Sie jede nachfolgende Abstimmung Ich bekomme den Fehler TypeError (can't dup NilClass).

denke ich, dieser Fehler durch die Tatsache verursacht wird, dass die Vote Klasse automatisch nach jeder Anfrage neu geladen wird, aber der Code in lib/extend_vote.rb wird nur einmal geladen, wenn der Server gestartet wird und dies die has_one :activity_stream_event Vereinigung verursacht weirdly zu verhalten. (Auch geht das Problem weg, wenn ich gesetzt config.cache_classes = true in development.rb)

Um dieses Problem zu lösen, habe ich versucht, die Abstimmung Erweiterungen bei jeder Anfrage neu geladen, um durch Hinzufügen eines to_prepare Block zu meinem development.rb:

config.to_prepare do 
    load 'extend_vote.rb' 
end 

Dies löst das Problem (can't dup NilClass), aber jetzt, wenn ich eine neue Abstimmung erstellen, ruft der Rückrufeine zusätzliche Zeit. Das heißt, die erste Stimme ruft sie einmal auf, die zweite ruft sie zweimal auf usw. Es scheint so, als ob der Block to_prepare die Erweiterung TOO aggressiv neu lädt und doppelte Rückrufe hinzufügt.

Was ist der beste Weg, Methoden und Callbacks zu diesem Vote Modell hinzuzufügen?

+1

Funktioniert es, wenn Sie 'class Vote' statt' Vote.class_eval' verwenden? Eine Sache, die Sie auch tun können, ist, den Code im Edelstein selbst zu bearbeiten und nur Ihre modifizierte Version zu verwenden. – agmcleod

+0

'class Vote' verhält sich genauso wie' Vote.class_eval' - keiner funktioniert. Ich denke, ich könnte den Edelstein modifizieren, aber ich möchte wirklich nicht lol. Was für ein Chaos! –

+0

Warum denken Sie, dass die Vote-Klasse neu geladen wird? In der Ressource befindet sich die Klasse im Verzeichnis lib, also ist es dasselbe wie Sie ... – Dougui

Antwort

1

Ich würde versuchen, was in den Kommentaren vorgeschlagen agmcleod sondern es in lib des Setzens, steckt es in config/initializers/vote.rb:

class Vote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    # something.. 
    end 
end 

Natürlich könnten Sie die gem Gabel mach deine Änderungen und verlinke zu deiner gegabelten Version in deiner Gemfile (das ist meine Präferenz).

+0

warum 'config/initialiser' statt' lib'?Ich nehme an, ich muss die 'load' Anweisung im' to_prepare' Block behalten? –

+0

None sollte diese Ladeanweisung nicht benötigen, da die Elemente in den Initialisierungsdateien beim Start für alle Umgebungen einmal geladen werden. – miked

+0

Das funktioniert nicht - wenn ich den 'load' im' to_prepare' Block belasse, bekomme ich den gleichen Fehler, den ich bekam, als sich die Datei in 'lib' befand. Wenn ich das 'load' entferne, dann bekomme ich den' 'can not dup NilClass)' Fehler –

0

Sie könnten versuchen, so etwas wie dieses:

class Vote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
     # something.. 
    end 
end 

Ich denke, als es Ihre Funktion und Anruffunktionen „after_create“ und „has_hone“ hinzufügen.

+0

Entschuldigung, ich habe den Kommentar des Amalcleod nicht gesehen. Es ist wahrscheinlich das gleiche Denken ... Es sollte funktionieren, aber es tut es nicht. – Dougui

+0

heh, zumindest hast du keine Abstimmung bekommen wie ich ... und unsere Vorschläge waren, naja, das selbe! lol – miked

+0

Dies kann sein, weil Sie sagen, es in einen Initialisierer zu setzen. Oder, vielleicht, sage ich es nach dir. – Dougui

4

Ein Wort der Vorsicht: Dies ist ein sehr altes Juwel (letzte Commit ist 3 Jahre alt) und nach dem Aussehen wird es nicht mit Schienen 3.x arbeiten, wie es ist. In Rails 3.x-Engines macht diese Art von Sachen viel einfacher.

Wie ich es verstehe, ist das Problem im ersten Fall nicht, dass das Votum Modell neu geladen wird (sollte es nicht), aber dass das activity_stream_event Modell neu geladen wird. Da das Abstimmungsmodell nicht neu geladen wird, bleibt die Assoziation vor dem Neuladen an der Version der Klasse activity_stream_event hängen. Da Rails Klassen auskern, bevor sie neu geladen werden, führt dies zu Problemen.

Mit diesem in mir, versuchen diesen Hack:

#in config/initializers/abstract_vote.rb 
AbstractVote = Vote 
AbstractVote.abstract_class = true 
Object.send :remove_const, :Vote 

#in app/models/vote.rb 

class Vote < AbstractVote 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 

    def create_activity_stream_event 
    end 
end 

Was das bedeutet ist, können Sie Ihre eigenen Vote Klasse haben, die sich von der in den Edelstein erbt.

Aber noch einmal, ich fordere Sie etwas mehr auf dem neuesten Stand zu finden oder Ihre eigene Rolle (das Juwel nur ~ 250 Zeilen von Rubin)

6

[UPDATE: sollte die richtige Lösung sein wobei das Modul gehört zu verhindern mehrere Male in der gleichen Klasse]

Ich glaube, dass Sie ActiveSupport::Concern verwenden können, um zu verhindern, dass das Modul mehrfach enthalten ist, was durch mehrere Male aufgerufenen Rückruf resultiert. das Beispiel Siehe unten:

module VotePatch 
    extend ActiveSupport::Concern 

    included do 
    after_create :create_activity_stream_event 
    has_one :activity_stream_event 
    end 

    module InstanceMethods 
    def create_activity_stream_event 
     #your code here 
    end 
    end 

end 

Vote.send(:include, VotePatch) 
+0

aktualisiert meine Antwort, ich glaube ActiveSupport :: Concern wird Ihr Problem beheben –

0

Ihr Problem aufgrund der Tatsache sein könnte, dass Sie Affen sind die Klasse Patchen. Wenn rails versucht, die Konstanten neu zu laden, wird Ihre Datei nicht berücksichtigt.

Versuchen Sie die Modultechnik wie unten angegeben zu verwenden.

Fügen Sie eine Datei lib/vote_fu_extension.rb genannt

module VoteFuExtension 
    def self.included(base) 
    base.has_one :activity_stream_event 
    base.after_create :create_activity_stream_event 
    end 
    def create_activity_stream_event 
    # something.. 
    end 
end 
Vote.send(:include, VoteFuExtension) 

schreiben initializer genannt config/initializers/vote_fu.rb

require "vote_fu_extension" 

Hinweis

Wenn Sie Klassenmethoden zum Vote Modell dieses answer beziehen hinzufügen möchten .

Shameless Stecker: Meine fork der vote_fu Edelstein hat einige neue Funktionen und Verbesserungen.

1

Adrien Coquio hat die richtige Idee mit ActiveSupport::Concerns, die the Rails way sind, um Modelle zu erweitern. Sein Code wird funktionieren, und Sie sollten es verwenden.

Allerdings wird dies nicht immer in der Entwicklung funktionieren, denn wenn Rails Ihre Klassen neu lädt, wenn sich eine Datei ändert, wird die Zeile #send nicht erneut ausgewertet. Die einzige Lösung, die ich finden konnte, war in der Produktion zu einem ActionDispatch Rückruf Anbringen der Datei, um sicherzustellen, ist wieder erforderlich nach jeder Seite zu laden:

if Rails.env.development? 
    ActionDispatch::Callbacks.to_prepare do 
    require_dependency "../../lib/vote_fu_extensions" 
    end 
end 

In der Produktion, oder wenn Sie cache_classes auf true in der Config gesetzt, Sie gewonnen Ich brauche das nicht.