friendly_id 3.3.3.0 → 4.0.0.beta7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +24 -0
  3. data/.yardopts +4 -0
  4. data/Changelog.md +9 -10
  5. data/README.md +39 -48
  6. data/Rakefile +56 -58
  7. data/WhatsNew.md +95 -0
  8. data/bench.rb +63 -0
  9. data/friendly_id.gemspec +40 -0
  10. data/gemfiles/Gemfile.rails-3.0.rb +18 -0
  11. data/gemfiles/Gemfile.rails-3.0.rb.lock +52 -0
  12. data/gemfiles/Gemfile.rails-3.1.rb +18 -0
  13. data/gemfiles/Gemfile.rails-3.1.rb.lock +57 -0
  14. data/lib/friendly_id.rb +126 -80
  15. data/lib/friendly_id/active_record_adapter/relation.rb +10 -2
  16. data/lib/friendly_id/active_record_adapter/slugged_model.rb +3 -9
  17. data/lib/friendly_id/base.rb +132 -0
  18. data/lib/friendly_id/configuration.rb +65 -152
  19. data/lib/friendly_id/finder_methods.rb +20 -0
  20. data/lib/friendly_id/history.rb +88 -0
  21. data/lib/friendly_id/migration.rb +18 -0
  22. data/lib/friendly_id/model.rb +22 -0
  23. data/lib/friendly_id/object_utils.rb +40 -0
  24. data/lib/friendly_id/reserved.rb +46 -0
  25. data/lib/friendly_id/scoped.rb +131 -0
  26. data/lib/friendly_id/slug.rb +9 -0
  27. data/lib/friendly_id/slug_sequencer.rb +82 -0
  28. data/lib/friendly_id/slugged.rb +191 -76
  29. data/lib/friendly_id/version.rb +2 -2
  30. data/test/base_test.rb +54 -0
  31. data/test/configuration_test.rb +27 -0
  32. data/test/core_test.rb +30 -0
  33. data/test/databases.yml +19 -0
  34. data/test/helper.rb +88 -0
  35. data/test/history_test.rb +55 -0
  36. data/test/object_utils_test.rb +26 -0
  37. data/test/reserved_test.rb +26 -0
  38. data/test/schema.rb +59 -0
  39. data/test/scoped_test.rb +57 -0
  40. data/test/shared.rb +118 -0
  41. data/test/slugged_test.rb +83 -0
  42. data/test/sti_test.rb +48 -0
  43. metadata +110 -102
  44. data/Contributors.md +0 -46
  45. data/Guide.md +0 -626
  46. data/extras/README.txt +0 -3
  47. data/extras/bench.rb +0 -40
  48. data/extras/extras.rb +0 -38
  49. data/extras/prof.rb +0 -19
  50. data/extras/template-gem.rb +0 -26
  51. data/extras/template-plugin.rb +0 -28
  52. data/generators/friendly_id/friendly_id_generator.rb +0 -30
  53. data/generators/friendly_id/templates/create_slugs.rb +0 -18
  54. data/lib/tasks/friendly_id.rake +0 -19
  55. data/rails/init.rb +0 -2
  56. data/test/active_record_adapter/ar_test_helper.rb +0 -149
  57. data/test/active_record_adapter/basic_slugged_model_test.rb +0 -14
  58. data/test/active_record_adapter/cached_slug_test.rb +0 -76
  59. data/test/active_record_adapter/core.rb +0 -138
  60. data/test/active_record_adapter/custom_normalizer_test.rb +0 -20
  61. data/test/active_record_adapter/custom_table_name_test.rb +0 -22
  62. data/test/active_record_adapter/default_scope_test.rb +0 -30
  63. data/test/active_record_adapter/optimistic_locking_test.rb +0 -18
  64. data/test/active_record_adapter/scoped_model_test.rb +0 -129
  65. data/test/active_record_adapter/simple_test.rb +0 -76
  66. data/test/active_record_adapter/slug_test.rb +0 -34
  67. data/test/active_record_adapter/slugged.rb +0 -33
  68. data/test/active_record_adapter/slugged_status_test.rb +0 -28
  69. data/test/active_record_adapter/sti_test.rb +0 -22
  70. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +0 -2
  71. data/test/active_record_adapter/support/database.mysql.yml +0 -4
  72. data/test/active_record_adapter/support/database.mysql2.yml +0 -4
  73. data/test/active_record_adapter/support/database.postgres.yml +0 -6
  74. data/test/active_record_adapter/support/database.sqlite3.yml +0 -2
  75. data/test/active_record_adapter/support/models.rb +0 -104
  76. data/test/active_record_adapter/tasks_test.rb +0 -82
  77. data/test/compatibility/ancestry/Gemfile.lock +0 -34
  78. data/test/friendly_id_test.rb +0 -96
  79. data/test/test_helper.rb +0 -13
metadata CHANGED
@@ -1,87 +1,125 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.3.0
5
- prerelease:
4
+ version: 4.0.0.beta7
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Norman Clarke
9
- - Adrian Mugnolo
10
- - Emilio Tagua
11
9
  autorequire:
12
10
  bindir: bin
13
11
  cert_chain: []
