* Page unique_cross_model_slug seems misconfigured * Update validator test * More verbose invalid attribute message * Try dynamic model/attribute registration * Rubocop * Use the cross-model-slug existence check * Register with the dynamic cross-model-slug checker * Cleanup * Add missing keys to fr * Update format regex * Adjust validation: always presence, unique if changed * Tests can create reserved-word pages when they need to * Slugs can be mixed case? * Case-sensitive is better, actually * Refactor to use CrossModel check * Refactor, rename for clarity * Refactor, avoid mocking oneself * Refactor, injectable everything * Add reservedword check to extracted exists? checker * Move to concerns * Without dynamic registration * extend when needed * Cleanup comments, remove registration references --------- Co-authored-by: Goran <gorang.pub@gmail.com>
39 lines
1.2 KiB
Ruby
39 lines
1.2 KiB
Ruby
##
|
|
# Validates if the give attribute is used across the reserved spaces.
|
|
class CrossModelSlugValidator < ActiveModel::EachValidator
|
|
FORMAT_REGEX = /\A[0-9a-z\-_]+\z/
|
|
|
|
def validate_each(record, attribute, value)
|
|
return if value.blank?
|
|
|
|
correct_format?(record, attribute, value)
|
|
not_on_reserved_list?(record, attribute, value)
|
|
unique_across_models?(record, attribute, value)
|
|
end
|
|
|
|
private
|
|
|
|
def not_on_reserved_list?(record, attribute, value)
|
|
return unless ReservedWords.all.include?(value)
|
|
|
|
record.errors.add(attribute, I18n.t("validators.cross_model_slug_validator.is_reserved"))
|
|
end
|
|
|
|
def correct_format?(record, attribute, value)
|
|
return if value.match?(FORMAT_REGEX)
|
|
|
|
record.errors.add(attribute, I18n.t("validators.cross_model_slug_validator.is_invalid"))
|
|
end
|
|
|
|
def unique_across_models?(record, attribute, value)
|
|
# attribute_changed? is likely redundant, but is much cheaper than the cross-model exists check
|
|
return unless record.public_send("#{attribute}_changed?")
|
|
return unless already_exists?(value)
|
|
|
|
record.errors.add(attribute, I18n.t("validators.cross_model_slug_validator.is_taken"))
|
|
end
|
|
|
|
def already_exists?(value)
|
|
CrossModelSlug.exists?(value.downcase)
|
|
end
|
|
end
|