before_filter mit Parametern

82

Ich habe eine Methode, die so etwas macht:

before_filter :authenticate_rights, :only => [:show]

def authenticate_rights
  project = Project.find(params[:id])
  redirect_to signin_path unless project.hidden
end

Ich möchte diese Methode auch in einigen anderen Controllern verwenden, daher habe ich die Methode in einen Helfer kopiert, der im application_controller enthalten ist.

Das Problem ist, dass in einigen Controllern die ID für das Projekt nicht das :idSymbol ist, sondern zB :project_id(und auch a :idvorhanden ist (für ein anderes Modell)

Wie würden Sie dieses Problem lösen? Gibt es eine Option, um der Aktion before_filter einen Parameter hinzuzufügen (um den richtigen Parameter zu übergeben)?

wählen
quelle

Antworten:

84

Ich würde es so machen:

before_filter { |c| c.authenticate_rights correct_id_here }

def authenticate_rights(project_id)
  project = Project.find(project_id)
  redirect_to signin_path unless project.hidden
end

Wo correct_id_hereist die relevante ID für den Zugriff auf a Project.

Alex
quelle
2
Gibt es eine Möglichkeit, ein ,:only => [:show]Symbol hinzuzufügen ? Ich erhalte einen Fehler beim Versuchbefore_filter { |c| c.authenticate_rights correct_id_here }, :only => [:show]
wählen Sie den
27
Versuchen Sie es anders herum : before_filter(:only => [:show]) { <block_code_here> }. Weitere Beispiele hier: apidock.com/rails/ActionController/Filters/ClassMethods/…
fguillen
1
Wenn Sie dies sichern, indem Sie die before_filterMethode privat machen, müssen Sie sie möglicherweise neu faktorisieren (z. B. auf einen übergeordneten Controller, ApplicationController usw. verschieben), c.send(:filter_name, ...)da der Filter nicht im Controller-Kontext ausgeführt wird. guides.rubyonrails.org/…
Richard Michael
2
Dadurch kann der before_filter nicht überschrieben / übersprungen werden, da kein Name (proc) vorhanden ist. Die Werte, die ich übergeben möchte, sind Klassenebene. Ist das möglich?
Oreoshake
1
@oreoshake: Ich habe das gleiche Problem. Haben Sie eine Lösung gefunden?
marcus3006
66

Mit etwas syntaktischem Zucker:

before_filter -> { find_campaign params[:id] }, only: [:show, :edit, :update, :destroy]

Oder wenn Sie sich entscheiden, noch ausgefallener zu werden:

before_filter ->(param=params[:id]) { find_campaign param }, only: %i|show edit update destroy|

Und da Rails 4 before_action, ein Synonym für before_filter, eingeführt wurde, kann es wie folgt geschrieben werden:

before_action ->(param=params[:id]) { find_campaign param }, only: %i|show edit update destroy|

NB

->steht für lambda, Lambda-Literal genannt , in Ruby 1.9 einführen

%i erstellt ein Array von Symbolen

Vadym Tyemirov
quelle
5
Diese Antwort ist eleganter, da das Lambda standardmäßig den Ausführungskontext der Klasse verwendet. Daher können private Methoden ohne Verwendung von '.send' aufgerufen werden
David Pelaez
@Vadym Tyemirov Ist find_campaignder private Methodenname? In param=params[:id]ist paramder Name der neuen lokalen Variablen, an die als Argument übergeben wird find_campaign? Das heißt, dass find_campaignwir innerhalb der privaten Methode selbst paramnicht verwenden params{:id]?
Ahnbizcad
1
find_campaignkönnte auch sein, aber ich würde es privat machen, um sicherzugehen, dass wir nicht offenlegen, was nicht konsumiert wird. paramsist eine Hash-Variable, die unseren Methoden paramzur Verfügung steht. Ist eine Variable, die Sie an die Methode find_campaign übergeben müssen, z. B.before_action ->(campaign_id=params[:id]) { find_campaign(campaign_id) }, only: %i| show edit update destroy |
Vadym Tyemirov,
14

Um die Antwort von @alex fortzusetzen, wenn Sie möchten :exceptoder :onlyeinige Methoden, ist hier die Syntax:

before_filter :only => [:edit, :update, :destroy] do |c| c.authenticate_rights params[:id] end 

Gefunden hier .

Augustin Riedinger
quelle
5

Ich finde die Blockmethode mit geschweiften Klammern do...enddie klarste Option

before_action(only: [:show]) { authenticate_rights(id) }

before_action ist nur die neuere bevorzugte Syntax für before_filter

Teilbaum
quelle
-1

Das sollte funktionieren:

project = Project.find(params[:project_id] || params[:id])

Dies sollte zurückgegeben werden, params[:project_id]wenn es im params-Hash vorhanden ist, oder zurückgeben, params[:id]wenn dies nicht der Fall ist.

Matheus Moreira
quelle
Das Problem ist, dass manchmal beide vorhanden (verschachtelt) sind und ein Projekt gefunden wird, das nicht das richtige ist.
Wahl
@choise: Das sollte nicht passieren: Wenn project_idvorhanden, stellt die orKlausel sicher, dass dies verwendet wird - nur wenn a project_idnicht angegeben wird, wird der idParameter ausgewählt. Mit anderen Worten: Wenn beide Parameter angegeben werden, stellt die orKlausel sicher, dass der richtige Wert verwendet wird, wie es immer bevorzugt wird project_id. Natürlich möchten Sie diese Methode nicht aufrufen, wenn keine vorhanden ist oder wenn keine project_idvorhanden ist, aber eine vorhanden ist, iddie nicht auf ein Projekt verweist.
Ola Tuvesson