14
- date: 2012-04-28 00:00:00.000000000 Z
12
+ date: 2011-08-15 00:00:00.000000000 -03:00
13
+ default_executable:
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
- name: babosa
18
- requirement: &70213652491800 !ruby/object:Gem::Requirement
16
+ name: activerecord
17
+ requirement: &70318438630040 !ruby/object:Gem::Requirement
19
18
  none: false
20
19
  requirements:
21
20
  - - ~>
22
21
  - !ruby/object:Gem::Version
23
- version: 0.3.0
24
- type: :runtime
22
+ version: '3.0'
23
+ type: :development
25
24
  prerelease: false
26
- version_requirements: *70213652491800
25
+ version_requirements: *70318438630040
27
26
  - !ruby/object:Gem::Dependency
28
- name: activerecord
29
- requirement: &70213652491280 !ruby/object:Gem::Requirement
27
+ name: sqlite3
28
+ requirement: &70318438629540 !ruby/object:Gem::Requirement
30
29
  none: false
31
30
  requirements:
32
- - - ! '>='
31
+ - - ~>
33
32
  - !ruby/object:Gem::Version
34
- version: '3.0'
35
- - - <
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *70318438629540
37
+ - !ruby/object:Gem::Dependency
38
+ name: minitest
39
+ requirement: &70318438629080 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
36
43
  - !ruby/object:Gem::Version
37
- version: '3.3'
44
+ version: 2.4.0
38
45
  type: :development
39
46
  prerelease: false
40
- version_requirements: *70213652491280
47
+ version_requirements: *70318438629080
41
48
  - !ruby/object:Gem::Dependency
42
49
  name: mocha
43
- requirement: &70213652490560 !ruby/object:Gem::Requirement
50
+ requirement: &70318438628620 !ruby/object:Gem::Requirement
44
51
  none: false
45
52
  requirements:
46
53
  - - ~>
47
54
  - !ruby/object:Gem::Version
48
- version: '0.9'
55
+ version: 0.9.12
49
56
  type: :development
50
57
  prerelease: false
51
- version_requirements: *70213652490560
58
+ version_requirements: *70318438628620
52
59
  - !ruby/object:Gem::Dependency
53
- name: sqlite3
54
- requirement: &70213652490100 !ruby/object:Gem::Requirement
60
+ name: ffaker
61
+ requirement: &70318438628120 !ruby/object:Gem::Requirement
55
62
  none: false
56
63
  requirements:
57
64
  - - ~>
58
65
  - !ruby/object:Gem::Version
59
- version: '1.3'
66
+ version: 1.8.0
60
67
  type: :development
61
68
  prerelease: false
62
- version_requirements: *70213652490100
69
+ version_requirements: *70318438628120
63
70
  - !ruby/object:Gem::Dependency
64
- name: rake
65
- requirement: &70213652489640 !ruby/object:Gem::Requirement
71
+ name: maruku
72
+ requirement: &70318438627560 !ruby/object:Gem::Requirement
66
73
  none: false
67
74
  requirements:
68
75
  - - ~>
69
76
  - !ruby/object:Gem::Version
70
- version: 0.9.2
77
+ version: 0.6.0
71
78
  type: :development
72
79
  prerelease: false
73
- version_requirements: *70213652489640
74
- description: ! " FriendlyId is the \"Swiss Army bulldozer\" of slugging and permalink
75
- plugins\n for Ruby on Rails. It allows you to create pretty URL's and work with\n
76
- \ human-friendly strings as if they were numeric ids for ActiveRecord models.\n"
80
+ version_requirements: *70318438627560
81
+ - !ruby/object:Gem::Dependency
82
+ name: yard
83
+ requirement: &70318438627040 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: 0.7.2
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: *70318438627040
92
+ description: ! 'FriendlyId is the "Swiss Army bulldozer" of slugging and permalink
93
+ plugins for
94
+
95
+ Ruby on Rails. It allows you to create pretty URL''s and work with
96
+
97
+ human-friendly strings as if they were numeric ids for ActiveRecord models.
98
+
99
+ '
77
100
  email:
78
101
  - norman@njclarke.com
79
- - adrian@mugnolo.com
80
- - miloops@gmail.com
81
102
  executables: []
82
103
  extensions: []
83
104
  extra_rdoc_files: []
84
105
  files:
106
+ - .gemtest
107
+ - .gitignore
108
+ - .travis.yml
109
+ - .yardopts
110
+ - Changelog.md
111
+ - Gemfile
112
+ - MIT-LICENSE
113
+ - README.md
114
+ - Rakefile
115
+ - WhatsNew.md
116
+ - bench.rb
117
+ - friendly_id.gemspec
118
+ - gemfiles/Gemfile.rails-3.0.rb
119
+ - gemfiles/Gemfile.rails-3.0.rb.lock
120
+ - gemfiles/Gemfile.rails-3.1.rb
121
+ - gemfiles/Gemfile.rails-3.1.rb.lock
122
+ - lib/friendly_id.rb
85
123
  - lib/friendly_id/active_record.rb
86
124
  - lib/friendly_id/active_record_adapter/configuration.rb
87
125
  - lib/friendly_id/active_record_adapter/relation.rb
@@ -89,63 +127,52 @@ files:
89
127
  - lib/friendly_id/active_record_adapter/slug.rb
90
128
  - lib/friendly_id/active_record_adapter/slugged_model.rb
91
129
  - lib/friendly_id/active_record_adapter/tasks.rb
