geothird_friendly_id 4.0.9.1
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 +43 -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 +18 -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/friendly_id.rb +114 -0
- data/lib/generators/friendly_id_generator.rb +17 -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/Gemfile.lock +37 -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 +247 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 878d3503934309a5f33b75fbfcbcd0f39f2c7308
|
4
|
+
data.tar.gz: f76a3f30b63f8531225b6f96afa24b5cded31d07
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 40e592b60bae27aea119208769c343bb191bab7cbc91fdbca5894f9dcdc7ec02ee6d6510a61334e749fed8d1272fd8a0561b3d459d6610780f03ec700f254f84
|
7
|
+
data.tar.gz: baafd016378fc23b5cef06267ad77475d87ca9720dd01268b51ba9b7e7e8d1fd0e7e225161498b4bb1e9fc5fdee7f97c396c6b74fabf71f297a450215a213841
|
data/.gemtest
ADDED
File without changes
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
rvm:
|
2
|
+
- 1.9.3
|
3
|
+
- 1.8.7
|
4
|
+
# - jruby
|
5
|
+
# - rbx
|
6
|
+
|
7
|
+
branches:
|
8
|
+
only:
|
9
|
+
- master
|
10
|
+
env:
|
11
|
+
- DB=postgres
|
12
|
+
- DB=mysql
|
13
|
+
- DB=sqlite3
|
14
|
+
|
15
|
+
gemfile:
|
16
|
+
- gemfiles/Gemfile.rails-3.0.rb
|
17
|
+
- gemfiles/Gemfile.rails-3.1.rb
|
18
|
+
- gemfiles/Gemfile.rails-3.2.rb
|
19
|
+
before_script: 'bundle exec rake db:create db:up > /dev/null'
|
20
|
+
script: 'bundle exec rake test'
|
data/.yardopts
ADDED
data/Changelog.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# FriendlyId Changelog
|
2
|
+
|
3
|
+
We would like to think our many {file:Contributors contributors} for
|
4
|
+
suggestions, ideas and improvements to FriendlyId.
|
5
|
+
|
6
|
+
* Table of Contents
|
7
|
+
{:toc}
|
8
|
+
|
9
|
+
## 4.0.9 (2012-10-31)
|
10
|
+
|
11
|
+
* Fixed support for Rails 3.2.9.rc1
|
12
|
+
|
13
|
+
## 4.0.8 (2012-08-01)
|
14
|
+
|
15
|
+
* Name internal anonymous class to fix marshall dump/load error (Jess Brown, Philip Arndt and Norman Clarke).
|
16
|
+
|
17
|
+
* Avoid using deprecated `update_attribute` (Philip Arndt).
|
18
|
+
|
19
|
+
* Added set_friendly_id method to Globalize module (Norman Clarke).
|
20
|
+
|
21
|
+
* autoload FriendlyId::Slug; previously this class was not accessible from
|
22
|
+
migrations unless required explicitly, which could cause some queries to
|
23
|
+
unexpectedly fail (Norman Clarke).
|
24
|
+
|
25
|
+
* Fix Mocha load order (Mark Turner).
|
26
|
+
|
27
|
+
* Minor doc updates (Rob Yurkowski).
|
28
|
+
|
29
|
+
* Other miscellaneous refactorings and doc updates.
|
30
|
+
|
31
|
+
## 4.0.7 (2012-06-06)
|
32
|
+
|
33
|
+
* to_param just calls super when no friendly_id is present, to keep the model's
|
34
|
+
default behavior. (Andrew White)
|
35
|
+
|
36
|
+
* FriendlyId can now properly sequence slugs that end in numbers even when a
|
37
|
+
single dash is used as the separator (Tomás Arribas).
|
38
|
+
|
39
|
+
## 4.0.6 (2012-05-21)
|
40
|
+
|
41
|
+
* Fix nil return value from to_param when save fails because of validation errors (Tomás Arribas)
|
42
|
+
* Fix incorrect usage of i18n API (Vinicius Ferriani)
|
43
|
+
* Improve error handling in reserved module (Adrián Mugnolo and Github user "nolamesa")
|
44
|
+
|
45
|
+
## 4.0.5 (2012-04-28)
|
46
|
+
|
47
|
+
* Favor `includes` over `joins` in globalize to avoid read-only results (Jakub Wojtysiak)
|
48
|
+
* Fix globalize compatibility with results from dynamic finders (Chris Salzberg)
|
49
|
+
|
50
|
+
|
51
|
+
## 4.0.4 (2012-03-26)
|
52
|
+
|
53
|
+
* Fix globalize plugin to avoid issues with asset precompilation (Philip Arndt)
|
54
|
+
|
55
|
+
|
56
|
+
## 4.0.3 (2012-03-14)
|
57
|
+
|
58
|
+
* Fix escape for '%' and '_' on SQLite (Norman Clarke and Sergey Petrunin)
|
59
|
+
* Allow FriendlyId to be extended or included (Norman Clarke)
|
60
|
+
* Allow Configuration#use to accept a Module (Norman Clarke)
|
61
|
+
* Fix bugs with History module + STI (Norman Clarke and Sergey Petrunin)
|
62
|
+
|
63
|
+
## 4.0.2 (2012-03-12)
|
64
|
+
|
65
|
+
* Improved conflict handling and performance in History module (Erik Ogan and Thomas Shafer)
|
66
|
+
* Fixed bug that impeded using underscores as a sequence separator (Erik Ogan and Thomas Shafer)
|
67
|
+
* Minor documentation improvements (Norman Clarke)
|
68
|
+
|
69
|
+
## 4.0.1 (2012-02-29)
|
70
|
+
|
71
|
+
* Added support for Globalize 3 (Enrico Pilotto and Philip Arndt)
|
72
|
+
* Allow the scoped module to use multiple scopes (Ben Caldwell)
|
73
|
+
* Fixes for conflicting slugs in history module (Erik Ogan, Thomas Shafer, Evan Arnold)
|
74
|
+
* Fix for conflicting slugs when using STI (Danny van der Heiden, Diederick Lawson)
|
75
|
+
* Maintainence improvements (Norman Clarke, Philip Arndt, Thomas Darde, Lee Hambley)
|
76
|
+
|
77
|
+
## 4.0.0 (2011-12-27)
|
78
|
+
|
79
|
+
This is a complete rewrite of FriendlyId, and introduces a smaller, faster and
|
80
|
+
less ambitious codebase. The primary change is the relegation of external slugs
|
81
|
+
to an optional addon, and the adoption of what were formerly "cached slugs"
|
82
|
+
as the primary way of handling slugging.
|
83
|
+
|
84
|
+
## Older releases
|
85
|
+
|
86
|
+
Please see the 3.x branch.
|
data/Gemfile
ADDED
data/Guide.rdoc
ADDED
@@ -0,0 +1,553 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
== About FriendlyId
|
5
|
+
|
6
|
+
FriendlyId is an add-on to Ruby's Active Record that allows you to replace ids
|
7
|
+
in your URLs with strings:
|
8
|
+
|
9
|
+
# without FriendlyId
|
10
|
+
http://example.com/states/4323454
|
11
|
+
|
12
|
+
# with FriendlyId
|
13
|
+
http://example.com/states/washington
|
14
|
+
|
15
|
+
It requires few changes to your application code and offers flexibility,
|
16
|
+
performance and a well-documented codebase.
|
17
|
+
|
18
|
+
=== Core Concepts
|
19
|
+
|
20
|
+
==== Slugs
|
21
|
+
|
22
|
+
The concept of "slugs[http://en.wikipedia.org/wiki/Slug_(web_publishing)]" is at
|
23
|
+
the heart of FriendlyId.
|
24
|
+
|
25
|
+
A slug is the part of a URL which identifies a page using human-readable
|
26
|
+
keywords, rather than an opaque identifier such as a numeric id. This can make
|
27
|
+
your application more friendly both for users and search engine.
|
28
|
+
|
29
|
+
==== Finders: Slugs Act Like Numeric IDs
|
30
|
+
|
31
|
+
To the extent possible, FriendlyId lets you treat text-based identifiers like
|
32
|
+
normal IDs. This means that you can perform finds with slugs just like you do
|
33
|
+
with numeric ids:
|
34
|
+
|
35
|
+
Person.find(82542335)
|
36
|
+
Person.find("joe")
|
37
|
+
|
38
|
+
|
39
|
+
== Setting Up FriendlyId in Your Model
|
40
|
+
|
41
|
+
To use FriendlyId in your ActiveRecord models, you must first either extend or
|
42
|
+
include the FriendlyId module (it makes no difference), then invoke the
|
43
|
+
{FriendlyId::Base#friendly_id friendly_id} method to configure your desired
|
44
|
+
options:
|
45
|
+
|
46
|
+
class Foo < ActiveRecord::Base
|
47
|
+
include FriendlyId
|
48
|
+
friendly_id :bar, :use => [:slugged, :simple_i18n]
|
49
|
+
end
|
50
|
+
|
51
|
+
The most important option is `:use`, which you use to tell FriendlyId which
|
52
|
+
addons it should use. See the documentation for this method for a list of all
|
53
|
+
available addons, or skim through the rest of the docs to get a high-level
|
54
|
+
overview.
|
55
|
+
|
56
|
+
=== The Default Setup: Simple Models
|
57
|
+
|
58
|
+
The simplest way to use FriendlyId is with a model that has a uniquely indexed
|
59
|
+
column with no spaces or special characters, and that is seldom or never
|
60
|
+
updated. The most common example of this is a user name:
|
61
|
+
|
62
|
+
class User < ActiveRecord::Base
|
63
|
+
extend FriendlyId
|
64
|
+
friendly_id :login
|
65
|
+
validates_format_of :login, :with => /\A[a-z0-9]+\z/i
|
66
|
+
end
|
67
|
+
|
68
|
+
@user = User.find "joe" # the old User.find(1) still works, too
|
69
|
+
@user.to_param # returns "joe"
|
70
|
+
redirect_to @user # the URL will be /users/joe
|
71
|
+
|
72
|
+
In this case, FriendlyId assumes you want to use the column as-is; it will never
|
73
|
+
modify the value of the column, and your application should ensure that the
|
74
|
+
value is unique and admissible in a URL:
|
75
|
+
|
76
|
+
class City < ActiveRecord::Base
|
77
|
+
extend FriendlyId
|
78
|
+
friendly_id :name
|
79
|
+
end
|
80
|
+
|
81
|
+
@city.find "Viña del Mar"
|
82
|
+
redirect_to @city # the URL will be /cities/Viña%20del%20Mar
|
83
|
+
|
84
|
+
Writing the code to process an arbitrary string into a good identifier for use
|
85
|
+
in a URL can be repetitive and surprisingly tricky, so for this reason it's
|
86
|
+
often better and easier to use {FriendlyId::Slugged slugs}.
|
87
|
+
|
88
|
+
|
89
|
+
== Slugged Models
|
90
|
+
|
91
|
+
FriendlyId can use a separate column to store slugs for models which require
|
92
|
+
some text processing.
|
93
|
+
|
94
|
+
For example, blog applications typically use a post title to provide the basis
|
95
|
+
of a search engine friendly URL. Such identifiers typically lack uppercase
|
96
|
+
characters, use ASCII to approximate UTF-8 character, and strip out other
|
97
|
+
characters which may make them aesthetically unappealing or error-prone when
|
98
|
+
used in a URL.
|
99
|
+
|
100
|
+
class Post < ActiveRecord::Base
|
101
|
+
extend FriendlyId
|
102
|
+
friendly_id :title, :use => :slugged
|
103
|
+
end
|
104
|
+
|
105
|
+
@post = Post.create(:title => "This is the first post!")
|
106
|
+
@post.friendly_id # returns "this-is-the-first-post"
|
107
|
+
redirect_to @post # the URL will be /posts/this-is-the-first-post
|
108
|
+
|
109
|
+
In general, use slugs by default unless you know for sure you don't need them.
|
110
|
+
To activate the slugging functionality, use the {FriendlyId::Slugged} module.
|
111
|
+
|
112
|
+
FriendlyId will generate slugs from a method or column that you specify, and
|
113
|
+
store them in a field in your model. By default, this field must be named
|
114
|
+
+:slug+, though you may change this using the
|
115
|
+
{FriendlyId::Slugged::Configuration#slug_column slug_column} configuration
|
116
|
+
option. You should add an index to this column, and in most cases, make it
|
117
|
+
unique. Do not make the column unique in case you wish to scope the slug
|
118
|
+
(more on this later). You may also wish to constrain it to NOT NULL, but this
|
119
|
+
depends on your app's behavior and requirements.
|
120
|
+
|
121
|
+
=== Example Setup
|
122
|
+
|
123
|
+
# your model
|
124
|
+
class Post < ActiveRecord::Base
|
125
|
+
extend FriendlyId
|
126
|
+
friendly_id :title, :use => :slugged
|
127
|
+
validates_presence_of :title, :slug, :body
|
128
|
+
end
|
129
|
+
|
130
|
+
# a migration
|
131
|
+
class CreatePosts < ActiveRecord::Migration
|
132
|
+
def self.up
|
133
|
+
create_table :posts do |t|
|
134
|
+
t.string :title, :null => false
|
135
|
+
t.string :slug, :null => false
|
136
|
+
t.text :body
|
137
|
+
end
|
138
|
+
|
139
|
+
add_index :posts, :slug, :unique => true
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.down
|
143
|
+
drop_table :posts
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
=== Working With Slugs
|
148
|
+
|
149
|
+
==== Formatting
|
150
|
+
|
151
|
+
By default, FriendlyId uses Active Support's
|
152
|
+
paramaterize[http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize]
|
153
|
+
method to create slugs. This method will intelligently replace spaces with
|
154
|
+
dashes, and Unicode Latin characters with ASCII approximations:
|
155
|
+
|
156
|
+
movie = Movie.create! :title => "Der Preis fürs Überleben"
|
157
|
+
movie.slug #=> "der-preis-furs-uberleben"
|
158
|
+
|
159
|
+
==== Uniqueness
|
160
|
+
|
161
|
+
When you try to insert a record that would generate a duplicate friendly id,
|
162
|
+
FriendlyId will append a sequence to the generated slug to ensure uniqueness:
|
163
|
+
|
164
|
+
car = Car.create :title => "Peugot 206"
|
165
|
+
car2 = Car.create :title => "Peugot 206"
|
166
|
+
|
167
|
+
car.friendly_id #=> "peugot-206"
|
168
|
+
car2.friendly_id #=> "peugot-206--2"
|
169
|
+
|
170
|
+
==== Sequence Separator - The Two Dashes
|
171
|
+
|
172
|
+
By default, FriendlyId uses two dashes to separate the slug from a sequence.
|
173
|
+
|
174
|
+
You can change this with the {FriendlyId::Slugged::Configuration#sequence_separator
|
175
|
+
sequence_separator} configuration option.
|
176
|
+
|
177
|
+
==== Column or Method?
|
178
|
+
|
179
|
+
FriendlyId always uses a method as the basis of the slug text - not a column. It
|
180
|
+
first glance, this may sound confusing, but remember that Active Record provides
|
181
|
+
methods for each column in a model's associated table, and that's what
|
182
|
+
FriendlyId uses.
|
183
|
+
|
184
|
+
Here's an example of a class that uses a custom method to generate the slug:
|
185
|
+
|
186
|
+
class Person < ActiveRecord::Base
|
187
|
+
friendly_id :name_and_location
|
188
|
+
def name_and_location
|
189
|
+
"#{name} from #{location}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
bob = Person.create! :name => "Bob Smith", :location => "New York City"
|
194
|
+
bob.friendly_id #=> "bob-smith-from-new-york-city"
|
195
|
+
|
196
|
+
==== Providing Your Own Slug Processing Method
|
197
|
+
|
198
|
+
You can override {FriendlyId::Slugged#normalize_friendly_id} in your model for
|
199
|
+
total control over the slug format.
|
200
|
+
|
201
|
+
==== Deciding When to Generate New Slugs
|
202
|
+
|
203
|
+
Overriding {FriendlyId::Slugged#should_generate_new_friendly_id?} lets you
|
204
|
+
control whether new friendly ids are created when a model is updated. For
|
205
|
+
example, if you only want to generate slugs once and then treat them as
|
206
|
+
read-only:
|
207
|
+
|
208
|
+
class Post < ActiveRecord::Base
|
209
|
+
extend FriendlyId
|
210
|
+
friendly_id :title, :use => :slugged
|
211
|
+
|
212
|
+
def should_generate_new_friendly_id?
|
213
|
+
new_record?
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
post = Post.create!(:title => "Hello world!")
|
218
|
+
post.slug #=> "hello-world"
|
219
|
+
post.title = "Hello there, world!"
|
220
|
+
post.save!
|
221
|
+
post.slug #=> "hello-world"
|
222
|
+
|
223
|
+
==== Locale-specific Transliterations
|
224
|
+
|
225
|
+
Active Support's +parameterize+ uses
|
226
|
+
transliterate[http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate],
|
227
|
+
which in turn can use I18n's transliteration rules to consider the current
|
228
|
+
locale when replacing Latin characters:
|
229
|
+
|
230
|
+
# config/locales/de.yml
|
231
|
+
de:
|
232
|
+
i18n:
|
233
|
+
transliterate:
|
234
|
+
rule:
|
235
|
+
ü: "ue"
|
236
|
+
ö: "oe"
|
237
|
+
etc...
|
238
|
+
|
239
|
+
movie = Movie.create! :title => "Der Preis fürs Überleben"
|
240
|
+
movie.slug #=> "der-preis-fuers-ueberleben"
|
241
|
+
|
242
|
+
This functionality was in fact taken from earlier versions of FriendlyId.
|
243
|
+
|
244
|
+
==== Gotchas: Common Problems
|
245
|
+
|
246
|
+
===== Slugs That Begin With Numbers
|
247
|
+
|
248
|
+
Ruby's `to_i` function casts strings to integers in such a way that +23abc.to_i+
|
249
|
+
returns 23. Because FriendlyId falls back to finding by numeric id, this means
|
250
|
+
that if you attempt to find a record with a non-existant slug, and that slug
|
251
|
+
begins with a number, your find will probably return the wrong record.
|
252
|
+
|
253
|
+
There are two fairly simple ways to avoid this:
|
254
|
+
|
255
|
+
* Use validations to ensure that slugs don't begin with numbers.
|
256
|
+
* Use explicit finders like +find_by_id+ to always find by the numeric id, or
|
257
|
+
+find_by_slug+ to always find using the friendly id.
|
258
|
+
|
259
|
+
===== Concurrency Issues
|
260
|
+
|
261
|
+
FriendlyId uses a before_validation callback to generate and set the slug. This
|
262
|
+
means that if you create two model instances before saving them, it's possible
|
263
|
+
they will generate the same slug, and the second save will fail.
|
264
|
+
|
265
|
+
This can happen in two fairly normal cases: the first, when a model using nested
|
266
|
+
attributes creates more than one record for a model that uses friendly_id. The
|
267
|
+
second, in concurrent code, either in threads or multiple processes.
|
268
|
+
|
269
|
+
To solve the nested attributes issue, I recommend simply avoiding them when
|
270
|
+
creating more than one nested record for a model that uses FriendlyId. See {this
|
271
|
+
Github issue}[https://github.com/norman/friendly_id/issues/185] for discussion.
|
272
|
+
|
273
|
+
To solve the concurrency issue, I recommend locking the model's table against
|
274
|
+
inserts while when saving the record. See {this Github
|
275
|
+
issue}[https://github.com/norman/friendly_id/issues/180] for discussion.
|
276
|
+
|
277
|
+
|
278
|
+
== History: Avoiding 404's When Slugs Change
|
279
|
+
|
280
|
+
FriendlyId's {FriendlyId::History History} module adds the ability to store a
|
281
|
+
log of a model's slugs, so that when its friendly id changes, it's still
|
282
|
+
possible to perform finds by the old id.
|
283
|
+
|
284
|
+
The primary use case for this is avoiding broken URLs.
|
285
|
+
|
286
|
+
=== Setup
|
287
|
+
|
288
|
+
In order to use this module, you must add a table to your database schema to
|
289
|
+
store the slug records. FriendlyId provides a generator for this purpose:
|
290
|
+
|
291
|
+
rails generate friendly_id
|
292
|
+
rake db:migrate
|
293
|
+
|
294
|
+
This will add a table named +friendly_id_slugs+, used by the {FriendlyId::Slug}
|
295
|
+
model.
|
296
|
+
|
297
|
+
=== Considerations
|
298
|
+
|
299
|
+
This module is incompatible with the +:scoped+ module.
|
300
|
+
|
301
|
+
Because recording slug history requires creating additional database records,
|
302
|
+
this module has an impact on the performance of the associated model's +create+
|
303
|
+
method.
|
304
|
+
|
305
|
+
=== Example
|
306
|
+
|
307
|
+
class Post < ActiveRecord::Base
|
308
|
+
extend FriendlyId
|
309
|
+
friendly_id :title, :use => :history
|
310
|
+
end
|
311
|
+
|
312
|
+
class PostsController < ApplicationController
|
313
|
+
|
314
|
+
before_filter :find_post
|
315
|
+
|
316
|
+
...
|
317
|
+
|
318
|
+
def find_post
|
319
|
+
@post = Post.find params[:id]
|
320
|
+
|
321
|
+
# If an old id or a numeric id was used to find the record, then
|
322
|
+
# the request path will not match the post_path, and we should do
|
323
|
+
# a 301 redirect that uses the current friendly id.
|
324
|
+
if request.path != post_path(@post)
|
325
|
+
return redirect_to @post, :status => :moved_permanently
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
== Unique Slugs by Scope
|
332
|
+
|
333
|
+
The {FriendlyId::Scoped} module allows FriendlyId to generate unique slugs
|
334
|
+
within a scope.
|
335
|
+
|
336
|
+
This allows, for example, two restaurants in different cities to have the slug
|
337
|
+
+joes-diner+:
|
338
|
+
|
339
|
+
class Restaurant < ActiveRecord::Base
|
340
|
+
extend FriendlyId
|
341
|
+
belongs_to :city
|
342
|
+
friendly_id :name, :use => :scoped, :scope => :city
|
343
|
+
end
|
344
|
+
|
345
|
+
class City < ActiveRecord::Base
|
346
|
+
extend FriendlyId
|
347
|
+
has_many :restaurants
|
348
|
+
friendly_id :name, :use => :slugged
|
349
|
+
end
|
350
|
+
|
351
|
+
City.find("seattle").restaurants.find("joes-diner")
|
352
|
+
City.find("chicago").restaurants.find("joes-diner")
|
353
|
+
|
354
|
+
Without :scoped in this case, one of the restaurants would have the slug
|
355
|
+
+joes-diner+ and the other would have +joes-diner--2+.
|
356
|
+
|
357
|
+
The value for the +:scope+ option can be the name of a +belongs_to+ relation, or
|
358
|
+
a column.
|
359
|
+
|
360
|
+
Please do note that you must drop the uniqueness constraint on the slug's
|
361
|
+
column in the database when you're scoping the slug.
|
362
|
+
|
363
|
+
=== Finding Records by Friendly ID
|
364
|
+
|
365
|
+
If you are using scopes your friendly ids may not be unique, so a simple find
|
366
|
+
like
|
367
|
+
|
368
|
+
Restaurant.find("joes-diner")
|
369
|
+
|
370
|
+
may return the wrong record. In these cases it's best to query through the
|
371
|
+
relation:
|
372
|
+
|
373
|
+
@city.restaurants.find("joes-diner")
|
374
|
+
|
375
|
+
Alternatively, you could pass the scope value as a query parameter:
|
376
|
+
|
377
|
+
Restaurant.find("joes-diner").where(:city_id => @city.id)
|
378
|
+
|
379
|
+
|
380
|
+
=== Finding All Records That Match a Scoped ID
|
381
|
+
|
382
|
+
Query the slug column directly:
|
383
|
+
|
384
|
+
Restaurant.find_all_by_slug("joes-diner")
|
385
|
+
|
386
|
+
=== Routes for Scoped Models
|
387
|
+
|
388
|
+
Recall that FriendlyId is a database-centric library, and does not set up any
|
389
|
+
routes for scoped models. You must do this yourself in your application. Here's
|
390
|
+
an example of one way to set this up:
|
391
|
+
|
392
|
+
# in routes.rb
|
393
|
+
resources :cities do
|
394
|
+
resources :restaurants
|
395
|
+
end
|
396
|
+
|
397
|
+
# in views
|
398
|
+
<%= link_to 'Show', [@city, @restaurant] %>
|
399
|
+
|
400
|
+
# in controllers
|
401
|
+
@city = City.find(params[:city_id])
|
402
|
+
@restaurant = @city.restaurants.find(params[:id])
|
403
|
+
|
404
|
+
# URLs:
|
405
|
+
http://example.org/cities/seattle/restaurants/joes-diner
|
406
|
+
http://example.org/cities/chicago/restaurants/joes-diner
|
407
|
+
|
408
|
+
|
409
|
+
== Translating Slugs Using Simple I18n
|
410
|
+
|
411
|
+
The {FriendlyId::SimpleI18n SimpleI18n} module adds very basic i18n support to
|
412
|
+
FriendlyId.
|
413
|
+
|
414
|
+
In order to use this module, your model must have a slug column for each locale.
|
415
|
+
By default FriendlyId looks for columns named, for example, "slug_en",
|
416
|
+
"slug_es", etc. The first part of the name can be configured by passing the
|
417
|
+
+:slug_column+ option if you choose. Note that the column for the default locale
|
418
|
+
must also include the locale in its name.
|
419
|
+
|
420
|
+
This module is most suitable to applications that need to support few locales.
|
421
|
+
If you need to support two or more locales, you may wish to use the
|
422
|
+
{FriendlyId::Globalize Globalize} module instead.
|
423
|
+
|
424
|
+
=== Example migration
|
425
|
+
|
426
|
+
def self.up
|
427
|
+
create_table :posts do |t|
|
428
|
+
t.string :title
|
429
|
+
t.string :slug_en
|
430
|
+
t.string :slug_es
|
431
|
+
t.text :body
|
432
|
+
end
|
433
|
+
add_index :posts, :slug_en
|
434
|
+
add_index :posts, :slug_es
|
435
|
+
end
|
436
|
+
|
437
|
+
=== Finds
|
438
|
+
|
439
|
+
Finds will take into consideration the current locale:
|
440
|
+
|
441
|
+
I18n.locale = :es
|
442
|
+
Post.find("la-guerra-de-las-galaxas")
|
443
|
+
I18n.locale = :en
|
444
|
+
Post.find("star-wars")
|
445
|
+
|
446
|
+
To find a slug by an explicit locale, perform the find inside a block
|
447
|
+
passed to I18n's +with_locale+ method:
|
448
|
+
|
449
|
+
I18n.with_locale(:es) do
|
450
|
+
Post.find("la-guerra-de-las-galaxas")
|
451
|
+
end
|
452
|
+
|
453
|
+
=== Creating Records
|
454
|
+
|
455
|
+
When new records are created, the slug is generated for the current locale only.
|
456
|
+
|
457
|
+
=== Translating Slugs
|
458
|
+
|
459
|
+
To translate an existing record's friendly_id, use
|
460
|
+
{FriendlyId::SimpleI18n::Model#set_friendly_id}. This will ensure that the slug
|
461
|
+
you add is properly escaped, transliterated and sequenced:
|
462
|
+
|
463
|
+
post = Post.create :name => "Star Wars"
|
464
|
+
post.set_friendly_id("La guerra de las galaxas", :es)
|
465
|
+
|
466
|
+
If you don't pass in a locale argument, FriendlyId::SimpleI18n will just use the
|
467
|
+
current locale:
|
468
|
+
|
469
|
+
I18n.with_locale(:es) do
|
470
|
+
post.set_friendly_id("la-guerra-de-las-galaxas")
|
471
|
+
end
|
472
|
+
|
473
|
+
|
474
|
+
== Translating Slugs Using Globalize
|
475
|
+
|
476
|
+
The {FriendlyId::Globalize Globalize} module lets you use
|
477
|
+
Globalize[https://github.com/svenfuchs/globalize3] to translate slugs. This
|
478
|
+
module is most suitable for applications that need to be localized to many
|
479
|
+
languages. If your application only needs to be localized to one or two
|
480
|
+
languages, you may wish to consider the {FriendlyId::SimpleI18n SimpleI18n}
|
481
|
+
module.
|
482
|
+
|
483
|
+
In order to use this module, your model's table and translation table must both
|
484
|
+
have a slug column, and your model must set the +slug+ field as translatable
|
485
|
+
with Globalize:
|
486
|
+
|
487
|
+
class Post < ActiveRecord::Base
|
488
|
+
translates :title, :slug
|
489
|
+
extend FriendlyId
|
490
|
+
friendly_id :title, :use => :globalize
|
491
|
+
end
|
492
|
+
|
493
|
+
=== Finds
|
494
|
+
|
495
|
+
Finds will take the current locale into consideration:
|
496
|
+
|
497
|
+
I18n.locale = :it
|
498
|
+
Post.find("guerre-stellari")
|
499
|
+
I18n.locale = :en
|
500
|
+
Post.find("star-wars")
|
501
|
+
|
502
|
+
To find a slug by an explicit locale, perform the find inside a block
|
503
|
+
passed to I18n's +with_locale+ method:
|
504
|
+
|
505
|
+
I18n.with_locale(:it) do
|
506
|
+
Post.find("guerre-stellari")
|
507
|
+
end
|
508
|
+
|
509
|
+
=== Creating Records
|
510
|
+
|
511
|
+
When new records are created, the slug is generated for the current locale only.
|
512
|
+
|
513
|
+
=== Translating Slugs
|
514
|
+
|
515
|
+
To translate an existing record's friendly_id, simply change the locale and
|
516
|
+
assign a value to the +slug+ field:
|
517
|
+
|
518
|
+
I18n.with_locale(:it) do
|
519
|
+
post.slug = "guerre-stellari"
|
520
|
+
end
|
521
|
+
|
522
|
+
|
523
|
+
== Reserved Words
|
524
|
+
|
525
|
+
The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
|
526
|
+
words from use as FriendlyId slugs.
|
527
|
+
|
528
|
+
By default, FriendlyId reserves the words "new" and "edit" when this module is
|
529
|
+
included. You can configure this globally by using {FriendlyId.defaults
|
530
|
+
FriendlyId.defaults}:
|
531
|
+
|
532
|
+
FriendlyId.defaults do |config|
|
533
|
+
config.use :reserved
|
534
|
+
# Reserve words for English and Spanish URLs
|
535
|
+
config.reserved_words = %w(new edit nueva nuevo editar)
|
536
|
+
end
|
537
|
+
|
538
|
+
Note that the error message will appear on the field +:friendly_id+. If you are
|
539
|
+
using Rails's scaffolded form errors display, then it will have no field to
|
540
|
+
highlight. If you'd like to change this so that scaffolding works as expected,
|
541
|
+
one way to accomplish this is to move the error message to a different field.
|
542
|
+
For example:
|
543
|
+
|
544
|
+
class Person < ActiveRecord::Base
|
545
|
+
extend FriendlyId
|
546
|
+
friendly_id :name, use: :slugged
|
547
|
+
|
548
|
+
after_validation :move_friendly_id_error_to_name
|
549
|
+
|
550
|
+
def move_friendly_id_error_to_name
|
551
|
+
errors.messages[:name] = errors.messages.delete(:friendly_id)
|
552
|
+
end
|
553
|
+
end
|