2009-04-30 7 views
8

Ich möchte in der Lage sein, von einer Controller-Aktion zu einer anderen bedingt zu senden, basierend auf einer Kombination von Abfrageparametern und Daten in der Datenbank.Was ist der richtige Weg, um eine Controller-Aktion von einer anderen Controller-Aktion ohne HTTP-Umleitung auszuführen?

Was ich jetzt habe, ist so etwas wie:

class OldController < ApplicationController 
    def old_controller_action 
    if should_use_new_controller 
     new_params = params.dup 
     new_params[:controller] = "new_controller_action" 
     redirect_to new_params 
     return 
    end 
    # rest of old and busted 
    end 
end 

class NewController < ApplicationController 
    def new_controller_action 
    # new hotness 
    end 
end 

Das funktioniert ganz gut, aber es gibt eine HTTP-Umleitung, die langsam ist. Ich würde gerne in der Lage sein, dasselbe zu tun, aber innerhalb derselben HTTP-Anfrage.

Gibt es einen sauberen Weg, dies zu tun?

Edit: Das Kopfgeld wird an jemanden gehen, der mir einen sauberen Weg zeigen kann, der die Controller und ihre Aktionen relativ unberührt lässt (anders als der Redirect-Code selbst).

Antwort

13

Anstatt Code über Aktionen hinweg aufzurufen, extrahieren Sie den Code in lib/oder etwas und rufen Sie diesen Code von beiden Controllern ab.

# lib/foo.rb 
module Foo 
    def self.bar 
    # ... 
    end 
end 

# posts_controller 
def index 
    Foo.bar 
end 

# things_controller 
def index 
    Foo.bar 
end 
+0

Ja, ich hatte auch daran gedacht, obwohl es aus Sicht der Code-Organisation keine besonders aufregende Option ist. –

+1

Es ist eigentlich in Ordnung, da es auf zusammenhängenden Methode erstellt von zwei Orten, die leicht testbar ist –

7

eine Instanz der Controller-Klasse erstellen:

@my_other_controller = MyOtherController.new 

Dann Methoden nennen:

@my_other_controller.some_method(params[:id]) 

ich das Modul Idee bevorzugen, aber dies den Trick tun sollten.

+0

erstellt Dies wird nicht so gut funktionieren. Es ist schwierig, die 'render'-Methode innerhalb dieser neuen Instanz des Controllers zu nennen. Wenn Sie Rendering im ursprünglichen Controller aufrufen, verlieren Sie alle Variablen, die im aufgerufenen Controller gesetzt sind. – holli

1

Ich vermute, Sie Option wollen 3, aber die einige Alternativen gehen durch die erste

Option 1 lässt - Schieben Sie die Controller-Auswahllogik in einen Helfer, der den richtigen Link in Ihrer Ansicht einfügt. Vorteile - Controller bleiben sauber, Nachteile - wenn die Entscheidungslogik von den übergebenen Werten abhängt, wird dieser Ansatz nicht funktionieren. Wenn URL von externen Websites aufgerufen wird, funktioniert das nicht.

Option 2 - Schieben Sie die Logik zurück in Ihr Modell. Pro's - hält den Controller sauber. Nachteile - funktioniert nicht gut, wenn Sie viel Sesson, Params oder Render/redirect_to Interaktion haben.

Option 3 - Bleiben Sie innerhalb des gleichen Controllers. Ich vermute, dass Sie versuchen, einige vorhandene Funktionalität durch einige neue Funktionalität zu ersetzen, aber nur in einigen Fällen. Pro's - Einfach und haben Zugriff auf alles, was Sie brauchen. Nachteile - funktioniert nur, wenn es sinnvoll ist, denselben Controller zu verwenden, d. H. Sie arbeiten mit derselben Entität wie Benutzer, Ort oder Firma.

Ermöglicht für Ihre Wahl ein Beispiel aussehen 3. Meine Links-Controller hat völlig diferent behavour für Administratoren als andere Benutzer ...

class LinksController < ApplicationController 
    #... 

    def new 
    #Check params and db values to make a choice here 
    admin? ? new_admin : new_user 
    end 

    #... 

private 

    def new_admin 
    #All of the good stuff - can use params, flash, etc 
    render :action => 'new_admin'  
    end 

    def new_user 
    #All of the good stuff - can use params, flash, etc 
    render :action => 'new_user' 
    end 

end 
0

Wenn zwei Controller die gleiche Sache zu tun versuchen, gibt es eine sehr gute Chance, das sollte in einem Modell sein.Nehmen Sie einen guten Blick auf Ihr Design und - es tut mir leid ich weiß nicht, Ihre Erfahrung mit MVC - lesen Sie auf dünnen Controller Techniken:

http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model http://www.robbyonrails.com/articles/2007/06/19/put-your-controllers-on-a-diet-already http://andrzejonsoftware.blogspot.com/2008/07/mvc-how-to-write-controllers.html

Wenn das Problem dass du den anderen Controller brauchst, um das Rendern zu machen, dann sollte vielleicht die Route dorthin zeigen, und die Technik des mageren Controllers sollte den Tag retten.

-1

tun:

class OldController < ApplicationController 
    def old_controller_action 
    if should_use_new_controller 
     new_controller_action 
    end 
    # rest of old and busted 
    end 
end 

und der neue Controller

class NewController < OldController 
    def new_controller_action 
    # new hotness 
    end 
end 
0

Wenn den gemeinsamen Code zwischen Controllern in ein Modul Extrahieren nicht für Sie arbeiten, würde ich Rack-Middleware verwenden. Ich habe keinen Code gesehen, der ActiveRecord in Middleware verwendet, aber ich weiß nicht, warum es nicht möglich sein sollte, da Leute Redis und dergleichen verwendet haben.

Ansonsten denke ich, Ihre einzige Option Verarbeitung der Anforderung mit so etwas wie (ungetestet, Pseudo-Beispiel) neu zu starten wäre:

env['REQUEST_URI'] = new_controller_uri_with_your_params 
call(env) 

Dies ist ähnlich wie Integrationstests durchgeführt werden. Aber ich weiß nicht, ob alles von call bis zu einem Controller ist idempotent und sicher, so zu wiederholen. Sie könnten die Quelle verfolgen und sehen. Aber selbst wenn es jetzt in Ordnung ist, könnte es in jeder zukünftigen Version von Schienen oder Racks brechen.

Die Verwendung von Middleware würde dies vermeiden, indem Sie die Anforderung abfangen, bevor sie ausgeführt wurde. Sie sollten immer noch in der Lage sein, Code mit Ihrer Rails-Anwendung zu teilen, indem Sie ihn in gemeinsame Module extrahieren, die an beiden Stellen enthalten sind.

Ehrlich gesagt denke ich, dass es einfach sauberer ist, das einfache Controller-Code zu faktorisieren, aber es ist schwer, ohne die Details deiner Situation zu wissen, also dachte ich mir, ich würde das vorschlagen.

Verwandte Themen