mil_friendly_id 4.0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +12 -0
- data/.travis.yml +20 -0
- data/.yardopts +4 -0
- data/Changelog.md +86 -0
- data/Gemfile +15 -0
- data/Guide.rdoc +553 -0
- data/MIT-LICENSE +19 -0
- data/README.md +150 -0
- data/Rakefile +108 -0
- data/WhatsNew.md +95 -0
- data/bench.rb +63 -0
- data/friendly_id.gemspec +43 -0
- data/gemfiles/Gemfile.rails-3.0.rb +21 -0
- data/gemfiles/Gemfile.rails-3.1.rb +22 -0
- data/gemfiles/Gemfile.rails-3.2.rb +22 -0
- data/geothird_friendly_id.gemspec +45 -0
- data/lib/friendly_id.rb +114 -0
- data/lib/friendly_id/base.rb +291 -0
- data/lib/friendly_id/configuration.rb +80 -0
- data/lib/friendly_id/finder_methods.rb +35 -0
- data/lib/friendly_id/globalize.rb +115 -0
- data/lib/friendly_id/history.rb +134 -0
- data/lib/friendly_id/migration.rb +19 -0
- data/lib/friendly_id/object_utils.rb +50 -0
- data/lib/friendly_id/reserved.rb +68 -0
- data/lib/friendly_id/scoped.rb +149 -0
- data/lib/friendly_id/simple_i18n.rb +95 -0
- data/lib/friendly_id/slug.rb +14 -0
- data/lib/friendly_id/slug_generator.rb +80 -0
- data/lib/friendly_id/slugged.rb +329 -0
- data/lib/generators/friendly_id_generator.rb +17 -0
- data/mil_friendly_id.gemspec +45 -0
- data/test/base_test.rb +72 -0
- data/test/compatibility/ancestry/Gemfile +8 -0
- data/test/compatibility/ancestry/ancestry_test.rb +34 -0
- data/test/compatibility/threading/Gemfile +8 -0
- data/test/compatibility/threading/threading.rb +45 -0
- data/test/configuration_test.rb +48 -0
- data/test/core_test.rb +48 -0
- data/test/databases.yml +19 -0
- data/test/generator_test.rb +20 -0
- data/test/globalize_test.rb +57 -0
- data/test/helper.rb +87 -0
- data/test/history_test.rb +149 -0
- data/test/object_utils_test.rb +28 -0
- data/test/reserved_test.rb +40 -0
- data/test/schema.rb +79 -0
- data/test/scoped_test.rb +83 -0
- data/test/shared.rb +156 -0
- data/test/simple_i18n_test.rb +133 -0
- data/test/slugged_test.rb +280 -0
- data/test/sti_test.rb +77 -0
- metadata +262 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
# The configuration paramters passed to +friendly_id+ will be stored in
|
3
|
+
# this object.
|
4
|
+
class Configuration
|
5
|
+
|
6
|
+
# The base column or method used by FriendlyId as the basis of a friendly id
|
7
|
+
# or slug.
|
8
|
+
#
|
9
|
+
# For models that don't use FriendlyId::Slugged, the base is the column that
|
10
|
+
# is used as the FriendlyId directly. For models using FriendlyId::Slugged,
|
11
|
+
# the base is a column or method whose value is used as the basis of the
|
12
|
+
# slug.
|
13
|
+
#
|
14
|
+
# For example, if you have a model representing blog posts and that uses
|
15
|
+
# slugs, you likely will want to use the "title" attribute as the base, and
|
16
|
+
# FriendlyId will take care of transforming the human-readable title into
|
17
|
+
# something suitable for use in a URL.
|
18
|
+
#
|
19
|
+
# @param [Symbol] A symbol referencing a column or method in the model. This
|
20
|
+
# value is usually set by passing it as the first argument to
|
21
|
+
# {FriendlyId::Base#friendly_id friendly_id}:
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# class Book < ActiveRecord::Base
|
25
|
+
# extend FriendlyId
|
26
|
+
# friendly_id :name
|
27
|
+
# end
|
28
|
+
attr_accessor :base
|
29
|
+
|
30
|
+
# The default configuration options.
|
31
|
+
attr_reader :defaults
|
32
|
+
|
33
|
+
# The model class that this configuration belongs to.
|
34
|
+
# @return ActiveRecord::Base
|
35
|
+
attr_accessor :model_class
|
36
|
+
|
37
|
+
def initialize(model_class, values = nil)
|
38
|
+
@model_class = model_class
|
39
|
+
@defaults = {}
|
40
|
+
set values
|
41
|
+
end
|
42
|
+
|
43
|
+
# Lets you specify the modules to use with FriendlyId.
|
44
|
+
#
|
45
|
+
# This method is invoked by {FriendlyId::Base#friendly_id friendly_id} when
|
46
|
+
# passing the +:use+ option, or when using {FriendlyId::Base#friendly_id
|
47
|
+
# friendly_id} with a block.
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
# class Book < ActiveRecord::Base
|
51
|
+
# extend FriendlyId
|
52
|
+
# friendly_id :name, :use => :slugged
|
53
|
+
# end
|
54
|
+
# @param [#to_s,Module] *modules Arguments should be Modules, or symbols or
|
55
|
+
# strings that correspond with the name of a module inside the FriendlyId
|
56
|
+
# namespace. By default FriendlyId provides +:slugged+, +:history+,
|
57
|
+
# +:simple_i18n+, +:globalize+, and +:scoped+.
|
58
|
+
def use(*modules)
|
59
|
+
modules.to_a.flatten.compact.map do |object|
|
60
|
+
mod = object.kind_of?(Module) ? object : FriendlyId.const_get(object.to_s.classify)
|
61
|
+
model_class.send(:include, mod)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# The column that FriendlyId will use to find the record when querying by
|
66
|
+
# friendly id.
|
67
|
+
#
|
68
|
+
# This method is generally only used internally by FriendlyId.
|
69
|
+
# @return String
|
70
|
+
def query_field
|
71
|
+
base.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def set(values)
|
77
|
+
values and values.each {|name, value| self.send "#{name}=", value}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
# These methods will be added to the model's {FriendlyId::Base#relation_class relation_class}.
|
3
|
+
module FinderMethods
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
# FriendlyId overrides this method to make it possible to use friendly id's
|
8
|
+
# identically to numeric ids in finders.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# person = Person.find(123)
|
12
|
+
# person = Person.find("joe")
|
13
|
+
#
|
14
|
+
# @see FriendlyId::ObjectUtils
|
15
|
+
def find_one(id)
|
16
|
+
return super if id.unfriendly_id?
|
17
|
+
where(@klass.friendly_id_config.query_field => id).first or super
|
18
|
+
end
|
19
|
+
|
20
|
+
# FriendlyId overrides this method to make it possible to use friendly id's
|
21
|
+
# identically to numeric ids in finders.
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# person = Person.exists?(123)
|
25
|
+
# person = Person.exists?("joe")
|
26
|
+
# person = Person.exists?({:name => 'joe'})
|
27
|
+
# person = Person.exists?(['name = ?', 'joe'])
|
28
|
+
#
|
29
|
+
# @see FriendlyId::ObjectUtils
|
30
|
+
def exists?(id = false)
|
31
|
+
return super if id.unfriendly_id?
|
32
|
+
super @klass.friendly_id_config.query_field => id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
|
5
|
+
=begin
|
6
|
+
|
7
|
+
== Translating Slugs Using Globalize
|
8
|
+
|
9
|
+
The {FriendlyId::Globalize Globalize} module lets you use
|
10
|
+
Globalize[https://github.com/svenfuchs/globalize3] to translate slugs. This
|
11
|
+
module is most suitable for applications that need to be localized to many
|
12
|
+
languages. If your application only needs to be localized to one or two
|
13
|
+
languages, you may wish to consider the {FriendlyId::SimpleI18n SimpleI18n}
|
14
|
+
module.
|
15
|
+
|
16
|
+
In order to use this module, your model's table and translation table must both
|
17
|
+
have a slug column, and your model must set the +slug+ field as translatable
|
18
|
+
with Globalize:
|
19
|
+
|
20
|
+
class Post < ActiveRecord::Base
|
21
|
+
translates :title, :slug
|
22
|
+
extend FriendlyId
|
23
|
+
friendly_id :title, :use => :globalize
|
24
|
+
end
|
25
|
+
|
26
|
+
=== Finds
|
27
|
+
|
28
|
+
Finds will take the current locale into consideration:
|
29
|
+
|
30
|
+
I18n.locale = :it
|
31
|
+
Post.find("guerre-stellari")
|
32
|
+
I18n.locale = :en
|
33
|
+
Post.find("star-wars")
|
34
|
+
|
35
|
+
Additionally, finds will fall back to the default locale:
|
36
|
+
|
37
|
+
I18n.locale = :it
|
38
|
+
Post.find("star-wars")
|
39
|
+
|
40
|
+
To find a slug by an explicit locale, perform the find inside a block
|
41
|
+
passed to I18n's +with_locale+ method:
|
42
|
+
|
43
|
+
I18n.with_locale(:it) { Post.find("guerre-stellari") }
|
44
|
+
|
45
|
+
=== Creating Records
|
46
|
+
|
47
|
+
When new records are created, the slug is generated for the current locale only.
|
48
|
+
|
49
|
+
=== Translating Slugs
|
50
|
+
|
51
|
+
To translate an existing record's friendly_id, use
|
52
|
+
{FriendlyId::Globalize::Model#set_friendly_id}. This will ensure that the slug
|
53
|
+
you add is properly escaped, transliterated and sequenced:
|
54
|
+
|
55
|
+
post = Post.create :name => "Star Wars"
|
56
|
+
post.set_friendly_id("Guerre stellari", :it)
|
57
|
+
|
58
|
+
If you don't pass in a locale argument, FriendlyId::Globalize will just use the
|
59
|
+
current locale:
|
60
|
+
|
61
|
+
I18n.with_locale(:it) { post.set_friendly_id("Guerre stellari") }
|
62
|
+
|
63
|
+
=end
|
64
|
+
module Globalize
|
65
|
+
|
66
|
+
def self.included(model_class)
|
67
|
+
model_class.instance_eval do
|
68
|
+
friendly_id_config.use :slugged
|
69
|
+
relation_class.send :include, FinderMethods
|
70
|
+
include Model
|
71
|
+
# Check if slug field is enabled to be translated with Globalize
|
72
|
+
unless respond_to?('translated_attribute_names') || translated_attribute_names.exclude?(friendly_id_config.query_field.to_sym)
|
73
|
+
puts "\n[FriendlyId] You need to translate '#{friendly_id_config.query_field}' field with Globalize (add 'translates :#{friendly_id_config.query_field}' in your model '#{self.class.name}')\n\n"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Model
|
79
|
+
def set_friendly_id(text, locale)
|
80
|
+
I18n.with_locale(locale || I18n.locale) do
|
81
|
+
set_slug(normalize_friendly_id(text))
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module FinderMethods
|
87
|
+
# FriendlyId overrides this method to make it possible to use friendly id's
|
88
|
+
# identically to numeric ids in finders.
|
89
|
+
#
|
90
|
+
# @example
|
91
|
+
# person = Person.find(123)
|
92
|
+
# person = Person.find("joe")
|
93
|
+
#
|
94
|
+
# @see FriendlyId::ObjectUtils
|
95
|
+
def find_one(id)
|
96
|
+
return super if id.unfriendly_id?
|
97
|
+
found = where(@klass.friendly_id_config.query_field => id).first
|
98
|
+
found = includes(:translations).
|
99
|
+
where(translation_class.arel_table[:locale].in([I18n.locale, I18n.default_locale])).
|
100
|
+
where(translation_class.arel_table[@klass.friendly_id_config.query_field].eq(id)).first if found.nil?
|
101
|
+
|
102
|
+
if found
|
103
|
+
# Reload the translations for the found records.
|
104
|
+
found.tap { |f| f.translations.reload }
|
105
|
+
else
|
106
|
+
# if locale is not translated fallback to default locale
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
protected :find_one
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
== History: Avoiding 404's When Slugs Change
|
6
|
+
|
7
|
+
FriendlyId's {FriendlyId::History History} module adds the ability to store a
|
8
|
+
log of a model's slugs, so that when its friendly id changes, it's still
|
9
|
+
possible to perform finds by the old id.
|
10
|
+
|
11
|
+
The primary use case for this is avoiding broken URLs.
|
12
|
+
|
13
|
+
=== Setup
|
14
|
+
|
15
|
+
In order to use this module, you must add a table to your database schema to
|
16
|
+
store the slug records. FriendlyId provides a generator for this purpose:
|
17
|
+
|
18
|
+
rails generate friendly_id
|
19
|
+
rake db:migrate
|
20
|
+
|
21
|
+
This will add a table named +friendly_id_slugs+, used by the {FriendlyId::Slug}
|
22
|
+
model.
|
23
|
+
|
24
|
+
=== Considerations
|
25
|
+
|
26
|
+
This module is incompatible with the +:scoped+ module.
|
27
|
+
|
28
|
+
Because recording slug history requires creating additional database records,
|
29
|
+
this module has an impact on the performance of the associated model's +create+
|
30
|
+
method.
|
31
|
+
|
32
|
+
=== Example
|
33
|
+
|
34
|
+
class Post < ActiveRecord::Base
|
35
|
+
extend FriendlyId
|
36
|
+
friendly_id :title, :use => :history
|
37
|
+
end
|
38
|
+
|
39
|
+
class PostsController < ApplicationController
|
40
|
+
|
41
|
+
before_filter :find_post
|
42
|
+
|
43
|
+
...
|
44
|
+
|
45
|
+
def find_post
|
46
|
+
@post = Post.find params[:id]
|
47
|
+
|
48
|
+
# If an old id or a numeric id was used to find the record, then
|
49
|
+
# the request path will not match the post_path, and we should do
|
50
|
+
# a 301 redirect that uses the current friendly id.
|
51
|
+
if request.path != post_path(@post)
|
52
|
+
return redirect_to @post, :status => :moved_permanently
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
=end
|
57
|
+
module History
|
58
|
+
|
59
|
+
# Configures the model instance to use the History add-on.
|
60
|
+
def self.included(model_class)
|
61
|
+
model_class.instance_eval do
|
62
|
+
raise "FriendlyId::History is incompatible with FriendlyId::Scoped" if self < Scoped
|
63
|
+
@friendly_id_config.use :slugged
|
64
|
+
has_many :slugs, :as => :sluggable, :dependent => :destroy,
|
65
|
+
:class_name => Slug.to_s, :order => "#{Slug.quoted_table_name}.id DESC"
|
66
|
+
after_save :create_slug
|
67
|
+
relation_class.send :include, FinderMethods
|
68
|
+
friendly_id_config.slug_generator_class.send :include, SlugGenerator
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def create_slug
|
75
|
+
return unless friendly_id
|
76
|
+
return if slugs.first.try(:slug) == friendly_id
|
77
|
+
# Allow reversion back to a previously used slug
|
78
|
+
relation = slugs.where(:slug => friendly_id)
|
79
|
+
result = relation.select("id").lock(true).all
|
80
|
+
relation.delete_all unless result.empty?
|
81
|
+
slugs.create! do |record|
|
82
|
+
record.slug = friendly_id
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds a finder that explictly uses slugs from the slug table.
|
87
|
+
module FinderMethods
|
88
|
+
|
89
|
+
# Search for a record in the slugs table using the specified slug.
|
90
|
+
def find_one(id)
|
91
|
+
return super(id) if id.unfriendly_id?
|
92
|
+
where(@klass.friendly_id_config.query_field => id).first or
|
93
|
+
with_old_friendly_id(id) {|x| find_one_without_friendly_id(x)} or
|
94
|
+
find_one_without_friendly_id(id)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Search for a record in the slugs table using the specified slug.
|
98
|
+
def exists?(id = false)
|
99
|
+
return super if id.unfriendly_id?
|
100
|
+
exists_without_friendly_id?(@klass.friendly_id_config.query_field => id) or
|
101
|
+
with_old_friendly_id(id) {|x| exists_without_friendly_id?(x)} or
|
102
|
+
exists_without_friendly_id?(id)
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
# Accepts a slug, and yields a corresponding sluggable_id into the block.
|
108
|
+
def with_old_friendly_id(slug, &block)
|
109
|
+
sql = "SELECT sluggable_id FROM #{Slug.quoted_table_name} WHERE sluggable_type = %s AND slug = %s"
|
110
|
+
sql = sql % [@klass.base_class.to_s, slug].map {|x| connection.quote(x)}
|
111
|
+
sluggable_id = connection.select_values(sql).first
|
112
|
+
yield sluggable_id if sluggable_id
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# This module overrides {FriendlyId::SlugGenerator#conflicts} to consider
|
117
|
+
# all historic slugs for that model.
|
118
|
+
module SlugGenerator
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def conflicts
|
123
|
+
sluggable_class = friendly_id_config.model_class.base_class
|
124
|
+
pkey = sluggable_class.primary_key
|
125
|
+
value = sluggable.send pkey
|
126
|
+
|
127
|
+
scope = Slug.with_deleted.where("slug = ? OR slug LIKE ?", normalized, wildcard)
|
128
|
+
scope = scope.where(:sluggable_type => sluggable_class.to_s)
|
129
|
+
scope = scope.where("sluggable_id <> ?", value) unless sluggable.new_record?
|
130
|
+
scope.order("LENGTH(slug) DESC, slug DESC")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateSlugs < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
create_table :slugs do |t|
|
5
|
+
t.string :slug, :null => false
|
6
|
+
t.integer :sluggable_id, :null => false
|
7
|
+
t.string :sluggable_type, :limit => 40
|
8
|
+
t.datetime :created_at
|
9
|
+
t.deleted_at :boolean
|
10
|
+
end
|
11
|
+
add_index :slugs, :sluggable_id
|
12
|
+
add_index :slugs, [:slug, :sluggable_type], :unique => true
|
13
|
+
add_index :slugs, :sluggable_type
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :slugs
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
# Utility methods for determining whether any object is a friendly id.
|
3
|
+
#
|
4
|
+
# Monkey-patching Object is a somewhat extreme measure not to be taken lightly
|
5
|
+
# by libraries, but in this case I decided to do it because to me, it feels
|
6
|
+
# cleaner than adding a module method to {FriendlyId}. I've given the methods
|
7
|
+
# names that unambigously refer to the library of their origin, which should
|
8
|
+
# be sufficient to avoid conflicts with other libraries.
|
9
|
+
module ObjectUtils
|
10
|
+
|
11
|
+
# True is the id is definitely friendly, false if definitely unfriendly,
|
12
|
+
# else nil.
|
13
|
+
#
|
14
|
+
# An object is considired "definitely unfriendly" if its class is or
|
15
|
+
# inherits from ActiveRecord::Base, Array, Hash, NilClass, Numeric, or
|
16
|
+
# Symbol.
|
17
|
+
#
|
18
|
+
# An object is considered "definitely friendly" if it responds to +to_i+,
|
19
|
+
# and its value when cast to an integer and then back to a string is
|
20
|
+
# different from its value when merely cast to a string:
|
21
|
+
#
|
22
|
+
# 123.friendly_id? #=> false
|
23
|
+
# :id.friendly_id? #=> false
|
24
|
+
# {:name => 'joe'}.friendly_id? #=> false
|
25
|
+
# ['name = ?', 'joe'].friendly_id? #=> false
|
26
|
+
# nil.friendly_id? #=> false
|
27
|
+
# "123".friendly_id? #=> nil
|
28
|
+
# "abc123".friendly_id? #=> true
|
29
|
+
def friendly_id?
|
30
|
+
# Considered unfriendly if this is an instance of an unfriendly class or
|
31
|
+
# one of its descendants.
|
32
|
+
unfriendly_classes = [ActiveRecord::Base, Array, Hash, NilClass, Numeric,
|
33
|
+
Symbol, TrueClass, FalseClass]
|
34
|
+
|
35
|
+
if unfriendly_classes.detect {|klass| self.class <= klass}
|
36
|
+
false
|
37
|
+
elsif respond_to?(:to_i) && to_i.to_s != to_s
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# True if the id is definitely unfriendly, false if definitely friendly,
|
43
|
+
# else nil.
|
44
|
+
def unfriendly_id?
|
45
|
+
val = friendly_id? ; !val unless val.nil?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Object.send :include, FriendlyId::ObjectUtils
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
|
3
|
+
=begin
|
4
|
+
|
5
|
+
== Reserved Words
|
6
|
+
|
7
|
+
The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
|
8
|
+
words from use as FriendlyId slugs.
|
9
|
+
|
10
|
+
By default, FriendlyId reserves the words "new" and "edit" when this module is
|
11
|
+
included. You can configure this globally by using {FriendlyId.defaults
|
12
|
+
FriendlyId.defaults}:
|
13
|
+
|
14
|
+
FriendlyId.defaults do |config|
|
15
|
+
config.use :reserved
|
16
|
+
# Reserve words for English and Spanish URLs
|
17
|
+
config.reserved_words = %w(new edit nueva nuevo editar)
|
18
|
+
end
|
19
|
+
|
20
|
+
Note that the error message will appear on the field +:friendly_id+. If you are
|
21
|
+
using Rails's scaffolded form errors display, then it will have no field to
|
22
|
+
highlight. If you'd like to change this so that scaffolding works as expected,
|
23
|
+
one way to accomplish this is to move the error message to a different field.
|
24
|
+
For example:
|
25
|
+
|
26
|
+
class Person < ActiveRecord::Base
|
27
|
+
extend FriendlyId
|
28
|
+
friendly_id :name, use: :slugged
|
29
|
+
|
30
|
+
after_validation :move_friendly_id_error_to_name
|
31
|
+
|
32
|
+
def move_friendly_id_error_to_name
|
33
|
+
errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
=end
|
38
|
+
module Reserved
|
39
|
+
|
40
|
+
# When included, this module adds configuration options to the model class's
|
41
|
+
# friendly_id_config.
|
42
|
+
def self.included(model_class)
|
43
|
+
model_class.class_eval do
|
44
|
+
friendly_id_config.class.send :include, Reserved::Configuration
|
45
|
+
friendly_id_config.defaults[:reserved_words] ||= ["new", "edit"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# This module adds the +:reserved_words+ configuration option to
|
50
|
+
# {FriendlyId::Configuration FriendlyId::Configuration}.
|
51
|
+
module Configuration
|
52
|
+
attr_writer :reserved_words
|
53
|
+
|
54
|
+
# Overrides {FriendlyId::Configuration#base} to add a validation to the
|
55
|
+
# model class.
|
56
|
+
def base=(base)
|
57
|
+
super
|
58
|
+
reserved_words = model_class.friendly_id_config.reserved_words
|
59
|
+
model_class.validates_exclusion_of :friendly_id, :in => reserved_words
|
60
|
+
end
|
61
|
+
|
62
|
+
# An array of words forbidden as slugs.
|
63
|
+
def reserved_words
|
64
|
+
@reserved_words ||= @defaults[:reserved_words]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|