Module: FriendlyId::History

Defined in:
lib/friendly_id/history.rb

Overview

History: Avoiding 404's When Slugs Change

FriendlyId's History module adds the ability to store a log of a model's slugs, so that when its friendly id changes, it's still possible to perform finds by the old id.

The primary use case for this is avoiding broken URLs.

Setup

In order to use this module, you must add a table to your database schema to store the slug records. FriendlyId provides a generator for this purpose:

rails generate friendly_id
rake db:migrate

This will add a table named friendly_id_slugs, used by the Slug model.

Considerations

This module is incompatible with the :scoped module.

Because recording slug history requires creating additional database records, this module has an impact on the performance of the associated model's create method.

Example

class Post < ActiveRecord::Base
  extend FriendlyId
  friendly_id :title, :use => :history
end

class PostsController < ApplicationController

  before_filter :find_post

  ...

  def find_post
    @post = Post.find params[:id]

    # If an old id or a numeric id was used to find the record, then
    # the request path will not match the post_path, and we should do
    # a 301 redirect that uses the current friendly id.
    if request.path != post_path(@post)
      return redirect_to @post, :status => :moved_permanently
    end
  end
end

Defined Under Namespace

Modules: FinderMethods, SlugGenerator

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Object) included(model_class)

Configures the model instance to use the History add-on.



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/friendly_id/history.rb', line 60

def self.included(model_class)
  model_class.instance_eval do
    raise "FriendlyId::History is incompatible with FriendlyId::Scoped" if self < Scoped
    @friendly_id_config.use :slugged
    has_many :slugs, :as => :sluggable, :dependent => :destroy,
      :class_name => Slug.to_s, :order => "#{Slug.quoted_table_name}.id DESC"
    after_save :create_slug
    relation_class.send :include, FinderMethods
    friendly_id_config.slug_generator_class.send :include, SlugGenerator
  end
end

Instance Method Details

- (Object) create_slug (private)



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/friendly_id/history.rb', line 74

def create_slug
  return unless friendly_id
  return if slugs.first.try(:slug) == friendly_id
  # Allow reversion back to a previously used slug
  relation = slugs.where(:slug => friendly_id)
  result = relation.select("id").lock(true).all
  relation.delete_all unless result.empty?
  slugs.create! do |record|
    record.slug = friendly_id
  end
end