2010-08-19 4 views
16

Ist es möglich, Variablen im Übergang zu senden?Übergeben von Variablen an Rails StateMachine-Edelsteinübergänge

dh
@car.crash!(:crashed_by => current_user) 

Ich habe Rückrufe in meinem Modell, aber ich brauche sie den Benutzer zu senden, die den Übergang angestiftet

after_crash do |car, transition| 
    # Log the car crashers name 
end 

Ich kann nicht current_user zugreifen, da ich im Modell bin und nicht der Controller- /Aussicht.

Und bevor Sie es sagen ... Ich weiß, ich weiß.

Versuchen Sie nicht, Session-Variablen in dem Modell

ich es zuzugreifen.

Wenn Sie jedoch einen Rückruf erstellen möchten, der etwas protokolliert oder auditiert, ist es sehr wahrscheinlich, dass Sie wissen wollen, wer das verursacht hat. Normalerweise würde ich etwas in meinem Controller haben, der etwas tat, wie ...

@foo.some_method(current_user) 

und meine Foo Modell würde einige Benutzer some_method anstiften erwarten, aber wie soll ich tun dies mit einem Übergang mit dem State gem?

Antwort

32

Wenn Sie die state_machine gem beziehen - https://github.com/pluginaweek/state_machine - dann unterstützt es Argumente zu Ereignissen

after_crash do |car, transition| 
    Log.crash(:car => car, :driver => transition.args.first) 
end 
+2

Zufällig stolperte ich bei einer Google-Suche wieder über meine eigene Frage und diese Antwort war sehr hilfreich. Vielen Dank. –

+0

Ich denke, das ist definitiv besser als User.current, das ich sehe, dass andere implementieren, wenn sie auf diese Art von Problem stoßen. Die Benutzerinteraktion gehört zur Controller-Ebene. –

+2

Das funktioniert gut, aber Sie müssen den Übergang korrekt buchstabieren. Lösung oben hat ein extra 's'. – Brenda

2

Ich glaube nicht, dass Sie Parameter an Ereignisse mit diesem Juwel übergeben können, also könnten Sie versuchen, den current_user auf @car (vorübergehend) zu speichern, damit Ihr Audit-Callback darauf zugreifen kann.

In Controller

@car.driver = current_user 

In Rückruf

after_crash do |car, transition| 
    create_audit_log car.driver, transition 
end 

Oder etwas in diese Richtung .. :)

+0

Dank Keeran. Das ist im Grunde, was ich am Ende getan habe. Da das Attribut keine Permanenz hat - irgendwelche Gedanken darüber, wie Sie dies tun könnten, ohne ein neues Feld in der DB zu erstellen? –

+0

Sie könnten versuchen, einen attr_accessor für: Treiber hinzuzufügen, aber ich bin mir nicht sicher, ob der Callback eine 'frische' Instanz des Modells neu lädt/verwendet (und so die temporäre Variable löscht). – keeran

7

Ich hatte Probleme mit allen anderen Antworten, und dann stellte ich fest, dass Sie das Ereignis in der Klasse einfach überschreiben können.

class Car 
    state_machine do 
    ... 
    event :crash do 
     transition any => :crashed 
    end 
    end 
    def crash(current_driver) 
    logger.debug(current_driver) 
    super 
    end 
end 

So stellen Sie sicher

+0

Dies ist bei weitem die einfachste, einfachste Antwort. –

0

Ein weiteres gemeinsames Muster „super“ in der benutzerdefinierten Methode aufzurufen (siehe state_machine docs), die Sie speichert von Variablen zwischen dem Controller und Modell passieren zu müssen ist, um dynamisch einen Zustand zu definieren -Kontrollmethode innerhalb der Callback-Methode. Dies wäre im obigen Beispiel nicht sehr elegant, könnte aber in Fällen bevorzugt sein, in denen das Modell die gleiche (n) Variable (n) in verschiedenen Zuständen handhaben muss.Zum Beispiel, wenn Sie ‚abgestürzt‘, ‚gestohlen‘ haben und ‚ausgeliehen‘ Zustände in Ihrem Auto-Modell, die alle mit einer verantwortlichen Person in Verbindung gebracht werden kann, könnten Sie haben:

state :crashed, :stolen, :borrowed do 
    def blameable? 
    true 
    end 

state all - [:crashed, :stolen, :borrowed] do 
    def blameable? 
    false 
    end 

dann in der Steuerung , können Sie etwas wie:

car.blame_person(person) if car.blameable? 
1

Ich verwendete Transaktionen, anstatt das Objekt zu aktualisieren und den Zustand in einem Anruf zu ändern. Zum Beispiel, in Update-Aktion,

ActiveRecord::Base.transaction do 
    if @car.update_attribute!(:crashed_by => current_user) 
    if @car.crash!() 
     format.html { redirect_to @car } 
    else 
     raise ActiveRecord::Rollback 
    else 
    raise ActiveRecord::Rollback 
    end 
end