2012-03-28 6 views
0

Eine vorhandene Rails 2.3.x-App mit einem benutzerdefinierten rollenbasierten Autorisierungssystem wird beibehalten.Rails hattm: gibt verknüpfte Datensätze zurück, aber mit ausschließlicher Übereinstimmung

Der Code hat etwas Ähnliches:

class Role << AR:Base 
    # has an int attribute called "level" with higher values indicating more powerful role 
    habtm: members 
end 

class Member << AR:Base 
    habtm: roles 
end 

Tabelle Rollen hat so etwas wie
(id, name, level)
1, admin, 1000
2, VIP, 500
3, regular, 100
4, some_other_role, 50

Ich habe folgende Mitglieder mit genannten Rollen
member1 (Rollen: admin, VIP, regular)
member2 (Rollen: VIP, regular)
member3 (Rollen: regular)

Was ich manchmal noch Mitglieder Pull-up auf der Grundlage ihrer höchsten zugewiesen Rolle:

Role.admins_exclusively # should return member1 
Role.vips_exclusively  # should return just member2 
Role.regulars_exclusively # should be just member3 

Kann meinen Kopf um nicht umgebrochen, wie dies zu tun in Rails, ohne Abfragen zu schreiben rohe SQL greifen zu müssen.

Irgendwelche Vorschläge?


Update: 29. März 2012
Dies war meine Lösung im Grunde eine Reihe von Methoden wie folgt definieren (auch einige dynamische Programmierung zusammen mit define_method() verwendet wird) für jede Rolle.

class Member < AR:Base 
    define_method :vips_exclusively do 
    scoped :joins => :roles, 
    :group => 'members.id', 
    :having => ["max(roles.level) = ?", Role.find_by_name('vip').level] 
    end 
end 

Allerdings habe ich festgestellt, dass es ein Problem mit älteren Schienen 2.3.x gibt. Das Aufrufen von size() oder count() auf Member.vips_exclusively würde beispielsweise zu falschen Summen führen. Calling length() würde das korrekte Ergebnis liefern, aber es wird empfohlen, size() zu verwenden, wo immer es möglich ist.

Nach dem Blick auf Rails-Code, es sieht aus wie Optionen wie :group und :having nicht weitergegeben werden, um count() wenn im Bereich() festgelegt. Ersetzen von Aufrufen von scoped() mit named_scopes (update: DOES NOT) lösen das Zählproblem.

Also habe ich Chris 'Vorschlag zusammen mit einigen Bearbeitungen für die Korrektheit/Kürze übernommen. Vielen Dank!


Ein weiteres Update.
Tatsächlich ist das Problem von: group und: nicht übergeben wurde auch in named_scoped Implementierung.

Und sicher genug, hier ist ein veraltetes Ticket ohne Fehler, der es jemals in den Quellbaum von Rails geschafft hat (zumindest nicht in der 2.3.x-Verzweigung).
https://rails.lighthouseapp.com/projects/8994/tickets/1349-named-scope-with-group-by-bug

Das ist großartig ...

Antwort

0

Ich glaube nicht, werden Sie direkt SQL-Abfragen schreiben müssen, aber ich denke, Sie mit einigen SQL-Gruppe eine named_scope benötigen und Klauseln, die zu tun, was Sie suchen:

In app/models/member.rb

named_scope :maximum_level, lambda { |level| { 
    :having => [ 'MAX(roles.level) = ?', level ], 
    :group => 'members.id', # edited to need quotes 
    :joins => :roles # dont need the whole join statement } 
} 

In app/models/role.rb

def exclusive_members 
    Member.maximum_level(self.level).all 
end 

def self.members_by_role_name(role_name) 
    role = self.find(:conditions => ['name = ?', role_name]).first 
    role.exclusive_members 
end 

Beispiel

>> r = Role.find(1) 
=> <Role id:1, name:"admin", level:1000> 
>> r.exclusive_members 
=> [ list of members with a highest role of "admin"] 
>> r.exclusive_members.map { |m| m.name } 
=> [ "member1" ] 
>> Role.members_by_role_name("admin") 
=> # the same list as you'd get by calling r.exclusive_members 
Verwandte Themen