norman / friendly_id

FriendlyId is the “Swiss Army bulldozer” of slugging and permalink plugins for ActiveRecord. It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models.
http://norman.github.io/friendly_id/
MIT License
6.13k stars 589 forks source link

How can I handle this scoped slug use case without a new option from Friendly ID to disable UUID generation? #996

Open nickjj opened 2 years ago

nickjj commented 2 years ago

Here's a stripped down example of what I have set up:

class Course < ActiveRecord::Base
  extend FriendlyId

  validates :title, presence: true, length: { maximum: 70 }
  validates :slug, presence: true, length: { maximum: 70 }, uniqueness: { scope: :version }
  validates :version, presence: true, length: { maximum: 10 }

  friendly_id :slug_candidates, use: [:slugged]

  def slug_candidates
    [
      :title,
      [:title, :version]
    ]
  end
end

My database migration has:

t.text :title, null: false, default: ""
t.text :slug, null: false
t.text :version, null: false

add_index :courses, %i[slug version], unique: true

The issue

I want to explicitly require that a version is always provided when creating a course such as:

Course.create!(title: "Yay for learning", version: "v1")

This works fine and you end up with a slug of yay-for-learning because the version didn't have to be taken into account to be unique.

But if you run this a second time you'll end up with a slug of yay-for-learning-v1 which technically allows you to have version: v1 twice which isn't what I'm trying to do.

If you run it a third time you get yay-for-learning-0fa8243c-78a9-4fe3-b89b-080117d98b57 as a slug since the slug+version alone isn't unique.

What I tried

I can't add uniqueness: true to the version on its own at the Rails validation level because that would prevent (2) completely different course titles from ever having v1 set for their versions when each course on its own should be able to do that.

This also doesn't work:

  validates :slug, presence: true, length: { maximum: 70 }, uniqueness: { scope: :version }
  validates :version, presence: true, length: { maximum: 10 }, uniqueness: { scope: :slug }

Because technically the slug gets generated with a UUID so the slug is always different. This is where I think the only way this can be solved is if Friendly ID provides a mechanism to disable UUIDs from being generated and instead bubble up an ActiveRecord validation error (or DB error if no validation exists at the Rails level).

Is there something I'm missing here?

What I'm trying to do

In case it's not clear my intent is:

That last bullet is what I'm having trouble getting to work with Friendly ID because instead of letting this validation error happen it behind the scenes generates a UUID to "fix" the uniqueness issue.

stale[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

nickjj commented 1 year ago

Just replying so it doesn't get auto-closed by a bot.