Skip to content
anonoz edited this page Jul 8, 2020 · 17 revisions

FriendlyId Tips and Tricks

Feel free to add any helpful hints for other users here.

User editable slug - with defaults

Submitted by maxcal

Case:

We want our post class to have slug and title attributes that are editable by the user. The user should be able to edit the title and slug independently. The slug should default to a slugged version of the title.

example output:

post = Post.create(title: "How Long is a Long Long Time", slug: 'how-long')
post.slug 
# 'how-long'
post = Post.create(title: "How Long is a Long Long Time")
post.slug 
# 'how-long-is-a-long-long-time'
class Post < ActiveRecord::Base
  extend FriendlyId
  friendly_id :title, :use => [:slugged]
  validates_presence_of :title

  def should_generate_new_friendly_id?
    slug.blank? && title_changed?
  end
end

Slug Only

Submitted by benpolinsky

Case:

We want our Post to have an editable slug that doesn't default to another field. The record's id will be used if no slug is set.

example output:

post = Post.create(title: "How Long is a Long Long Time")
post.slug 
# nil
post.update_attributes(title: "Some other title", temporary_slug: "My favorite post)
post.slug 
# 'my-favorite-post'
class Post < ActiveRecord::Base
  attr_accessor :temporary_slug
  extend FriendlyId
  friendly_id :temporary_slug, :use => [:slugged]

  def should_generate_new_friendly_id?
    temporary_slug_changed?
  end
 
 # track changes in non persisted attribute
   
  def temporary_slug=(value)
    attribute_will_change!('temporary_slug') if temporary_slug != value
    @temporary_slug = value
  end
  
  def temporary_slug_changed?
    changed.include?('temporary_slug')
  end
  

end

Speed up mass backfilling

Submitted by anonoz

If your existing model has a lot of callbacks, validations etc, you can skip all of them by creating an empty model class of its own inside migration file. For example:

class AddFriendlyIdToCompanies < ActiveRecord::Migration[5.0]
  disable_ddl_transaction!

  # Bare minimum class to skip all callbacks
  class FriendlyIdCompany < ActiveRecord::Base
    self.table_name = 'companies'
    extend FriendlyId
    friendly_id :slug_candidate, use: [:slugged, :finders]

    def slug_candidate
      "#{name}"[0, 20] + " #{SecureRandom.hex[0, 6]}"
    end
  end

  def up
    unless column_exists?(:companies, :slug)
      add_column :companies, :slug, :text
      add_index :companies, :slug, unique: true, algorithm: :concurrently
    end

    print "Updating friendly_id slug for companies"
    FriendlyIdCompany.each do |row|
      row.save; print('.')
    end
    puts ' '
  end

  def down
    remove_index :companies, :slug
    remove_column :companies, :slug
  end
end

Just remember to not inclyde the history plug in, it doesn't work well with this because the sluggable_type will not be pointing to the original model class. You can use history in the normal model class with callbacks later on.