2010-07-21 30 views
3

每個用戶都有很多角色;找出用戶是否具有「管理員」的角色,我們可以使用has_role?方法:將`is_x?`混疊到`has_role? x`

some_user.has_role?('admin')

它是這樣定義的:

def has_role?(role_in_question) 
    roles.map(&:name).include?(role_in_question.to_s) 
end 

我希望能夠寫some_user.has_role?('admin')作爲some_user.is_admin?,所以我所做的:

def method_missing(method, *args) 
    if method.to_s.match(/^is_(\w+)[?]$/) 
     has_role? $1 
    else 
     super 
    end 
    end 

也能正常工作的情況下some_user.is_admin?,但是當我嘗試CA失敗將其放在另一個關聯中引用的用戶上:

>> Annotation.first.created_by.is_admin? 
NoMethodError: undefined method `is_admin?' for "KKadue":User 
    from /Library/Ruby/Gems/1.8/gems/activerecord-2.3.4/lib/active_record/associations/association_proxy.rb:215:in `method_missing' 
    from (irb):345 
    from :0 

什麼給出了?

回答

3

Rails會在執行send之前檢查您是否在respond_to? "is_admin?"之前。

所以,你需要專注respond_to?也喜歡:

def respond_to?(method, include_private=false) 
    super || method.to_s.match(/^is_(\w+)[?]$/) 
end 

:不要問我爲什麼檢查respond_to?,而不是僅僅做一個send那裏,我沒有看到好理由。

:最好的辦法(紅寶石1.9.2+)是定義respond_to_missing?相反,你可以與所有版本的東西有點像看中兼容:

def respond_to_missing?(method, include_private=false) 
    method.to_s.match(/^is_(\w+)[?]$/) 
end 

unless 42.respond_to?(:respond_to_missing?) # needed for Ruby before 1.9.2: 
    def respond_to?(method, include_private=false) 
    super || respond_to_missing?(method, include_private) 
    end 
end 
+0

詳細說明,您需要重寫'respond_to?'以完成此項工作。 – rfunduk 2010-07-21 19:30:31

2

ActiveRecord::Associations::AssociationProxy類覆蓋method_missing,並截取您在找到模型之前所尋找的呼叫。

發生這種情況是因爲AP檢查模型respond_to?的方法,在您的情況下,它不。

你有幾個解決方案,除了編輯Rails的來源:

首先,手動定義每個確保對方的*使用元編程的用戶對象的方法。喜歡的東西:

class User 
    Role.all.each do |role| 
    define_method "is_#{role.name}?" do 
     has_role?(role.name) 
    end 
    end 
end 

另一個是通過一些其它手段來加載用戶對象如

User.find(Annotation.first.user_id).is_admin? 

或者使用列出的其他的答案中的一個。