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.friendly.find params[:id]
# If an old id or a numeric id was used to find the record, then
# the request slug will not match the current slug, and we should do
# a 301 redirect to the new path
if params[:id] != @post.slug
return redirect_to @post, :status => :moved_permanently
end
end
end
Defined Under Namespace
Modules: Configuration, 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
- #history_is_up_to_date? ⇒ Boolean private
-
#scope_for_slug_generator ⇒ Object
private
If we're updating, don't consider historic slugs for the same record to be conflicts.
Class Method Details
.included(model_class) ⇒ Object
Configures the model instance to use the History add-on.
72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/friendly_id/history.rb', line 72 def self.included(model_class) model_class.class_eval do has_many :slugs, -> { order(id: :desc) }, **{ as: :sluggable, dependent: @friendly_id_config.dependent_value, class_name: Slug.to_s } after_save :create_slug end end |
.setup(model_class) ⇒ Object
62 63 64 65 66 67 68 69 |
# File 'lib/friendly_id/history.rb', line 62 def self.setup(model_class) model_class.instance_eval do friendly_id_config.use :slugged friendly_id_config.class.send :include, History::Configuration friendly_id_config.finder_methods = FriendlyId::History::FinderMethods FriendlyId::Finders.setup(model_class) if friendly_id_config.uses? :finders end end |
Instance Method Details
#create_slug ⇒ Object (private)
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/friendly_id/history.rb', line 122 def create_slug return unless friendly_id return if history_is_up_to_date? # 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.destroy_all unless relation.empty? slugs.create! do |record| record.slug = friendly_id record.scope = serialized_scope if friendly_id_config.uses?(:scoped) end end |
#history_is_up_to_date? ⇒ Boolean (private)
137 138 139 140 141 142 143 144 |
# File 'lib/friendly_id/history.rb', line 137 def history_is_up_to_date? latest_history = slugs.first check = latest_history.try(:slug) == friendly_id if friendly_id_config.uses?(:scoped) check &&= latest_history.scope == serialized_scope end check 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.
111 112 113 114 115 116 117 118 119 120 |
# File 'lib/friendly_id/history.rb', line 111 def scope_for_slug_generator relation = super.joins(:slugs) unless new_record? relation = relation.merge(Slug.where("sluggable_id <> ?", id)) end if friendly_id_config.uses?(:scoped) relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope)) end relation end |