【问题标题】:Rails - apply an array of scopesRails - 应用一系列范围
【发布时间】:2019-01-17 02:27:51
【问题描述】:

我有一个类,它接受一个范围数组并迭代地应用它们:

class AssignableLearningObjectives::Collector
  def initialize(user:, only_self_assignable: false, scopes: [])
    @user = user
    @only_self_assignable = only_self_assignable
    @scopes = scopes
  end

  .....

  def available_objectives
    objectives = assignable_objectives.or(manager_assigned_objectives).or(global_objectives).distinct
    return objectives unless scopes.any?

    scopes.each{ |scope| objectives = objectives.send(scope) }
    objectives
  end

我的问题是

scopes.each{ |scope| objectives = objectives.send(scope) }
objectives

有没有更好的方法来做到这一点?我希望有一个 rails 方法 apply_scopes 或类似的东西,但是找不到类似的东西。

我担心的是范围是从控制器发送的,用户可以提交范围为“destroy_all”或同样有趣的请求。

有没有一种简单的方法可以让 Rails 处理这个问题?或者我需要在将每个范围应用到集合之前手动检查它吗?

提前致谢

编辑:

如果需要,我很乐意单独验证每个范围,但即使这样也会导致问题。 rails 中有一种方法在 3.0.9 中被删除,我可以使用它,Model.scopes:

https://apidock.com/rails/v3.0.9/ActiveRecord/NamedScope/ClassMethods/scopes

但是,这已被弃用。有什么方法可以调用一个类来列出它的范围吗?我不敢相信 Rails 3 中有该功能并完全删除了......

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    来自fine guide

    14 个作用域
    [...]
    为了定义一个简单的作用域,我们在类中使用 scope 方法,传递我们希望在调用这个作用域时运行的查询:

    class Article < ApplicationRecord
      scope :published, -> { where(published: true) }
    end
    

    这和定义一个类方法完全一样,使用哪个看个人喜好:

    class Article < ApplicationRecord
      def self.published
        where(published: true)
      end
    end
    

    所以scope 主要是创建一个应该具有特定行为(即返回关系)的类方法的一种奇特方式,并且任何返回关系的类方法方法都是一个作用域。 scope 曾经很特别,但现在它们只是类方法,所有类方法都被复制到关系中以支持链接。

    没有办法知道方法Model.m 是否是一个“真实”范围,它将返回一个关系或一些随机类方法,而无需运行它并检查它返回的内容或手动检查其源代码。你所寻求的scopes方法已经消失了,再也不会回来了。

    您可以尝试将每个您知道不好的类方法列入黑名单;这种方式存在错误和疯狂。

    唯一明智的选择是将每个您知道是好的并且您希望用户能够调用的类方法都列入白名单。然后你应该在控制器和AssignableLearningObjectives::Collector 内部过滤scopes 数组。我会检查这两个地方,因为根据可用的信息以及您通过代码采用的路径,您可以对允许的内容有不同的标准;我想 DRY 稍微少一点,但效率和稳健性不是朋友。

    您可以在AssignableLearningObjectives::Collector 构造函数或available_objectives 中应用范围白名单。


    如果你想要比以下更漂亮的东西:

    scopes.each{ |scope| objectives = objectives.send(scope) }
    objectives
    

    那么你可以使用inject:

    def available_objectives
      objectives = assignable_objectives....
      scopes.inject(objectives) { |objectives, scope| objectives.send(scope) }
    end
    

    【讨论】:

    • 感谢您抽出宝贵时间回答,我只是不明白为什么他们删除了一种具有明显用途的方法,SO 上的各种帖子都使用了它。正如您所建议的那样,我将沿着白名单范围的路线进行检查,以检查通过发送的范围
    • scope 的行为在某个地方发生了变化,作用域和类方法变得相同。这使得无法区分scope 生成的类方法、直接实现为类方法的作用域以及其他类方法。 scopes 方法消失了,因为它变得无法实现,并且可能被认为不够重要,无法强制使用 scope 声明所有范围。无论如何,将这些东西列入白名单是个好主意。
    猜你喜欢
    • 2023-04-05
    • 2021-06-14
    • 2012-11-27
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2021-05-13
    • 2021-09-06
    • 1970-01-01
    相关资源
    最近更新 更多