docbrown/app/models/cross_model_slug.rb
Joshua Wehner a20cf3e02b
Unique cross-model-slug check is likely misconfigured (#19263)
* 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>
2023-04-07 17:04:45 +02:00

37 lines
1.3 KiB
Ruby

#
# We have a simple top-level route equivalent to `/slug`. Because of this,
# we want to verify that newly created records don't overlap with a previously-
# defined `slug` -- in other words, slug values should be unique across all
# relevant models. Except also, models might use "username" instead of "slug".
#
# "Slug-like" models are all included in a cross-model-uniqueness check. An
# impacted models are checked for the existence of a record matching a given
# value. Additionally, we have some special cases (eg, sitemap) that we want to
# apply across all registered models.
#
class CrossModelSlug
MODELS = {
"User" => :username,
"Page" => :slug,
"Podcast" => :slug,
"Organization" => :slug
}.freeze
class << self
def exists?(value)
# Presence check is likely redundant, but is **much** cheaper than the
# cross-model check
return false if value.blank?
value = value.downcase
# Reserved check may be redundant, but allows this to be used outside of Validator
return true if ReservedWords.all.include?(value)
return true if value.include?("sitemap-") # https://github.com/forem/forem/pull/6704
MODELS.detect do |class_name, attribute|
class_name.constantize.exists?({ attribute => value })
end
end
end
end