130
+ - lib/friendly_id/base.rb
92
131
  - lib/friendly_id/configuration.rb
93
132
  - lib/friendly_id/datamapper.rb
133
+ - lib/friendly_id/finder_methods.rb
134
+ - lib/friendly_id/history.rb
135
+ - lib/friendly_id/migration.rb
136
+ - lib/friendly_id/model.rb
137
+ - lib/friendly_id/object_utils.rb
94
138
  - lib/friendly_id/railtie.rb
139
+ - lib/friendly_id/reserved.rb
140
+ - lib/friendly_id/scoped.rb
95
141
  - lib/friendly_id/sequel.rb
142
+ - lib/friendly_id/slug.rb
143
+ - lib/friendly_id/slug_sequencer.rb
96
144
  - lib/friendly_id/slug_string.rb
97
145
  - lib/friendly_id/slugged.rb
98
146
  - lib/friendly_id/status.rb
99
147
  - lib/friendly_id/test.rb
100
148
  - lib/friendly_id/version.rb
101
- - lib/friendly_id.rb
102
149
  - lib/generators/friendly_id_generator.rb
103
- - lib/tasks/friendly_id.rake
104
- - Changelog.md
105
- - Contributors.md
106
- - Guide.md
107
- - README.md
108
- - MIT-LICENSE
109
- - Rakefile
110
- - rails/init.rb
111
- - generators/friendly_id/friendly_id_generator.rb
112
- - generators/friendly_id/templates/create_slugs.rb
113
- - test/active_record_adapter/ar_test_helper.rb
114
- - test/active_record_adapter/basic_slugged_model_test.rb
115
- - test/active_record_adapter/cached_slug_test.rb
116
- - test/active_record_adapter/core.rb
117
- - test/active_record_adapter/custom_normalizer_test.rb
118
- - test/active_record_adapter/custom_table_name_test.rb
119
- - test/active_record_adapter/default_scope_test.rb
120
- - test/active_record_adapter/optimistic_locking_test.rb
121
- - test/active_record_adapter/scoped_model_test.rb
122
- - test/active_record_adapter/simple_test.rb
123
- - test/active_record_adapter/slug_test.rb
124
- - test/active_record_adapter/slugged.rb
125
- - test/active_record_adapter/slugged_status_test.rb
126
- - test/active_record_adapter/sti_test.rb
127
- - test/active_record_adapter/support/database.jdbcsqlite3.yml
128
- - test/active_record_adapter/support/database.mysql.yml
129
- - test/active_record_adapter/support/database.mysql2.yml
130
- - test/active_record_adapter/support/database.postgres.yml
131
- - test/active_record_adapter/support/database.sqlite3.yml
132
- - test/active_record_adapter/support/models.rb
133
- - test/active_record_adapter/tasks_test.rb
134
- - test/compatibility/ancestry/Gemfile.lock
135
- - test/friendly_id_test.rb
136
- - test/test_helper.rb
137
- - extras/bench.rb
138
- - extras/extras.rb
139
- - extras/prof.rb
140
- - extras/README.txt
141
- - extras/template-gem.rb
142
- - extras/template-plugin.rb
143
- - .gemtest
150
+ - test/base_test.rb
151
+ - test/configuration_test.rb
152
+ - test/core_test.rb
153
+ - test/databases.yml
154
+ - test/helper.rb
155
+ - test/history_test.rb
156
+ - test/object_utils_test.rb
157
+ - test/reserved_test.rb
158
+ - test/schema.rb
159
+ - test/scoped_test.rb
160
+ - test/shared.rb
161
+ - test/slugged_test.rb
162
+ - test/sti_test.rb
163
+ has_rdoc: true
144
164
  homepage: http://norman.github.com/friendly_id
145
165
  licenses: []
146
- post_install_message: ! " FriendlyId 3.3.x is now in long-term maintanence. For
147
- new projects with\n Rails 3.1.x please consider using 4.0, which is under active
148
- development:\n\n https://github.com/norman/friendly_id\n"
166
+ post_install_message: ! 'NOTE: FriendlyId 4.x breaks compatibility with 3.x. If you''re
167
+ upgrading
168
+
169
+ from 3.x, please see this document:
170
+
171
+
172
+ https://github.com/norman/friendly_id/blob/4.0.0/WhatsNew.md
173
+
174
+
175
+ '
149
176
  rdoc_options: []
150
177
  require_paths:
151
178
  - lib
@@ -155,35 +182,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
182
  - - ! '>='
156
183
  - !ruby/object:Gem::Version
157
184
  version: '0'
158
- segments:
159
- - 0
160
- hash: 1898456187298114319
161
185
  required_rubygems_version: !ruby/object:Gem::Requirement
162
186
  none: false
163
187
  requirements:
164
- - - ! '>='
188
+ - - ! '>'
165
189
  - !ruby/object:Gem::Version
166
- version: '0'
167
- segments:
168
- - 0
169
- hash: 1898456187298114319
190
+ version: 1.3.1
170
191
  requirements: []
171
- rubyforge_project: friendly-id
172
- rubygems_version: 1.8.11
192
+ rubyforge_project: friendly_id
193
+ rubygems_version: 1.6.2
173
194
  signing_key:
174
195
  specification_version: 3
175
196
  summary: A comprehensive slugging and pretty-URL plugin.
