很难将 `is_x?` 别名为 `has_role? x`

发布于 2024-09-10 12:22:45 字数 906 浏览 6 评论 0原文

每个用户有多个角色;要查明用户是否具有“admin”角色,我们可以使用 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

什么给出?

Each user has many roles; to find out whether a user has the "admin" role, we can use the has_role? method:

some_user.has_role?('admin')

Which is defined like this:

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

I'd like to be able to write some_user.has_role?('admin') as some_user.is_admin?, so I did:

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

This works fine for the some_user.is_admin? case, but fails when I try to call it on a user referenced in another association:

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

What gives?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深海蓝天 2024-09-17 12:22:45

Rails 检查您是否respond_to?在发送之前输入“is_admin?”

因此,您需要专门化 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

Rails checks if you respond_to? "is_admin?" before doing a send.

So you need to specialize respond_to? also like:

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

Note: Don't ask me why rails checks for respond_to? instead of just doing a send there, I don't see a good reason.

Also: The best way (Ruby 1.9.2+) is to define respond_to_missing? instead, and you can be compatible with all versions with something a bit fancy like:

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
生来就爱笑 2024-09-17 12:22:45

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?

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

The ActiveRecord::Associations::AssociationProxy class overrides method_missing and intercepts the call you are looking for before it gets to the model.

This happens because AP checks if the model respond_to? the method, which in your case, it doesn't.

You have a few solutions aside from editing Rails' source:

First, manually define each of the is_* methods for the user object using metaprogramming. Something like:

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

Another is to load the User object via some other means such as

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

Or use one of the other answers listed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文