Onichan
Onichan

Reputation: 4526

Use Numeric Sequence for Duplicate Slugs [Friendly ID]

I noticed on the docs it says: Previous versions of FriendlyId appended a numeric sequence to make slugs unique, but this was removed to simplify using FriendlyId in concurrent code.

Is there any way to revert back to this format? My model only has name so there aren't any other viable slug candidates and (time or date wouldn't make sense in this case for slug candidates).

How can I change this (current format):

car.friendly_id #=> "peugeot-206"
car2.friendly_id #=> "peugeot-206-f9f3789a-daec-4156-af1d-fab81aa16ee5"
car3.friendly_id #=> "peugeot-206-f9dsafad-eamj-2091-a3de-fabsafafdsa5"

Into this:

car.friendly_id #=> "peugeot-206"
car2.friendly_id #=> "peugeot-206-1"
car3.friendly_id #=> "peugeot-206-2"

Upvotes: 0

Views: 1353

Answers (5)

Dorian
Dorian

Reputation: 9145

Example use of sequentially_slugged

class Pipeline < ApplicationRecord
  extend FriendlyId

  friendly_id :name, use: :sequentially_slugged

  belongs_to :user

  scope :published, -> { where(published: true) }
end

Upvotes: 1

manish nautiyal
manish nautiyal

Reputation: 2584

create a file in /config/initializers/slugged.rb

This works for me

module FriendlyId::Slugged
  def resolve_friendly_id_conflict(candidates)
    column = friendly_id_config.slug_column
    separator = friendly_id_config.sequence_separator
    slug = normalize_friendly_id(candidates.first)
    sequence = self.class.where("#{column} like '#{slug}#{separator}%'").count + 2
    "#{slug}#{separator}#{sequence}"
  end
end

Output will be

for first record  => firstname-lastname

for second record => firstname-lastname-2

for third record  => firstname-lastname-3

Upvotes: 0

kimrgrey
kimrgrey

Reputation: 562

Old behaviour was implemented in special module. But at the moment it has not been released yet. So, if you want to restore old behaviour you can switch friebdly_id in your Gemfile on Github and add sequentially_slugged to the modules list.

Upvotes: 3

MilesStanfield
MilesStanfield

Reputation: 4639

I realize you said

(time or date wouldn't make sense in this case for slug candidates)

But in assuming you we're only referring to the string format of time and not unix which is a numeric sequence then I came up with this workaround to your problems/concerns:

  1. your model only has name attribute
  2. you can't use id to append to the slug because it hasn't been created yet
  3. you want the slugs to be uniq and incrementing
  4. you don't want to use UUID

# app/models/car.rb
class Car < ActiveRecord::Base
  extend FriendlyId
  friendly_id :name, use: :slugged

  def normalize_friendly_id(string)
    incremented_number = (Time.now.to_f * 1000000).to_i
    "#{super}-#{incremented_number}"
  end
end

So now this works

car1 = Car.create(name: "peugeot")
car2 = Car.create(name: "peugeot")
car3 = Car.create(name: "peugeot")

car1.friendly_id #=> "peugeot-1451368076324115"
car2.friendly_id #=> "peugeot-1451368076457560"
car3.friendly_id #=> "peugeot-1451368076460087"

Note: the numbers are incrementing

Time.now.to_f * 1000 would be miliseconds and I'm using Time.now.to_f * 1000000 which is MICROSECONDS <- that's one millionth of a second. It's NOT going to be created at the same time and therefore won't run into slug conflicts. And if someone out there thinks it could then just add a few more zeros to that multiplier.

Upvotes: 2

Martin M
Martin M

Reputation: 8658

There are good reasons that "serial number" was replaced by UUID (race conditions).
I normally use an additional slug candidate with the ID, that is maintained uniq by the db and much shorter than an UUID:

[ [:name], [:name, :id] ]

Upvotes: -1

Related Questions