176
- test_files:
177
- - test/active_record_adapter/basic_slugged_model_test.rb
178
- - test/active_record_adapter/cached_slug_test.rb
179
- - test/active_record_adapter/custom_normalizer_test.rb
180
- - test/active_record_adapter/custom_table_name_test.rb
181
- - test/active_record_adapter/default_scope_test.rb
182
- - test/active_record_adapter/optimistic_locking_test.rb
183
- - test/active_record_adapter/scoped_model_test.rb
184
- - test/active_record_adapter/simple_test.rb
185
- - test/active_record_adapter/slug_test.rb
186
- - test/active_record_adapter/slugged_status_test.rb
187
- - test/active_record_adapter/sti_test.rb
188
- - test/active_record_adapter/tasks_test.rb
189
- - test/friendly_id_test.rb
197
+ test_files: []
data/Contributors.md DELETED
@@ -1,46 +0,0 @@
1
- We are grateful for many contributions from the Ruby and Rails
2
- community, in particular from the following people:
3
-
4
- * Adam Cigánek
5
- * Alex Coles
6
- * Alexander Gräfe
7
- * Alistair Holt
8
- * Andre Duffeck
9
- * Andrew Loe III
10
- * Ben Woosley
11
- * Bence Nagy
12
- * Brian Collins
13
- * Bruno Michel
14
- * Chris Nolan
15
- * Cyrille Stepanyk
16
- * David Ramalho
17
- * Diego Carrion
18
- * Diego R. V.
19
- * Eric Lindvall
20
- * Florian Aßmanqn
21
- * Gabe da Silveira
22
- * Harry Love
23
- * Ian Stewart
24
- * James Cropcho
25
- * Jesse Crouch
26
- * Joe Van Dyk
27
- * Johan Kok
28
- * Josh Nichols
29
- * Juan Schwindt
30
- * Kamal Fariz Mahyuddin
31
- * Laurence A. Lee
32
- * Loïc Guitaut
33
- * Louis T.
34
- * Mikhail Shirkov
35
- * Nash Kabbara
36
- * Nathan Phelps
37
- * Philip Arndt
38
- * Ramon Soares
39
- * Rdavila
40
- * Rob Ingram
41
- * Ryan Wood
42
- * Sean Abrahams
43
- * Steve Luscher
44
- * Steven Noble
45
- * Tim Kadom
46
- * William Melody
data/Guide.md DELETED
@@ -1,626 +0,0 @@
1
- # FriendlyId Guide
2
-
3
- * Table of Contents
4
- {:toc}
5
-
6
- ## Overview
7
-
8
- FriendlyId is an ORM-centric Ruby library that lets you work with human-friendly
9
- strings as if they were numeric ids. Among other things, this facilitates
10
- replacing "unfriendly" URL's like:
11
-
12
- http://example.com/states/4323454
13
-
14
- with "friendly" ones such as:
15
-
16
- http://example.com/states/washington
17
-
18
- FriendlyId is typically used with Rails and Active Record, but can also be used in
19
- non-Rails applications, and with [Sequel](http://github.com/norman/friendly_id_sequel) and
20
- [DataMapper](http://github.com/myabc/friendly_id_datamapper).
21
-
22
- ## Simple Models
23
-
24
- The simplest way to use FriendlyId is with a model that has a uniquely indexed
25
- column with no spaces or special characters, and that is seldom or never
26
- updated. The most common example of this is a user name or login column:
27
-
28
- class User < ActiveRecord::Base
29
- validates_format_of :login, :with => /\A[a-z0-9]+\z/i
30
- has_friendly_id :login
31
- end
32
-
33
- @user = User.find "joe" # the old User.find(1) still works, too
34
- @user.to_param # returns "joe"
35
- redirect_to @user # the URL will be /users/joe
36
-
37
- In this case, FriendlyId assumes you want to use the column as-is; it will never
38
- modify the value of the column, and your application should ensure that the value
39
- is admissible in a URL:
40
-
41
- class City < ActiveRecord::Base
42
- has_friendly_id :name
43
- end
44
-
45
- @city.find "Viña del Mar"
46
- redirect_to @city # the URL will be /cities/Viña%20del%20Mar
47
-
48
- For this reason, it is often more convenient to use Slugs rather than a single
49
- column.
50
-
51
- ## Slugged Models
52
-
53
- FriendlyId uses a separate table to store slugs for models which require some
54
- processing of the friendly_id text. The most common example is a blog post's
55
- title, which may have spaces, uppercase characters, or other attributes you
56
- wish to modify to make them more suitable for use in URL's.
57
-
58
- class Post < ActiveRecord::Base
59
- has_friendly_id :title, :use_slug => true
60
- end
61
-
62
- @post = Post.create(:title => "This is the first post!")
63
- @post.friendly_id # returns "this-is-the-first-post"
64
- redirect_to @post # the URL will be /posts/this-is-the-first-post
65
-
66
- If you are unsure whether to use slugs, then your best bet is to use them,
67
- because FriendlyId provides many useful features that only work with this
68
- feature. These features are explained in detail {file:Guide.md#features below}.
69
-
70
- ## Installation
71
-
72
- ### Gem installation
73
-
74
- gem install friendly_id
75
-
76
- After installing the gem, add an entry in the Gemfile:
77
-
78
- gem "friendly_id", "~> 3.3.0"
79
-
80
- ### Roadmap
81
-
82
- FriendlyId 3.3 is now in **long term maintenance mode.** It will continue to be
83
- supported and maintained indefinitely, but no new features will be added to it.
84
-
85
- [FriendlyId 4.0](https://github.com/norman/friendly_id/tree/4.0.0) is a
86
- ground-up rewrite of FriendlyId, and is the project's future, and will be
87
- released by September, 2011.
88
-
89
- ### Setup
90
-
91
- rails generate friendly_id
92
- rake db:migrate
93
-
94
- This will install the Rake tasks and slug migration for FriendlyId. If you are
95
- not going to use slugs, you can use the `skip-migration` option:
96
-
97
- rails generate friendly_id --skip-migration
98
-
99
- FriendlyId is now set up and ready for you to use.
100
-
101
- ## Configuration
102
-
103
- FriendlyId is configured in your model using the `has_friendly_id` method:
104
-
105
- has_friendly_id :a_column_or_method options_hash
106
-
107
- class Post < ActiveRecord::Base
108
- # use the "title" column as the basis of the friendly_id, and use slugs
109
- has_friendly_id :title, :use_slug => true,
110
- # remove accents and other diacritics from Latin characters
111
- :approximate_ascii => true,
112
- # don't use slugs larger than 50 bytes
113
- :max_length => 50
114
- end
115
-
116
- Read on to learn about the various features that can be configured. For the
117
- full list of valid configuration options, see the instance attribute summary
118
- for {FriendlyId::Configuration}.
119
-
120
- # Features
121
-
122
- ## FriendlyId Strings
123
-
124
- FriendlyId uses the [Babosa](http://github.com/norman/babosa) library for
125
- generating slug strings. When using slugs, FriendlyId/Babosa will automatically
126
- modify the slug text to make it more suitable for use in a URL:
127
-
128
- class City < ActiveRecord::Base
129
- has_friendly_id :name, :use_slug => true
130
- end
131
-
132
- @city.create :name => "Viña del Mar"
133
- @city.friendly_id # will be "viña-del-mar"
134
-
135
- By default, the string is downcased and stripped, spaces are replaced with
136
- dashes, and non-word characters other than "-" are removed.
137
-
138
- ### Replacing Accented Characters
139
-
140
- If your strings use Latin characters, you can use the `:approximate_ascii` option to remove
141
- accents and other diacritics:
142
-
143
- class City < ActiveRecord::Base
144
- has_friendly_id :name, :use_slug => true, :approximate_ascii => true
145
- end
146
-
147
- @city.create :name => "Łódź, Poland"
148
- @city.friendly_id # will be "lodz-poland"
149
-
150
- There are special options for some languages:
151
-
152
- class Person < ActiveRecord::Base
153
- has_friendly_id :name, :use_slug => true, :approximate_ascii => true,
154
- :ascii_approximation_options => :german
155
- end
156
-
157
- @person.create :name => "Jürgen Müller"
158
- @person.friendly_id # will be "juergen-mueller"
159
-
160
- FriendlyId supports whatever languages are supported by
161
- [Babosa](https://github.com/norman/babosa); at the time of writing, this
162
- includes Danish, German, Serbian and Spanish.
163
-
164
- ### Unicode Slugs
165
-
166
- By default, any character outside the Unicode Latin character range will be
167
- passed through untouched, allowing you to have slugs in Arabic, Japanese,
168
- Greek, etc:
169
-
170
- @post.create :title => "katakana: ゲコゴサザシジ!"
171
- @post.friendly_id # will be: "katakana-ゲコゴサザシジ"
172
-
173
- ### ASCII Slugs
174
-
175
- You can also configure FriendlyId using `:strip_non_ascii` to simply delete
176
- any non-ascii characters:
177
-
178
- class Post < ActiveRecord::Base
179
- has_friendly_id :title, :use_slug => true, :strip_non_ascii => true
180
- end
181
-
182
- @post.create :title => "katakana: ゲコゴサザシジ!"
183
- @post.friendly_id # will be: "katakana"
184
-
185
-
186
- ### Using a Custom Method to Generate the Slug Text
187
-
188
- FriendlyId can use either a column or a method to generate the slug text for
189
- your model:
190
-
191
- class City < ActiveRecord::Base
192
-
193
- belongs_to :country
194
- has_friendly_id :name_and_country, :use_slug => true
195
-
196
- def name_and_country
197
- #{name} #{country.name}
198
- end
199
-
200
- end
201
-
202
- @country = Country.create(:name => "Argentina")
203
- @city = City.create(:name => "Buenos Aires", :country => @country)
204
- @city.friendly_id # will be "buenos-aires-argentina"
205
-
206
- One word of caution: in the example above, if the country's name were updated,
207
- say, to "Argentine Republic", the city's friendly_id would not be
208
- automatically updated. For this reason, it's a good idea to avoid using
209
- frequently-updated relations as a part of the friendly_id.
210
-
211
- ## Using a Custom Method to Process the Slug Text
212
-
213
- If the built-in slug text handling options don't work for your application,
214
- you can override the `normalize_friendly_id` method in your model class in
215
- order to fine-tune the output:
216
-
217
- class City < ActiveRecord::Base
218
-
219
- def normalize_friendly_id(text)
220
- my_text_modifier_method(text)
221
- end
222
-
223
- end
224
-
225
- The normalize_friendly_id method takes a single argument and receives an
226
- instance of {FriendlyId::SlugString}, a class which wraps a regular Ruby string
227
- with additional formatting options.
228
-
229
- ### Converting non-Latin characters to ASCII with Stringex
230
-
231
- Stringex is a library which provides some interesting options for transliterating
232
- non-Latin strings to ASCII:
233
-
234
- "你好".to_url => "ni-hao"
235
-
236
- Using Stringex with FriendlyId is a simple matter of installing and requiring
237
- the `stringex` gem, and overriding the `normalize_friendly_id` method in your
238
- model:
239
-
240
- class City < ActiveRecord::Base
241
- def normalize_friendly_id(text)
242
- text.to_url
243
- end
244
- end
245
-
246
- However, be aware of some limitations of Stringex - it just does a context-free
247
- character-by-character approximation for Unicode strings without sensitivity to
248
- the string's language. This means, for example, that the Han characters used by
249
- Japanese, Mandarin, Cantonese, and other languages are all replaced with the
250
- same ASCII text. For Han characters, Stringex uses Mandarin, which makes its
251
- output on Japanese text useless. You can read more about the limitations of
252
- Stringex in [the documentation for
253
- Unidecoder](http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm#DESIGN_GOALS_AND_CONSTRAINTS),
254
- the Perl library upon which Stringex is based.
255
-
256
- ## Redirecting to the Current Friendly URL
257
-
258
- FriendlyId maintains a history of your record's older slugs, so if your
259
- record's friendly_id changes, your URL's won't break. It offers several
260
- methods to determine whether the model instance was found using the most
261
- recent friendly_id. This helps you redirect to your "unfriendly" URL's to your
262
- new "friendly" ones when adding FriendlyId to an existing application:
263
-
264
- class PostsController < ApplicationController
265
-
266
- before_filter ensure_current_post_url, :only => :show
267
-
268
- ...
269
-
270
- def ensure_current_post_url
271
- redirect_to @post, :status => :moved_permanently unless @post.friendly_id_status.best?
272
- end
273
-
274
- end
275
-
276
- For more information, take a look at the documentation for {FriendlyId::Status}.
277
-
278
- ## Non-unique Slugs
279
-
280
- FriendlyId will append a arbitrary number to the end of the id to keep it
281
- unique if necessary:
282
-
283
- /posts/new-version-released
284
- /posts/new-version-released--2
285
- /posts/new-version-released--3
286
- ...
287
- etc.
288
-
289
- Note that the number is preceded by "--" rather than "-" to distinguish it from
290
- the rest of the slug. This is important to enable having slugs like:
291
-
292
- /cars/peugeot-206
293
- /cars/peugeot-206--2
294
-
295
- You can configure the separator string used by your model by setting the
296
- `:sequence_separator` option in `has_friendly_id`:
297
-
298
- has_friendly_id :title, :use_slug => true, :sequence_separator => ":"
299
-
300
- You can also override the default used in
301
- {FriendlyId::Configuration::DEFAULTS} to set the value for any model using
302
- FriendlyId. If you change this value in an existing application, be sure to
303
- {file:Guide.md#regenerating_slugs regenerate the slugs} afterwards.
304
-
305
- For reasons I hope are obvious, you can't change this value to "-". If you try,
306
- FriendlyId will raise an error.
307
-
308
- ## Reserved Words
309
-
310
- You can configure a list of strings as reserved so that, for example, you
311
- don't end up with this problem:
312
-
313
- /users/joe-schmoe # A user chose "joe schmoe" as his user name - no worries.
314
- /users/new # A user chose "new" as his user name, and now no one can sign up.
315
-
316
- Reserved words are configured using the `:reserved_words` option:
317
-
318
- class Restaurant < ActiveRecord::Base
319
- belongs_to :city
320
- has_friendly_id :name, :use_slug => true, :reserved_words => ["my", "values"]
321
- end
322
-
323
- The reserved words can be specified as an array or (since 3.1.7) as a regular
324
- expression.
325
-
326
- The strings "new" and "index" are reserved by default. When you attempt to
327
- store a reserved value, FriendlyId raises a
328
- {FriendlyId::ReservedError}. You can also override the default
329
- reserved words in {FriendlyId::Configuration::DEFAULTS} to set the value for any
330
- model using FriendlyId.
331
-
332
- If you'd like to show a validation error when a word is reserved, you can add
333
- an callback to your model that catches the error:
334
-
335
- class Person < ActiveRecord::Base
336
- has_friendly_id :name, :use_slug => true
337
-
338
- after_validation :validate_reserved
339
-
340
- def validate_reserved
341
- slug_text
342
- rescue FriendlyId::ReservedError
343
- @errors[friendly_id_config.method] = "is reserved. Please choose something else"
344
- return false
345
- end
346
- end
347
-
348
- ## Caching the FriendlyId Slug for Better Performance
349
-
350
- Checking the slugs table all the time has an impact on performance, so as of
351
- 2.2.0, FriendlyId offers slug caching.
352
-
353
- ### Automatic setup
354
-
355
- To enable slug caching, simply add a column named "cached_slug" to your model.
356
- FriendlyId will automatically use this column if it detects it:
357
-
358
- class AddCachedSlugToUsers < ActiveRecord::Migration
359
- def self.up
360
- add_column :users, :cached_slug, :string
361
- add_index :users, :cached_slug, :unique => true
362
- end
363
-
364
- def self.down
365
- remove_column :users, :cached_slug
366
- end
367
- end
368
-
369
- Then, redo the slugs:
370
-
371
- rake friendly_id:redo_slugs MODEL=User
372
-
373
- FriendlyId will automatically query against the cache column if it's available,
374
- which will <a href="#some_benchmarks">improve the performance</a> of many queries.
375
-
376
- A few warnings when using this feature:
377
-
378
- * *DO NOT* forget to redo the slugs, or else this feature will not work!
379
- * This feature uses `attr_protected` to protect the `cached_slug` column,
380
- unless you have already invoked `attr_accessible`. If you wish to use
381
- `attr_accessible`, you must invoke it BEFORE you invoke `has_friendly_id` in
382
- your class.
383
- * Cached slugs [are incompatible with scopes](#scoped_models_and_cached_slugs) and
384
- are ignored if your model uses the `:scope option`.
385
-
386
- ### Using a custom column name
387
-
388
- You can also use a different name for the column if you choose, via the
389
- `:cache_column` config option:
390
-
391
- class User < ActiveRecord::Base
392
- has_friendly_id :name, :use_slug => true, :cache_column => 'my_cached_slug'
393
- end
394
-
395
- Don't use "slug" or "slugs" because FriendlyId needs those names for its own
396
- purposes.
397
-
398
- ## Nil slugs and skipping validations
399
-
400
- You can choose to allow `nil` friendly_ids via the `:allow_nil` config option:
401
-
402
- class User < ActiveRecord::Base
403
- has_friendly_id :name, :allow_nil => true
404
- end
405
-
406
- This works whether the model uses slugs or not.
407
-
408
- For slugged models, if the friendly_id text is `nil`, no slug will be created.
409
- This can be useful, for example, to only create slugs for published articles
410
- and avoid creating many slugs with sequences.
411
-
412
- For models that don't use slugs, this will make FriendlyId skip all its
413
- validations when the friendly_id text is `nil`. This can be useful, for
414
- example, if you wish to add the friendly_id value in an `:after_save` callback.
415
-
416
- For non-slugged models, if you simply wish to skip friendly_ids's validations
417
- for some reason, you can override the `skip_friendly_id_validations` method.
418
- Note that this method is **not** used by slugged models.
419
-
420
- ## Scoped Slugs
421
-
422
- _Note that in FriendlyId prior to 3.2.0, you could specify a non-standard
423
- `:scope` argument on finds. This feature has been removed in 3.2.0 in favor of
424
- the query stategies described below._
425
-
426
- FriendlyId can generate unique slugs within a given scope. For example, assume
427
- you have an application that displays restaurants. Without scoped slugs, if two
428
- restaurants are named "Joe's Diner," the second one will end up with
429
- "joes-diner--2" as its friendly_id. Using scoped allows you to keep the slug
430
- names unique for each city, so that the second "Joe's Diner" can also have the
431
- slug "joes-diner", as long as it's located in a different city:
432
-
433
- class Restaurant < ActiveRecord::Base
434
- belongs_to :city
435
- has_friendly_id :name, :use_slug => true, :scope => :city
436
- end
437
-
438
- class City < ActiveRecord::Base
439
- has_many :restaurants
440
- has_friendly_id :name, :use_slug => true
441
- end
442
-
443
- City.find("seattle").restaurants.find("joes-diner")
444
- City.find("chicago").restaurants.find("joes-diner")
445
-
446
-
447
- The value for the `:scope` key in your model can be a custom method you
448
- define, or the name of a relation. If it's the name of a relation, then the
449
- scope's text value will be the result of calling `to_param` on the related
450
- model record. In the example above, the city model also uses FriendlyId and so
451
- its `to_param` method returns its friendly_id: "chicago" or "seattle".
452
-
453
- ### Complications with Scoped Slugs
454
-
455
- #### Scoped Models and Cached Slugs
456
-
457
- If you want to use cached slugs with scoped models, be sure not to create a unique index on the
458
- `cached_slug` column.
459
-
460
-
461
- #### Finding Records by friendly\_id
462
-
463
- If you are using scopes your friendly ids may not be unique, so a simple find like
464
-
465
- Restaurant.find("joes-diner")
466
-
467
- may return the wrong record. In these cases when you want to use the friendly\_id for queries,
468
- either query as a relation, or specify the scope in your query conditions:
469
-
470
- # will only return restaurants named "Joe's Diner" in the given city
471
- @city.restaurants.find("joes-diner")
472
-
473
- # or
474
-
475
- Restaurants.find("joes-diner", :include => :slugs, :conditions => {:slugs => {:scope => @city.to_param}})
476
-
477
-
478
- #### Finding All Records That Match a Scoped ID
479
-
480
- If you want to find all records with a particular friendly\_id regardless of scope,
481
- the easiest way is to use cached slugs and query this column directly:
482
-
483
- Restaurant.find_all_by_cached_slug("joes-diner")
484
-
485
-
486
- If you're not using cached slugs, then this is slightly more complicated, but
487
- still doable:
488
-
489
- name, sequence = params[:id].parse_friendly_id
490
- Restaurant.all(:include => :slugs, :conditions => {
491
- :slugs => {:name => name, :sequence => sequence}
492
- })
493
-
494
-
495
- #### Updating a Relation's Scoped Slugs
496
-
497
- When using a relation as the scope, updating the relation will update the slugs,
498
- but only if both models have specified the relationship. In the above example,
499
- updates to City will update the slugs for Restaurant because City specifies that
500
- it `has_many :restaurants`.
501
-
502
- ### Routes for Scoped Models
503
-
504
- Note that FriendlyId does not set up any routes for scoped models; you must do
505
- this yourself in your application. Here's an example of one way to set this up:
506
-
507
- # in routes.rb
508
- resources :cities do
509
- resources :restaurants
510
- end
511
-
512
- # in views
513
- <%= link_to 'Show', [@city, @restaurant] %>
514
-
515
- # in controllers
516
- @city = City.find(params[:city_id])
517
- @restaurant = @city.restaurants.find(params[:id])
518
-
519
- # URL's:
520
- http://example.org/cities/seattle/restaurants/joes-diner
521
- http://example.org/cities/chicago/restaurants/joes-diner
522
-
523
-
524
- ## FriendlyId Rake Tasks
525
-
526
- FriendlyId provides several tasks to help maintain your application.
527
-
528
- ### Generating New Slugs For the First Time
529
-
530
- friendly_id:make_slugs MODEL=<model name>
531
-
532
- Use this task to generate slugs after installing FriendlyId in a new
533
- application.
534
-
535
- ### Regenerating Slugs
536
-
537
- friendly_id:redo_slugs MODEL=<model name>
538
-
539
- Use this task to regenerate slugs after making any changes to your model's
540
- FriendlyId configuration options that affect slug generation. For example,
541
- if you introduce a `cached_slug` column or change the `:seqence_separator`.
542
-
543
- ### Deleting Old Slugs
544
-
545
- rake friendly_id:remove_old_slugs MODEL=<model name> DAYS=<days>
546
-
547
- Use this task if you wish to delete expired slugs; manually or perhaps via
548
- cron. If you don't specify the days option, the default is to remove unused
549
- slugs older than 45 days.
550
-
551
- # Misc tips
552
-
553
- ## Allowing Users to Override/Control Slugs
554
-
555
- Would you like to mostly use default slugs, but allow the option of a
556
- custom user-chosen slug in your application? If so, then you're not the first to
557
- want this. Here's a [demo
558
- application](http://github.com/norman/friendly_id_manual_slug_demo) showing how
559
- it can be done.
560
-
561
- ## Default Scopes
562
-
563
- Whether you're using FriendlyId or not, a good rule of thumb for default scopes
564
- is to always use your model's table name. Otherwise any time you do a join, you
565
- risk having queries fail because of duplicate column names - particularly for a
566
- default scope like this one:
567
-
568
- default_scope :order => "created_at DESC"
569
-
570
- Instead, do this:
571
-
572
- default_scope :order => = "#{quoted_table_name}.created_at DESC"
573
-
574
- Or even better, unless you're using a custom primary key:
575
-
576
- default_scope :order => = "#{quoted_table_name}.id DESC"
577
-
578
- because sorting by a unique integer column is faster than sorting by a date
579
- column.
580
-
581
- ## MySQL MyISAM tables
582
-
583
- Currently, the default FriendlyId migration will not work with MyISAM tables
584
- because it creates an index that's too large. The easiest way to work around
585
- this is to change the generated migration to add limits on some column lengths.
586
- Please see [this issue](http://github.com/norman/friendly_id/issues#issue/50) in
587
- the FriendlyId issue tracker for more information.
588
-
589
- # Hacking FriendlyId
590
-
591
- A couple of notes for programmers intending to work on FriendlyId:
592
-
593
- If you intend to send a pull request, in general it's best to make minor
594
- changes in the master branch, and major changes in the edge branch.
595
-
596
- Before removing any public or protected methods, FriendlyId will deprecate
597
- them through one major release cycle. Private methods may, however, change at
598
- any time.
599
-
600
- ## Some Benchmarks
601
-
602
- These benchmarks can give you an idea of FriendlyId's impact on the
603
- performance of your application. Of course your results may vary.
604
-
605
- Note that much of the performance difference can be attributed to finding an
606
- SQL record by a text column. Finding a single record by numeric primary key is
607
- always the fastest operation, and thus the best choice when possible. If you
608
- decide not to use FriendlyId for performance reasons, keep in mind that your
609
- own solution is unlikely to be any faster than FriendlyId with cached slugs
610
- enabled. But if it is, then your patches would be very welcome!
611
-
612
-
613
- activerecord (3.0.0)
614
- ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
615
- friendly_id (3.1.4)
616
- sqlite3-ruby (1.3.1)
617
- sqlite3 3.6.12 in-memory database
618
-
619
- | DEFAULT | NO_SLUG | SLUG | CACHED_SLUG |
620
- ------------------------------------------------------------------------------------------------
621
- find model by id x1000 | 0.286 | 0.365 | 0.518 | 0.393 |
622
- find model using array of ids x1000 | 0.329 | 0.441 | 0.709 | 0.475 |
623
- find model using id, then to_param x1000 | 0.321 | 0.332 | 0.976 | 0.399 |
624
- ================================================================================================
625
- Total | 0.936 | 1.138 | 2.203 | 1.266 |
626
-