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
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
Class Method Summary (collapse)
-
+ (Object) included(model_class)
Configures the model instance to use the History add-on.
- + (Object) setup(model_class)
Instance Method Summary (collapse)
- - (Object) create_slug private
-
- (Object) scope_for_slug_generator
private
If we're updating, don't consider historic slugs for the same record to be conflicts.
Class Method Details
+ (Object) included(model_class)
Configures the model instance to use the History add-on.
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/friendly_id/history.rb', line 71 def self.included(model_class) model_class.class_eval do has_many :slugs, -> {order("#{Slug.quoted_table_name}.id DESC")}, { :as => :sluggable, :dependent => :destroy, :class_name => Slug.to_s } after_save :create_slug end end |
+ (Object) setup(model_class)
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/friendly_id/history.rb', line 57 def self.setup(model_class) model_class.instance_eval do friendly_id_config.use :slugged friendly_id_config.finder_methods = FriendlyId::History::FinderMethods if friendly_id_config.uses? :finders relation.class.send(:include, friendly_id_config.finder_methods) if ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2 model_class.send(:extend, friendly_id_config.finder_methods) end end end end |
Instance Method Details
- (Object) create_slug (private)
121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/friendly_id/history.rb', line 121 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) if friendly_id_config.uses?(:scoped) relation = relation.where(:scope => serialized_scope) end relation.delete_all slugs.create! do |record| record.slug = friendly_id record.scope = serialized_scope if friendly_id_config.uses?(:scoped) end end |
- (Object) scope_for_slug_generator (private)
If we're updating, don't consider historic slugs for the same record to be conflicts. This will allow a record to revert to a previously used slug.
111 112 113 114 115 116 117 118 119 |
# File 'lib/friendly_id/history.rb', line 111 def scope_for_slug_generator relation = super return relation if new_record? relation = relation.merge(Slug.where('sluggable_id <> ?', id)) if friendly_id_config.uses?(:scoped) relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope)) end relation end |