2016-03-21 3 views
0

Ich folgte Ryan Bates RC# 386 - Autorisierung. Ryans Originalcode ist wie folgt:Konvertierung von if/else zu case/wenn in Ruby

module Permissions 
    def self.permission_for(user) 
    if user.nil? 
     GuestPermission.new 
    elsif user.is_admin? 
     AdminPermission.new(user) 
    else 
     MemberPermission.new(user) 
    end 
    end 
end 

was wie erwartet funktioniert.

Ich muss mehr als nur drei Autorisierungsschemata zulassen. Ich muss zusätzliche Autorisierungsrollen hinzufügen, wie Editor, Moderator, Senior Editor usw. Ich versuche, dies zu tun, indem ich es in eine case -Statement ändern. Mein Code ist:

module Permissions 
    def self.permission_for(user) 
    case user#.role #TODO: This will eventually be refactored into a role checker... 
    when user.is_admin? 
     AdminPermission.new(user) 
    when user.current_user 
     MemberPermission.new(user) 
    else 
     GuestPermission.new 
    end 
    end 
end 

Allerdings bin ich auf

when user.is_admin? 

Irgendwelche Ideen ein NoMethod Fehler zu bekommen?

+1

Es ist, weil 'user.is_admin?. ===' nicht definiert ist. – sawa

+0

@Sawa - 'user.is_admin?' Ist definiert. Der gesamte Code von Ryan Bates funktioniert, und Sie können sehen, wo "elsif user.is_admin?" Im Arbeitscode enthalten ist. – Matteo

Antwort

2

Der Hauptvorteil - und der nur Vorteil, wenn Sie mich fragen - von einem case/when Block ist, dass die Bedingung in den case genau einmal und nur einmal ausgeführt werden.

Also in diesem Fall:

case some_relatively_expensive_operation() 
    when :foo 
    # ... 
    when :bar 
    # ... 
end 

some_relatively_expensive_operation wird nur einmal durchgeführt werden, während in diesem Fall:

if some_relatively_expensive_operation() == :foo 
    # ... 
elsif some_relatively_expensive_operation() == :bar 
    # ... 
end 

some_relatively_expensive_operation() läuft entweder einmal (wenn es :foo) oder zweimal (wenn es ist etwas anderes).

Dies ist nur für die Leistung spielt keine Rolle, es macht auch die Bedingung leichter später zu ändern, da Sie nur so einmal und nicht wie viele if/elsif Blöcke haben Sie zu tun haben.

Offensichtlich funktioniert diese Struktur überhaupt nicht, wenn Sie mehrere Bedingungen testen möchten, die unterschiedlich sind - was Ihr aktueller Code tut. Ja, Sie können dies mit einem Lambda umgehen, was eine andere Antwort tut, aber dies vermisst völlig den Sinn dieses Konstrukts und ist wie das Einschlagen eines Nagels mit einem Hammer.

Also, was Sie tun müssen, ist eine Methode zum Objekt hinzufügen user ist, die einen Wert mit der Rolle zurückgibt.Ich habe keinen Einblick in Ihrem User Modell, aber dies könnte einfach sein:

class User 

    # [..] 

    def role 
    if is_admin? 
     :admin 
    else 
     :member 
    end 
    end 
end 

Wir haben immer noch für die besonderen Gast Erlaubnis, zu überprüfen, die vor die Haupt case getan werden kann:

def self.permission_for(user) 
    # No current user - return Guest permissions 
    return GuestPermission.new unless user.current_user 

    case user.role 
    when :admin 
     AdminPermission.new(user) 
    when :member 
     MemberPermission.new(user) 
    end 
end 

Alternativ können Sie User.role ändern, um :guest zurückzugeben. Was auch immer Sie bevorzugen.

Jetzt werden Sie feststellen, dass diese Zeilen tatsächlich sehr ähnlich aussehen. Warum verwenden Sie hier überhaupt einen case/when Block? Können wir nicht einfach den Klassennamen von user.role konstruieren? Ja wir können!

# Get instance to the class 
    klass = Object.const_get "#{user.role.to_s.capitalize}Permission" 

    # And create it! 
    klass.new user 

Und wir können eine zweizeilige Methode dafür machen!

def self.permission_for(user) 
    return GuestPermission.new unless user.current_user 
    return Object.const_get("#{user.role.to_s.capitalize}Permission").new user 
end 

Oder auch nur eine Zeile, wenn User.role kehrt :guest, wenn es keine aktuellen Benutzer.

+0

Ist das nicht das, was Sie wollen, ist eine Verzweigung, die nur einmal ausgewertet wird, und nur für den Wert, den Sie analysieren möchten? Ich werde versuchen, Ihre Refactoring zu implementieren, und wenn es funktioniert, werde ich als die beste Antwort auswählen. FYI - nicht veröffentlicht, aber ich muss schließlich jeden Benutzer mehrere Rollen über mehrere Projekte/Abteilungen haben. Ich denke, dass Ihre Lösung die beste Grundlage dafür ist. Kuddos an alle, die geantwortet haben, und auf eine solche Art und Weise. Ich muss den Stack lieben! – Matteo

+0

@Matteo "Mehrere Rollen" ist nicht etwas, das Sie bereits erwähnt haben, das Implementieren könnte so einfach sein wie eine Schleife, die ich vermute: 'user.roles.map {| role | Object.const_get ("# {role.to_s.capitalize} Permission"). Neuer Benutzer} 'aber es würde vom Inhalt der verschiedenen' * Permission'-Klassen abhängen ... – Carpetsmoker

0

Case-Anweisung Prüfung Gleichheit jeder, wenn Klausel zum Beispiel

case x 
    when 1 
     puts "Do something when x === 1." 
    when "D" 
     puts "Do something when x === D." 
    else 
     puts "Doing nothing" 
end 

So für Ihr Beispiel Sie die if/then Stil Syntax behalten wollen würde oder etwas zu tun, wie folgt aus:

case user.role 
    when :guest 
     #something 
    when :admin 
     #something 
    #etc... 
end 

http://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-case

2
case user 
when ->(u) { u.is_admin? } then AdminPermission.new(user) 
when ->(u) { u.current_user } then MemberPermission.new(user) 
else GuestPermission.new 
end 

oder

case 
when user.is_admin? then AdminPermission.new(user) 
when user.current_user then MemberPermission.new(user) 
else GuestPermission.new 
end 

Ersterer verwendet den Proc#===, letzterer ist ein einwandfrei gültiger Fall ohne Ausgangszustand.

+1

Ich weiß nicht, warum # 2 nicht mehr verwendet wird. Es sieht so viel besser aus und ist viel sparsamer als "if/elsif/end". –

+0

@CarySwoveland genau. – mudasobwa

+2

Es ist zu schade, 'wenn' den' & 'Operator nicht unterstützt, z.B. 'when &: is_admin?'. Das würde die erste Lösung viel schöner machen. –