3

每个用户都有很多角色;要找出用户是否具有“管理员”角色,我们可以使用以下has_role?方法:

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?情况,但是当我尝试在另一个关联中引用的用户上调用它时失败:

>> 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

是什么赋予了?

4

2 回答 2

3

Railsrespond_to? "is_admin?"在执行send.

因此,您还需要专攻respond_to?

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

注意:不要问我为什么rails 会检查respond_to?而不是仅仅在send那里做,我看不出有什么好的理由。

另外:最好的方法(Ruby 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
于 2010-07-21T19:26:20.500 回答
2

该类在到达模型之前ActiveRecord::Associations::AssociationProxy覆盖并拦截您正在查找的调用。method_missing

发生这种情况是因为 AP 检查模型是否respond_to?是方法,在您的情况下,它没有。

除了编辑 Rails 的源代码之外,您还有一些解决方案:

首先,使用元编程为用户对象手动定义每个 is_* 方法。就像是:

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?

或使用列出的其他答案之一。

于 2010-07-21T19:33:09.667 回答