/
history.rb
104 lines (79 loc) · 3.2 KB
/
history.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
require "friendly_id/slug"
module FriendlyId
=begin
This 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 {FriendlyId::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.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
=end
module History
# Configures the model instance to use the History add-on.
def self.included(model_class)
model_class.instance_eval do
raise "FriendlyId::History is incompatibe with FriendlyId::Scoped" if self < Scoped
@friendly_id_config.use :slugged
has_many :slugs, :as => :sluggable, :dependent => :destroy, :class_name => Slug.to_s
before_save :build_slug, :if => lambda {|r| r.should_generate_new_friendly_id?}
relation_class.send :include, FinderMethods
end
end
private
def build_slug
slugs.build :slug => friendly_id
end
# Adds a finder that explictly uses slugs from the slug table.
module FinderMethods
# Search for a record in the slugs table using the specified slug.
def find_one(id)
return super if id.unfriendly_id?
where(@klass.friendly_id_config.query_field => id).first or
with_old_friendly_id(id) {|x| find_one_without_friendly_id(x)} or
find_one_without_friendly_id(id)
end
# Search for a record in the slugs table using the specified slug.
def exists?(id = nil)
return super if id.unfriendly_id?
exists_without_friendly_id?(@klass.friendly_id_config.query_field => id) or
with_old_friendly_id(id) {|x| exists_without_friendly_id?(x)} or
exists_without_friendly_id?(id)
end
private
# Accepts a slug, and yields a corresponding sluggable_id into the block.
def with_old_friendly_id(slug, &block)
sql = "SELECT sluggable_id FROM #{Slug.quoted_table_name} WHERE sluggable_type = %s AND slug = %s"
sql = sql % [@klass.base_class.name, slug].map {|x| connection.quote(x)}
sluggable_id = connection.select_values(sql).first
yield sluggable_id if sluggable_id
end
end
end
end