ruby-on-rails – 如果after_initialize回调返回false,则返回nil对象

前端之家收集整理的这篇文章主要介绍了ruby-on-rails – 如果after_initialize回调返回false,则返回nil对象前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我的Rails应用程序有许多形成层次结构的模型.例如:零售商>部门>产品分类>产品>评论.

业务需求是,高级用户可以使用新的或现有的“普通”用户分享”层次结构中的任何单个元素.没有与他们共享的对象,普通用户无权查看(或做任何其他事情)与层次结构的任何级别中的任何对象.

共享过程包括选择共享是否授予对目标对象的只读,读取更新或完整CRUD的权限.

共享任何对象向该对象和层次结构中的所有较低级别对象授予R / O,R / W或CRUD权限,并向对象的所有直接祖先授予R / O权限.对象集合有机地增长,所以许可系统只需要记录user_id,share_id的object_id和共享的性质(R / O,CRUD等),就可以工作.随着该层次结构中的对象总体不断增长,在DB中为每个用户/对象组合创建一个显式权限记录是不切实际的.

相反,在用户请求周期开始时,ApplicationController收集所有权限记录(用户X具有部门#5的CRUD权限),并将它们保存在内存中的哈希中.一个权限模型知道当任何对象传递给它时如何评估哈希 – Permission.allow?(:show,Department#5)将根据用户权限哈希的内容返回true或false.

例如,我们来看看部门模式:

# app/models/department.rb
class Department < ActiveRecord::Base

  after_initialize :check_permission

  private

  def check_permission
    # some code that returns true or false 
  end

end

当check_permission方法返回true时,我希望Department.first正常恢复数据库中的第一条记录,但如果check_permission返回false,我想返回nil.

现在,我有一个解决方案,默认范围触发权限检查,但是这导致了查询数量的2倍,而对于具有大量对象的类,内存问题和时间/性能问题肯定会在视野中.

我的目标是使用after_initialize回调来预先对对象进行许可.

但是,after_initialize将无法阻止返回原始对象.它允许我重置对象的属性的值,但不要放弃它.

有人知道如何实现吗?

编辑:

非常感谢所有提供的答案和意见;希望这个扩展版本的问题澄清了一些事情.

解决方法

基本上您需要在返回数据库查询结果之前检查访问权限(或权限).而您正在尝试将此逻辑整合到您的模型中.

这是可能的,但不是您在问题中描述的设计.在ActiveRecord适配器方法(如第一个,所有,最后一个等等)中直接实现它并不干净.你需要重新思考你的设计.

(如果这太多阅读,请跳到“D”)

您有几个选择,这些选择都取决于您的权限的定义方式.我们来看几例:

A.用户列出了他拥有的部门,只有他可以访问他们

您可以简单地将其实现为与Active Record Associations的has_many / belongs_to关联

B.用户和部门是独立的(换句话说:没有上述情况下所述的所有权),并且可以为每个用户和每个部门单独设置权限.

您再次可以实现与Active Record Associations的has_and_belongs_to_many关联.您将需要创建Web逻辑,以便您的应用程序的管理员可以添加/编辑/删除访问权限.

C.更复杂的情况:现有的授权库

大多数人会转向授权解决方案,如cancan,punditother

D.当这些授权库对您的需求过大时(实际上是我的大部分项目中的情况),我发现通过rails scoping实现授权可以满足我的所有需求.

让我们通过一个简单的例子看看.我希望管理员能够访问整个数据库记录;并且常规用户仅访问状态=开放且仅在操作时间(例如上午8点至下午6点)的部门.我写了一个实现我的权限逻辑的范围

# Class definition
class Department
  scope :accessible_by -> (user) do
    # admin user have all access,always
    if user.is_admin?
      all
    # Regular user can access only 'open' departments,and only
    # if their request is done between 8am and 6pm
    elsif Time.now.hour >= 8 and Time.now.hour <= 18
      where status: 'open'
    # Fallback to return ActiveRecord empty result set
    else
      none
    end
  end
end

# Fetching without association
Department.accessible_by(current_user)

# Fetching through association
Building.find(5).departments.accessible_by(current_user)

定义范围要求我们在我们的代码中使用它.您可以考虑“忘记”范围并直接访问模型的风险(即写入Department.all而不是Department.accessible_by(current_user)).所以这就是为什么你必须坚定地测试您的规范(在控制器或功能级别)的权限.

注意在这个例子中,当权限失败(如你在你的问题中提到),而不是一个空的结果集,我们不返回nil.通常情况下,您可以保持ActiveRecord方法链接功能.但是您也可以引发异常并将其从控制器中救出,然后重定向到“未授权”页面.

猜你在找的Ruby相关文章