2010-07-21 38 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? 

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