Validates_associated to include 'only' and 'except' options

I have a use case where I’d like a record to validate a nested has_many association, but only those nested records which meet a certain condition (or in this case, only those which don’t).

This isn’t the same as if:/unless: which are conditions on the main record (which I also have), but rather only:/except:, e.g.

validates_associated :credentials, on: :update, unless: :allow_incomplete?, except: :expired?

We’ve achieved this by monkey-patching AssociatedValidator like so:

module ActiveRecord
  module Validations
    class AssociatedValidator < ActiveModel::EachValidator
      def validate_each(record, attribute, value)
        opts = options.dup
        conditions = opts.extract! :only, :except
        if Array(value).any? { |r| invalid_object?(r, **conditions) }
          record.errors.add(attribute, :invalid, **opts.merge(value: value))
        end
      end

      private

      def invalid_object?(record, only: proc { true }, except: proc { false })
        !record.try(:marked_for_destruction?) &&
        only.to_proc.call(record) &&
        !except.to_proc.call(record) &&
        !record.valid?
      end
    end
  end
end

I was going to open this as a PR but guidelines advise proposing it here first. Is this an okay idea?

1 Like