满足特定条件时仅 运行 一组验证的最佳实践?

Best practice for only running a group of validations when a certain condition is met?

我有一个包含 3 个验证的评论模型,如果评论的 status"submitted",则应该只 运行。这是我目前的模型...

    class Review < ApplicationRecord
      enum status: { pending: 0, submitted: 1 }
      ...

      belongs_to :reviewable, polymorphic: true
      belongs_to :user

      validates :title, presence: true, length: { in: 5..50 }, if: -> { status == "submitted" }
      validates :description, length: { in: 10..500 }, if: -> { status == "submitted" }
      validates :stars, presence: true, inclusion: { in: (1..5) }, if: -> { status == "submitted" }
      ...
    end

已生成状态为 "pending" 的新评论。我仍然需要在创建新评论时验证 reviewableuser 是否与评论相关联(因此,我不能在创建操作中一起跳过验证)...但仅需要在用户更新评论时验证 titledescriptionstars

有没有办法将三个验证(标题、描述和星级)包装在一个条件中,以便它只 运行 在用户更新评论时进行验证(这会改变statuspendingsubmitted)...而不是对所有这些都调用 if: -> { status == "submitted" }

第一个简化是您可以使用符号而不是内联块:

  validates :title, presence: true, length: { in: 5..50 }, if: :submitted?
  validates :description, length: { in: 10..500 }, if: :submitted?
  validates :stars, presence: true, inclusion: { in: (1..5) }, if: :submitted?

一般来说,你会定义一个方法来匹配:

  def submitted?
    status == "submitted"
  end

..但是你不需要在这里,因为enum免费给你这样的方法


如果真的要挤,还有一个更隐晦的选择:

  with_options if: -> { status == "submitted" } do |x|
    x.validates :title, presence: true, length: { in: 5..50 }
    x.validates :description, length: { in: 10..500 }
    x.validates :stars, presence: true, inclusion: { in: (1..5) }
  end

(我切换回块,因为只有一个副本,但在这种情况下我仍然喜欢 if: :submitted? 形式。)

with_options 不是特别知名或广泛使用的方法 -- 它在那里,但我只建议在非常不寻常的情况下使用它。