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_globalize
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
-
.included(model_class) ⇒ Object
Configures the model instance to use the History add-on.
- .setup(model_class) ⇒ Object
Instance Method Summary collapse
- #create_slug ⇒ Object private
-
#scope_for_slug_generator ⇒ Object
private
If we're updating, don't consider historic slugs for the same record to be conflicts.
- #super_create_slug(locale) ⇒ Object private
Class Method Details
.included(model_class) ⇒ Object
Configures the model instance to use the History add-on.
71 72 73 74 75 76 77 78 79 80 |
# File 'lib/friendly_id/history.rb', line 71 def self.included(model_class) model_class.class_eval do has_many :slugs, -> {order(Slug.arel_table[:id].desc)}, as: :sluggable, dependent: :destroy, class_name: Slug.to_s after_save :create_slug end end |
.setup(model_class) ⇒ Object
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
#create_slug ⇒ Object (private)
120 121 122 123 124 |
# File 'lib/friendly_id/history.rb', line 120 def create_slug translations.map(&:locale).each do |locale| ::Globalize.with_locale(locale) { super_create_slug(locale) } end end |
#scope_for_slug_generator ⇒ Object (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.
110 111 112 113 114 115 116 117 118 |
# File 'lib/friendly_id/history.rb', line 110 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 |
#super_create_slug(locale) ⇒ Object (private)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/friendly_id/history.rb', line 126 def super_create_slug(locale) return unless friendly_id return if slugs.where(locale: locale).first.try(:slug) == friendly_id # Allow reversion back to a previously used slug relation = slugs.where(slug: friendly_id, locale: locale) 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.locale = locale record.scope = serialized_scope if friendly_id_config.uses?(:scoped) end end |