2012-03-28 10 views
0

カスタムロールベースの承認システムを持つ既存のRails 2.3.xアプリケーションをメンテナンスします。rails habtm:関連付けられたレコードを返しますが、排他的一致を返します

コードは次のようなものがあります。

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 

役割テーブルを私は以下のメンバーを持っている
(id, name, level)
1, admin, 1000
2, VIP, 500
3, regular, 100
4, some_other_role, 50

のようなものを持っています述べられた役割
MEMBER1(役割:adminVIPregular
member2(役割:VIPregular
member3(役割:regular)私は時々必要なもの

は、彼らの最高の割り当てに基づいてメンバーをプルアップですロール:

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

生のSQLクエリを書き込むことなく、Railsでこれを行う方法を頭で囲むことはできません。

提案がありますか?


更新:月29日、2012
これは、基本的にはそれぞれの役割について(よくdefine_methodと一緒にいくつかの動的なプログラミングを使用して())は、このようなメソッドの束を定義するための私のソリューションでした。

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 

ただし、古いレール2.3.xに問題があることを発見しました。たとえば、Member.vips_exclusivelyにsize()またはcount()を呼び出すと、不正確な合計が生成されます。 length()を呼び出すと正しい結果が得られますが、可能な限りsize()を使用することをお勧めします。

Railsコードを見た後、scoped()で設定した場合、:group:havingのようなオプションはcount()に渡されません。スコープ付き()をnamed_scopes(アップデート:なし:)に置き換えると、カウントの問題が解決します。

私はChrisの提案と、正確さと簡潔さのためのいくつかの編集を取り入れました。ありがとうございました!


別のアップデート。
実際に:groupと:の問題は、渡されていないため、named_scoped実装にもあります。

そして、これまでにRailsソースツリー(少なくとも2.3.xブランチにはない)に修正された古いチケットがあります。
https://rails.lighthouseapp.com/projects/8994/tickets/1349-named-scope-with-group-by-bug

これは素晴らしいです...

答えて

0

私はあなたが直接SQLクエリを記述する必要がありますとは思わないが、私はあなたには、いくつかのSQLグループでnamed_scopeを必要とし、あなたが探しているものを行うための句を持つと思う:

でアプリ/モデル/ member.rbアプリ/モデル/ role.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 } 
} 

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 

>> 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 
関連する問題