7

I am using the friendly_id gem. In the portfolio.rb I placed these two lines:

  extend FriendlyId
  friendly_id :title, use: :slugged

As you can see I am also using the slug option. When I create a project with title "example" it works find and I can find the project under mysite.com/projects/example. Now, if I create a second one with the same title I get a title for it like this one: mysite.com/projects/example-74b6c506-5c61-41a3-8b77-a261e3fab5d3. I don't really like this title. I was hoping for a friendlier title like example-2.

At this question, RSB (user) told me that its friendly_id that causes that. I was wondering if there is a way to create a more friendly. At first I thought of "manually" checking if the same title exists (in a while loop) and assigning another title using either example-2 or example-3 or... example-N.

However do I need to do something like that or am I missing something? Is there an easier way to do something like that?

Community
  • 1
  • 1

2 Answers2

7

Check the documentation for the latest version of friendly_id:

A new "candidates" functionality which makes it easy to set up a list of alternate slugs that can be used to uniquely distinguish records, rather than appending a sequence.

Example straight from the docs:

class Restaurant < ActiveRecord::Base
  extend FriendlyId
  friendly_id :slug_candidates, use: :slugged

  # Try building a slug based on the following fields in
  # increasing order of specificity.
  def slug_candidates
    [
      :name,
      [:name, :city],
      [:name, :street, :city],
      [:name, :street_number, :street, :city]
    ]
  end
end
Thilo
  • 17,565
  • 5
  • 68
  • 84
  • This is correct I think, although for reasons different than the question! – Richard Peck Aug 19 '14 at 09:31
  • Awesome! Thanks for sharing! It can be used as a solution as well! –  Aug 19 '14 at 09:32
  • @RichPeck Not sure what you mean? The question is how to generate a friendlier slug for duplicates. This answers it, in a general way, no? – Thilo Aug 19 '14 at 09:38
  • 1
    Yep it does, but it will append other names to the slug - I think he was looking for a more "robust" way of handling it – Richard Peck Aug 19 '14 at 09:43
  • I wrote an answer for you - don't mean to detract in any way, I just wanted to find out how to do it myself! – Richard Peck Aug 19 '14 at 10:01
4

UUID

The problem you're alluding to is the way in which friendly-id appends a hash (they call it a UUID) to duplicate entries:

Now that candidates have been added, FriendlyId no longer uses a numeric sequence to differentiate conflicting slug, but rather a UUID (e.g. something like 2bc08962-b3dd-4f29-b2e6-244710c86106). This makes the codebase simpler and more reliable when running concurrently, at the expense of uglier ids being generated when there are conflicts.

I don't understand why they have done this, as it goes against the mantra of friendly ID, but nonetheless, you have to appreciate that's how it works. And whilst I don't think the slug_candidates method above will prove any more successful, I do think that you'll be able to use something like a custom method to define the slug you wish

--

You'll want to read this documentation (very informative)

It says there are two ways to determine the "slug" your records are assigned, either by using a custom method, or by overriding the normalize_friendly_id method. Here's my interpretation of both of these for you:

Custom Method

#app/models/project.rb
Class Project < ActiveRecord::Base
   extend FriendlyID
   friendly_id :custom_name, use: :slugged

   def custom_name
     name = self.count "name = #{name}"
     count = (name > 0) ? "-" + name : nil 
     "#{name}#{count}"
   end
end

Normalize_Friendly_ID

#app/models/project.rb
Class Project < ActiveRecord::Base
   extend FriendlyID
   friendly_id :name, use: :slugged

   def normalize_friendly_id
     count = self.count "name = #{name}"
     super + "-" + count if name > 0
   end
end
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • Using a UUID prevents conflicts from happening when databases are merged (Which might happen regularly when a web application is large enough to be distributed across multiple servers+databases). Although it is only my personal assumption, I believe this to be the reason for the friendly_id team to have changed the slug conflict behaviour to use an UUID – Qqwy Feb 10 '16 at 17:54
  • note the normalize method you provided no longer works and i get the error "wrong number of arguments (given 1, expected 0)". i know this is from a while ago but any help would be appreciated because this approach seems reasonable to me to override the default appending of uuid's. also note I am using a scope: :account_id on the friendly_id field (:name), in case this will affect the code to get this working – chrickso Apr 15 '18 at 15:42
  • note i have created a separate question thread for this: https://stackoverflow.com/questions/49844078/using-rails-5-how-can-i-make-friendlyid-append-a-count1-to-duplicate-slugs – chrickso Apr 15 '18 at 16:15