friendly_id 5.0.0.beta4 → 5.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 04766b0ed96f1b271571b983cf3b73cf9385406c
4
- data.tar.gz: 5881c90dc07cf35780402d04211cc93ebd597d01
3
+ metadata.gz: eb59a1c03451e5b3848ab8fa00da5aa476276d79
4
+ data.tar.gz: f6057d3fad4267d1cea422b0eb72b78f433afc78
5
5
  SHA512:
6
- metadata.gz: 06b4a78391d3a6f9ca7a42e4769087e4dbf70a559e29313147b48b655dbc59ffddfbaf3f91e78854d64c8b1cedf1bf2e088280c9747fcb9fc45318ec7243d925
7
- data.tar.gz: 2e66f50e25d14cf807e2f2719c49e13e2936656ac50a6842f1225357089e0561b2ec39effad76571558999d04d905715f365649cd1119b7a2a8765dd81084c79
6
+ metadata.gz: 9bbd5d21be8df888836e6e98b14c43d45eb89da31816830c5287038dce5f1a60c9920adf3b69a498e96b5d0cb6addfe952c5983d307228f5c57726095d9160c7
7
+ data.tar.gz: cae59c6cbc8406c0cedf700ad1201a23b01d74ec6b7d871333d8c156e273332485f581b59d2df9e59da689e4c4219dffe6a8f770f7a48e9f8af1b8b0a18b8de3
data/.gitignore CHANGED
@@ -10,3 +10,5 @@ coverage
10
10
  *.rbc
11
11
  *.lock
12
12
  .rbx
13
+ Guide.md
14
+ .friendly_id
data/.yardopts CHANGED
@@ -1,3 +1,4 @@
1
+ -e guide.rb
1
2
  --files=Changelog.md,Guide.md
2
3
  --private
3
4
  --protected
data/Changelog.md CHANGED
@@ -3,6 +3,13 @@
3
3
  We would like to think our many {file:Contributors contributors} for
4
4
  suggestions, ideas and improvements to FriendlyId.
5
5
 
6
+ ## 5.0.0.rc1 (2013-08-28)
7
+
8
+ * Removed some outdated tests.
9
+ * Improved documentation.
10
+ * Removed Guide from repository and added tasks to maintain docs up to date
11
+ on Github pages at http://norman.github.io/friendly_id.
12
+
6
13
  ## 5.0.0.beta4 (2013-08-21)
7
14
 
8
15
  * Add an initializer to the generator; move the default reserved words there.
data/README.md CHANGED
@@ -15,7 +15,7 @@ branch](https://github.com/norman/friendly_id/tree/4.0-stable).
15
15
 
16
16
  # FriendlyId
17
17
 
18
- <em>For the most complete, user-friendly documentation, see the [FriendlyId Guide](http://rubydoc.info/github/norman/friendly_id/master/file/Guide.md).</em>
18
+ <em>For the most complete, user-friendly documentation, see the [FriendlyId Guide](http://norman.github.io/friendly_id/file.Guide.html).</em>
19
19
 
20
20
  FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
21
21
  Active Record. It lets you create pretty URLs and work with human-friendly
@@ -91,11 +91,13 @@ The most important changes are:
91
91
  restaurant.friendly_id # the-plaza-diner
92
92
 
93
93
  You can restore some of the old behavior by overriding the
94
- `should_generate_new_friendly_id` method.
94
+ `should_generate_new_friendly_id?` method.
95
95
 
96
96
  * The `friendly_id` Rails generator now generates an initializer showing you
97
97
  how to do some commmon global configuration.
98
98
 
99
+ * The Globalize plugin has moved to a separate gem (currently in alpha).
100
+
99
101
  * The `:reserved` module no longer includes any default reserved words.
100
102
  Previously it blocked "edit" and "new" everywhere. The default word list has
101
103
  been moved to `config/initializers/friendly_id.rb` and now includes many more
@@ -127,10 +129,15 @@ A migration like this should be sufficient:
127
129
  ## Docs
128
130
 
129
131
  The most current docs from the master branch can always be found
130
- [here](http://rubydoc.info/github/norman/friendly_id/master/frames).
132
+ [here](http://norman.github.io/friendly_id).
133
+
134
+ Docs for older versions are also available:
135
+
136
+ * [4.0](http://norman.github.io/friendly_id/4.0/)
137
+ * [3.3](http://norman.github.io/friendly_id/3.3/)
131
138
 
132
139
  The best place to start is with the
133
- [Guide](http://rubydoc.info/github/norman/friendly_id/master/file/Guide.md),
140
+ [Guide](http://norman.github.io/friendly_id/file.Guide.html),
134
141
  which compiles the top-level RDocs into one outlined document.
135
142
 
136
143
  You might also want to watch Ryan Bates's [Railscast on FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid),
data/Rakefile CHANGED
@@ -27,7 +27,7 @@ task :gem do
27
27
  end
28
28
 
29
29
  desc "Build YARD documentation"
30
- task :yard => :guide do
30
+ task :yard do
31
31
  puts %x{bundle exec yard}
32
32
  end
33
33
 
@@ -38,26 +38,7 @@ end
38
38
 
39
39
  desc "Generate Guide.md"
40
40
  task :guide do
41
- def read_comments(path)
42
- path = File.expand_path("../#{path}", __FILE__)
43
- match = File.read(path).match(/\n=begin(.*)\n=end/m)[1].to_s
44
- match.split("\n").reject {|x| x =~ /^@/}.join("\n")
45
- end
46
-
47
- buffer = []
48
-
49
- buffer << read_comments("lib/friendly_id.rb")
50
- buffer << read_comments("lib/friendly_id/base.rb")
51
- buffer << read_comments("lib/friendly_id/finders.rb")
52
- buffer << read_comments("lib/friendly_id/slugged.rb")
53
- buffer << read_comments("lib/friendly_id/history.rb")
54
- buffer << read_comments("lib/friendly_id/scoped.rb")
55
- buffer << read_comments("lib/friendly_id/simple_i18n.rb")
56
- buffer << read_comments("lib/friendly_id/reserved.rb")
57
-
58
- File.open("Guide.md", "w") do |file|
59
- file.write(buffer.join("\n"))
60
- end
41
+ load File.expand_path('../guide.rb', __FILE__)
61
42
  end
62
43
 
63
44
  namespace :test do
@@ -110,3 +91,7 @@ namespace :db do
110
91
  end
111
92
 
112
93
  task :doc => :yard
94
+
95
+ task :docs do
96
+ sh %{git checkout gh-pages && rake doc && git checkout @{-1}}
97
+ end
data/guide.rb ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This script generates the Guide.md file included in the Yard docs.
4
+
5
+ def comments_from path
6
+ path = File.expand_path("../lib/friendly_id/#{path}", __FILE__)
7
+ match = File.read(path).match(/\n=begin(.*)\n=end/m)[1].to_s
8
+ match.split("\n").reject {|x| x =~ /^@/}.join("\n").strip
9
+ end
10
+
11
+ File.open(File.expand_path('../Guide.md', __FILE__), 'w:utf-8') do |guide|
12
+ ['../friendly_id.rb', 'base.rb', 'finders.rb', 'slugged.rb', 'history.rb',
13
+ 'scoped.rb', 'simple_i18n.rb', 'reserved.rb'].each do |file|
14
+ guide.write comments_from file
15
+ guide.write "\n"
16
+ end
17
+ end
data/lib/friendly_id.rb CHANGED
@@ -50,6 +50,19 @@ module FriendlyId
50
50
  autoload :Slugged, "friendly_id/slugged"
51
51
  autoload :Finders, "friendly_id/finders"
52
52
 
53
+ # Instances of these classes will never be considered a friendly id.
54
+ # @see FriendlyId::ObjectUtils#friendly_id
55
+ UNFRIENDLY_CLASSES = [
56
+ ActiveRecord::Base,
57
+ Array,
58
+ FalseClass,
59
+ Hash,
60
+ NilClass,
61
+ Numeric,
62
+ Symbol,
63
+ TrueClass
64
+ ]
65
+
53
66
  # FriendlyId takes advantage of `extended` to do basic model setup, primarily
54
67
  # extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
55
68
  # friendly_id} as a class method.
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # To learn more, check out the guide:
9
9
  #
10
- # http://rubydoc.info/github/norman/friendly_id/master/file/Guide.md
10
+ # http://norman.github.io/friendly_id/file.Guide.html
11
11
 
12
12
  FriendlyId.defaults do |config|
13
13
  # ## Reserved Words
@@ -29,10 +29,7 @@ module FriendlyId
29
29
  def friendly_id?
30
30
  # Considered unfriendly if this is an instance of an unfriendly class or
31
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}
32
+ if FriendlyId::UNFRIENDLY_CLASSES.detect {|klass| self.class <= klass}
36
33
  false
37
34
  elsif respond_to?(:to_i) && to_i.to_s != to_s
38
35
  true
@@ -4,7 +4,7 @@ module FriendlyId
4
4
 
5
5
  ## Reserved Words
6
6
 
7
- The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
7
+ The {FriendlyId::Reserved Reserved} module adds the ability to exclude a list of
8
8
  words from use as FriendlyId slugs.
9
9
 
10
10
  With Ruby on Rails, FriendlyId's generator generates an initializer that
@@ -17,16 +17,16 @@ it will have no field to highlight. If you'd like to change this so that
17
17
  scaffolding works as expected, one way to accomplish this is to move the error
18
18
  message to a different field. For example:
19
19
 
20
- class Person < ActiveRecord::Base
21
- extend FriendlyId
22
- friendly_id :name, use: :slugged
20
+ class Person < ActiveRecord::Base
21
+ extend FriendlyId
22
+ friendly_id :name, use: :slugged
23
23
 
24
- after_validation :move_friendly_id_error_to_name
24
+ after_validation :move_friendly_id_error_to_name
25
25
 
26
- def move_friendly_id_error_to_name
27
- errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
26
+ def move_friendly_id_error_to_name
27
+ errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
28
+ end
28
29
  end
29
- end
30
30
 
31
31
  =end
32
32
  module Reserved
@@ -37,7 +37,7 @@ friendly_id_globalize gem instead.
37
37
  Finds will take into consideration the current locale:
38
38
 
39
39
  I18n.locale = :es
40
- Post.find("la-guerra-de-las-galaxas")
40
+ Post.find("la-guerra-de-las-galaxias")
41
41
  I18n.locale = :en
42
42
  Post.find("star-wars")
43
43
 
@@ -45,7 +45,7 @@ To find a slug by an explicit locale, perform the find inside a block
45
45
  passed to I18n's `with_locale` method:
46
46
 
47
47
  I18n.with_locale(:es) do
48
- Post.find("la-guerra-de-las-galaxas")
48
+ Post.find("la-guerra-de-las-galaxias")
49
49
  end
50
50
 
51
51
  ### Creating Records
@@ -59,13 +59,13 @@ To translate an existing record's friendly_id, use
59
59
  you add is properly escaped, transliterated and sequenced:
60
60
 
61
61
  post = Post.create :name => "Star Wars"
62
- post.set_friendly_id("La guerra de las galaxas", :es)
62
+ post.set_friendly_id("La guerra de las galaxias", :es)
63
63
 
64
64
  If you don't pass in a locale argument, FriendlyId::SimpleI18n will just use the
65
65
  current locale:
66
66
 
67
67
  I18n.with_locale(:es) do
68
- post.set_friendly_id("La guerra de las galaxas")
68
+ post.set_friendly_id("La guerra de las galaxias")
69
69
  end
70
70
  =end
71
71
  module SimpleI18n
@@ -1,3 +1,3 @@
1
1
  module FriendlyId
2
- VERSION = "5.0.0.beta4"
2
+ VERSION = "5.0.0.rc1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: friendly_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta4
4
+ version: 5.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Norman Clarke
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-21 00:00:00.000000000 Z
12
+ date: 2013-08-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -155,13 +155,13 @@ files:
155
155
  - CONTRIBUTING.md
156
156
  - Changelog.md
157
157
  - Gemfile
158
- - Guide.md
159
158
  - MIT-LICENSE
160
159
  - README.md
161
160
  - Rakefile
162
161
  - bench.rb
163
162
  - friendly_id.gemspec
164
163
  - gemfiles/Gemfile.rails-4.0.rb
164
+ - guide.rb
165
165
  - lib/friendly_id.rb
166
166
  - lib/friendly_id/.gitattributes
167
167
  - lib/friendly_id/base.rb
@@ -182,11 +182,6 @@ files:
182
182
  - lib/friendly_id/version.rb
183
183
  - lib/generators/friendly_id_generator.rb
184
184
  - test/base_test.rb
185
- - test/compatibility/ancestry/Gemfile
186
- - test/compatibility/ancestry/ancestry_test.rb
187
- - test/compatibility/threading/Gemfile
188
- - test/compatibility/threading/Gemfile.lock
189
- - test/compatibility/threading/threading.rb
190
185
  - test/configuration_test.rb
191
186
  - test/core_test.rb
192
187
  - test/databases.yml
data/Guide.md DELETED
@@ -1,597 +0,0 @@
1
-
2
-
3
- ## About FriendlyId
4
-
5
- FriendlyId is an add-on to Ruby's Active Record that allows you to replace ids
6
- in your URLs with strings:
7
-
8
- # without FriendlyId
9
- http://example.com/states/4323454
10
-
11
- # with FriendlyId
12
- http://example.com/states/washington
13
-
14
- It requires few changes to your application code and offers flexibility,
15
- performance and a well-documented codebase.
16
-
17
- ### Core Concepts
18
-
19
- #### Slugs
20
-
21
- The concept of *slugs* is at the heart of FriendlyId.
22
-
23
- A slug is the part of a URL which identifies a page using human-readable
24
- keywords, rather than an opaque identifier such as a numeric id. This can make
25
- your application more friendly both for users and search engine.
26
-
27
- #### Finders: Slugs Act Like Numeric IDs
28
-
29
- To the extent possible, FriendlyId lets you treat text-based identifiers like
30
- normal IDs. This means that you can perform finds with slugs just like you do
31
- with numeric ids:
32
-
33
- Person.find(82542335)
34
- Person.friendly.find("joe")
35
-
36
-
37
- ## Setting Up FriendlyId in Your Model
38
-
39
- To use FriendlyId in your ActiveRecord models, you must first either extend or
40
- include the FriendlyId module (it makes no difference), then invoke the
41
- {FriendlyId::Base#friendly_id friendly_id} method to configure your desired
42
- options:
43
-
44
- class Foo < ActiveRecord::Base
45
- include FriendlyId
46
- friendly_id :bar, :use => [:slugged, :simple_i18n]
47
- end
48
-
49
- The most important option is `:use`, which you use to tell FriendlyId which
50
- addons it should use. See the documentation for this method for a list of all
51
- available addons, or skim through the rest of the docs to get a high-level
52
- overview.
53
-
54
- ### The Default Setup: Simple Models
55
-
56
- The simplest way to use FriendlyId is with a model that has a uniquely indexed
57
- column with no spaces or special characters, and that is seldom or never
58
- updated. The most common example of this is a user name:
59
-
60
- class User < ActiveRecord::Base
61
- extend FriendlyId
62
- friendly_id :login
63
- validates_format_of :login, :with => /\A[a-z0-9]+\z/i
64
- end
65
-
66
- @user = User.friendly.find "joe" # the old User.find(1) still works, too
67
- @user.to_param # returns "joe"
68
- redirect_to @user # the URL will be /users/joe
69
-
70
- In this case, FriendlyId assumes you want to use the column as-is; it will never
71
- modify the value of the column, and your application should ensure that the
72
- value is unique and admissible in a URL:
73
-
74
- class City < ActiveRecord::Base
75
- extend FriendlyId
76
- friendly_id :name
77
- end
78
-
79
- @city.friendly.find "Viña del Mar"
80
- redirect_to @city # the URL will be /cities/Viña%20del%20Mar
81
-
82
- Writing the code to process an arbitrary string into a good identifier for use
83
- in a URL can be repetitive and surprisingly tricky, so for this reason it's
84
- often better and easier to use {FriendlyId::Slugged slugs}.
85
-
86
- ## Performing Finds with FriendlyId
87
-
88
- FriendlyId offers enhanced finders which will search for your record by
89
- friendly id, and fall back to the numeric id if necessary. This makes it easy
90
- to add FriendlyId to an existing application with minimal code modification.
91
-
92
- By default, these methods are available only on the `friendly` scope:
93
-
94
- Restaurant.friendly.find('plaza-diner') #=> works
95
- Restaurant.friendly.find(23) #=> also works
96
- Restaurant.find(23) #=> still works
97
- Restaurant.find('plaza-diner') #=> will not work
98
-
99
- ### Restoring FriendlyId 4.0-style finders
100
-
101
- Prior to version 5.0, FriendlyId overrode the default finder methods to perform
102
- friendly finds all the time. This required modifying parts of Rails that did
103
- not have a public API, which was harder to maintain and at times caused
104
- compatiblity problems. In 5.0 we decided change the library's defaults and add
105
- the friendly finder methods only to the `friendly` scope in order to boost
106
- compatiblity. However, you can still opt-in to original functionality very
107
- easily by using the `:finders` addon:
108
-
109
- class Restaurant < ActiveRecord::Base
110
- extend FriendlyId
111
-
112
- scope :active, -> {where(:active => true)}
113
-
114
- friendly_id :name, :use => [:slugged, :finders]
115
- end
116
-
117
- Restaurant.friendly.find('plaza-diner') #=> works
118
- Restaurant.find('plaza-diner') #=> now also works
119
- Restaurant.active.find('plaza-diner') #=> now also works
120
-
121
- ### Updating your application to use FriendlyId's finders
122
-
123
- Unless you've chosen to use the `:finders` addon, be sure to modify the finders
124
- in your controllers to use the `friendly` scope. For example:
125
-
126
- # before
127
- def set_restaurant
128
- @restaurant = Restaurant.find(params[:id])
129
- end
130
-
131
- # after
132
- def set_restaurant
133
- @restaurant = Restaurant.friendly.find(params[:id])
134
- end
135
-
136
-
137
- ## Slugged Models
138
-
139
- FriendlyId can use a separate column to store slugs for models which require
140
- some text processing.
141
-
142
- For example, blog applications typically use a post title to provide the basis
143
- of a search engine friendly URL. Such identifiers typically lack uppercase
144
- characters, use ASCII to approximate UTF-8 character, and strip out other
145
- characters which may make them aesthetically unappealing or error-prone when
146
- used in a URL.
147
-
148
- class Post < ActiveRecord::Base
149
- extend FriendlyId
150
- friendly_id :title, :use => :slugged
151
- end
152
-
153
- @post = Post.create(:title => "This is the first post!")
154
- @post.friendly_id # returns "this-is-the-first-post"
155
- redirect_to @post # the URL will be /posts/this-is-the-first-post
156
-
157
- In general, use slugs by default unless you know for sure you don't need them.
158
- To activate the slugging functionality, use the {FriendlyId::Slugged} module.
159
-
160
- FriendlyId will generate slugs from a method or column that you specify, and
161
- store them in a field in your model. By default, this field must be named
162
- `:slug`, though you may change this using the
163
- {FriendlyId::Slugged::Configuration#slug_column slug_column} configuration
164
- option. You should add an index to this column, and in most cases, make it
165
- unique. You may also wish to constrain it to NOT NULL, but this depends on your
166
- app's behavior and requirements.
167
-
168
- ### Example Setup
169
-
170
- # your model
171
- class Post < ActiveRecord::Base
172
- extend FriendlyId
173
- friendly_id :title, :use => :slugged
174
- validates_presence_of :title, :slug, :body
175
- end
176
-
177
- # a migration
178
- class CreatePosts < ActiveRecord::Migration
179
- def self.up
180
- create_table :posts do |t|
181
- t.string :title, :null => false
182
- t.string :slug, :null => false
183
- t.text :body
184
- end
185
-
186
- add_index :posts, :slug, :unique => true
187
- end
188
-
189
- def self.down
190
- drop_table :posts
191
- end
192
- end
193
-
194
- ### Working With Slugs
195
-
196
- #### Formatting
197
-
198
- By default, FriendlyId uses Active Support's
199
- [paramaterize](http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize)
200
- method to create slugs. This method will intelligently replace spaces with
201
- dashes, and Unicode Latin characters with ASCII approximations:
202
-
203
- movie = Movie.create! :title => "Der Preis fürs Überleben"
204
- movie.slug #=> "der-preis-furs-uberleben"
205
-
206
- #### Column or Method?
207
-
208
- FriendlyId always uses a method as the basis of the slug text - not a column. It
209
- first glance, this may sound confusing, but remember that Active Record provides
210
- methods for each column in a model's associated table, and that's what
211
- FriendlyId uses.
212
-
213
- Here's an example of a class that uses a custom method to generate the slug:
214
-
215
- class Person < ActiveRecord::Base
216
- friendly_id :name_and_location
217
- def name_and_location
218
- "#{name} from #{location}"
219
- end
220
- end
221
-
222
- bob = Person.create! :name => "Bob Smith", :location => "New York City"
223
- bob.friendly_id #=> "bob-smith-from-new-york-city"
224
-
225
- FriendlyId refers to this internally as the "base" method.
226
-
227
- #### Uniqueness
228
-
229
- When you try to insert a record that would generate a duplicate friendly id,
230
- FriendlyId will append a UUID to the generated slug to ensure uniqueness:
231
-
232
- car = Car.create :title => "Peugot 206"
233
- car2 = Car.create :title => "Peugot 206"
234
-
235
- car.friendly_id #=> "peugot-206"
236
- car2.friendly_id #=> "peugot-206-f9f3789a-daec-4156-af1d-fab81aa16ee5"
237
-
238
- Previous versions of FriendlyId appended a numeric sequence a to make slugs
239
- unique, but this was removed to simplify using FriendlyId in concurrent code.
240
-
241
- #### Candidates
242
-
243
- Since UUIDs are ugly, FriendlyId provides a "slug candidates" functionality to
244
- let you specify alternate slugs to use in the event the one you want to use is
245
- already taken. For example:
246
-
247
- class Restaurant < ActiveRecord::Base
248
- extend FriendlyId
249
- friendly_id :slug_candidates, use: :slugged
250
-
251
- # Try building a slug based on the following fields in
252
- # increasing order of specificity.
253
- def slug_candidates
254
- [
255
- :name,
256
- [:name, :city],
257
- [:name, :street, :city],
258
- [:name, :street_number, :street, :city]
259
- ]
260
- end
261
- end
262
-
263
- r1 = Restaurant.create! name: 'Plaza Diner', city: 'New Paltz'
264
- r2 = Restaurant.create! name: 'Plaza Diner', city: 'Kingston'
265
-
266
- r1.friendly_id #=> 'plaza-diner'
267
- r2.friendly_id #=> 'plaza-diner-kingston'
268
-
269
- To use candidates, make your FriendlyId base method return an array. The
270
- method need not be named `slug_candidates`; it can be anything you want. The
271
- array may contain any combination of symbols, strings, procs or lambdas and
272
- will be evaluated lazily and in order. If you include symbols, FriendlyId will
273
- invoke a method on your model class with the same name. Strings will be
274
- interpreted literally. Procs and lambdas will be called and their return values
275
- used as the basis of the friendly id. If none of the candidates can generate a
276
- unique slug, then FriendlyId will append a UUID to the first candidate as a
277
- last resort.
278
-
279
- #### Sequence Separator
280
-
281
- By default, FriendlyId uses a dash to separate the slug from a sequence.
282
-
283
- You can change this with the {FriendlyId::Slugged::Configuration#sequence_separator
284
- sequence_separator} configuration option.
285
-
286
- #### Providing Your Own Slug Processing Method
287
-
288
- You can override {FriendlyId::Slugged#normalize_friendly_id} in your model for
289
- total control over the slug format. It will be invoked for any generated slug,
290
- whether for a single slug or for slug candidates.
291
-
292
- #### Deciding When to Generate New Slugs
293
-
294
- As of FriendlyId 5.0, slugs are only generated when the `slug` field is nil. If
295
- you want a slug to be regenerated,set the slug field to nil:
296
-
297
- restaurant.friendly_id # joes-diner
298
- restaurant.name = "The Plaza Diner"
299
- restaurant.save!
300
- restaurant.friendly_id # joes-diner
301
- restaurant.slug = nil
302
- restaurant.save!
303
- restaurant.friendly_id # the-plaza-diner
304
-
305
- You can also override the
306
- {FriendlyId::Slugged#should_generate_new_friendly_id?} method, which lets you
307
- control exactly when new friendly ids are set:
308
-
309
- class Post < ActiveRecord::Base
310
- extend FriendlyId
311
- friendly_id :title, :use => :slugged
312
-
313
- def should_generate_new_friendly_id?
314
- title_changed?
315
- end
316
- end
317
-
318
- #### Locale-specific Transliterations
319
-
320
- Active Support's `parameterize` uses
321
- [transliterate](http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate),
322
- which in turn can use I18n's transliteration rules to consider the current
323
- locale when replacing Latin characters:
324
-
325
- # config/locales/de.yml
326
- de:
327
- i18n:
328
- transliterate:
329
- rule:
330
- ü: "ue"
331
- ö: "oe"
332
- etc...
333
-
334
- movie = Movie.create! :title => "Der Preis fürs Überleben"
335
- movie.slug #=> "der-preis-fuers-ueberleben"
336
-
337
- This functionality was in fact taken from earlier versions of FriendlyId.
338
-
339
- #### Gotchas: Common Problems
340
-
341
- FriendlyId uses a before_validation callback to generate and set the slug. This
342
- means that if you create two model instances before saving them, it's possible
343
- they will generate the same slug, and the second save will fail.
344
-
345
- This can happen in two fairly normal cases: the first, when a model using nested
346
- attributes creates more than one record for a model that uses friendly_id. The
347
- second, in concurrent code, either in threads or multiple processes.
348
-
349
- To solve the nested attributes issue, I recommend simply avoiding them when
350
- creating more than one nested record for a model that uses FriendlyId. See [this
351
- Github issue](https://github.com/norman/friendly_id/issues/185) for discussion.
352
-
353
-
354
- ## History: Avoiding 404's When Slugs Change
355
-
356
- FriendlyId's {FriendlyId::History History} module adds the ability to store a
357
- log of a model's slugs, so that when its friendly id changes, it's still
358
- possible to perform finds by the old id.
359
-
360
- The primary use case for this is avoiding broken URLs.
361
-
362
- ### Setup
363
-
364
- In order to use this module, you must add a table to your database schema to
365
- store the slug records. FriendlyId provides a generator for this purpose:
366
-
367
- rails generate friendly_id
368
- rake db:migrate
369
-
370
- This will add a table named `friendly_id_slugs`, used by the {FriendlyId::Slug}
371
- model.
372
-
373
- ### Considerations
374
-
375
- Because recording slug history requires creating additional database records,
376
- this module has an impact on the performance of the associated model's `create`
377
- method.
378
-
379
- ### Example
380
-
381
- class Post < ActiveRecord::Base
382
- extend FriendlyId
383
- friendly_id :title, :use => :history
384
- end
385
-
386
- class PostsController < ApplicationController
387
-
388
- before_filter :find_post
389
-
390
- ...
391
-
392
- def find_post
393
- @post = Post.find params[:id]
394
-
395
- # If an old id or a numeric id was used to find the record, then
396
- # the request path will not match the post_path, and we should do
397
- # a 301 redirect that uses the current friendly id.
398
- if request.path != post_path(@post)
399
- return redirect_to @post, :status => :moved_permanently
400
- end
401
- end
402
- end
403
-
404
-
405
- ## Unique Slugs by Scope
406
-
407
- The {FriendlyId::Scoped} module allows FriendlyId to generate unique slugs
408
- within a scope.
409
-
410
- This allows, for example, two restaurants in different cities to have the slug
411
- `joes-diner`:
412
-
413
- class Restaurant < ActiveRecord::Base
414
- extend FriendlyId
415
- belongs_to :city
416
- friendly_id :name, :use => :scoped, :scope => :city
417
- end
418
-
419
- class City < ActiveRecord::Base
420
- extend FriendlyId
421
- has_many :restaurants
422
- friendly_id :name, :use => :slugged
423
- end
424
-
425
- City.friendly.find("seattle").restaurants.friendly.find("joes-diner")
426
- City.friendly.find("chicago").restaurants.friendly.find("joes-diner")
427
-
428
- Without :scoped in this case, one of the restaurants would have the slug
429
- `joes-diner` and the other would have `joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5`.
430
-
431
- The value for the `:scope` option can be the name of a `belongs_to` relation, or
432
- a column.
433
-
434
- Additionally, the `:scope` option can receive an array of scope values:
435
-
436
- class Cuisine < ActiveRecord::Base
437
- extend FriendlyId
438
- has_many :restaurants
439
- friendly_id :name, :use => :slugged
440
- end
441
-
442
- class City < ActiveRecord::Base
443
- extend FriendlyId
444
- has_many :restaurants
445
- friendly_id :name, :use => :slugged
446
- end
447
-
448
- class Restaurant < ActiveRecord::Base
449
- extend FriendlyId
450
- belongs_to :city
451
- friendly_id :name, :use => :scoped, :scope => [:city, :cuisine]
452
- end
453
-
454
- All supplied values will be used to determine scope.
455
-
456
- ### Finding Records by Friendly ID
457
-
458
- If you are using scopes your friendly ids may not be unique, so a simple find
459
- like
460
-
461
- Restaurant.friendly.find("joes-diner")
462
-
463
- may return the wrong record. In these cases it's best to query through the
464
- relation:
465
-
466
- @city.restaurants.friendly.find("joes-diner")
467
-
468
- Alternatively, you could pass the scope value as a query parameter:
469
-
470
- Restaurant.friendly.find("joes-diner").where(:city_id => @city.id)
471
-
472
-
473
- ### Finding All Records That Match a Scoped ID
474
-
475
- Query the slug column directly:
476
-
477
- Restaurant.where(:slug => "joes-diner")
478
-
479
- ### Routes for Scoped Models
480
-
481
- Recall that FriendlyId is a database-centric library, and does not set up any
482
- routes for scoped models. You must do this yourself in your application. Here's
483
- an example of one way to set this up:
484
-
485
- # in routes.rb
486
- resources :cities do
487
- resources :restaurants
488
- end
489
-
490
- # in views
491
- <%= link_to 'Show', [@city, @restaurant] %>
492
-
493
- # in controllers
494
- @city = City.friendly.find(params[:city_id])
495
- @restaurant = @city.restaurants.friendly.find(params[:id])
496
-
497
- # URLs:
498
- http://example.org/cities/seattle/restaurants/joes-diner
499
- http://example.org/cities/chicago/restaurants/joes-diner
500
-
501
-
502
- ## Translating Slugs Using Simple I18n
503
-
504
- The {FriendlyId::SimpleI18n SimpleI18n} module adds very basic i18n support to
505
- FriendlyId.
506
-
507
- In order to use this module, your model must have a slug column for each locale.
508
- By default FriendlyId looks for columns named, for example, "slug_en",
509
- "slug_es", etc. The first part of the name can be configured by passing the
510
- `:slug_column` option if you choose. Note that the column for the default locale
511
- must also include the locale in its name.
512
-
513
- This module is most suitable to applications that need to support few locales.
514
- If you need to support two or more locales, you may wish to use the
515
- friendly_id_globalize gem instead.
516
-
517
- ### Example migration
518
-
519
- def self.up
520
- create_table :posts do |t|
521
- t.string :title
522
- t.string :slug_en
523
- t.string :slug_es
524
- t.text :body
525
- end
526
- add_index :posts, :slug_en
527
- add_index :posts, :slug_es
528
- end
529
-
530
- ### Finds
531
-
532
- Finds will take into consideration the current locale:
533
-
534
- I18n.locale = :es
535
- Post.find("la-guerra-de-las-galaxas")
536
- I18n.locale = :en
537
- Post.find("star-wars")
538
-
539
- To find a slug by an explicit locale, perform the find inside a block
540
- passed to I18n's `with_locale` method:
541
-
542
- I18n.with_locale(:es) do
543
- Post.find("la-guerra-de-las-galaxas")
544
- end
545
-
546
- ### Creating Records
547
-
548
- When new records are created, the slug is generated for the current locale only.
549
-
550
- ### Translating Slugs
551
-
552
- To translate an existing record's friendly_id, use
553
- {FriendlyId::SimpleI18n::Model#set_friendly_id}. This will ensure that the slug
554
- you add is properly escaped, transliterated and sequenced:
555
-
556
- post = Post.create :name => "Star Wars"
557
- post.set_friendly_id("La guerra de las galaxas", :es)
558
-
559
- If you don't pass in a locale argument, FriendlyId::SimpleI18n will just use the
560
- current locale:
561
-
562
- I18n.with_locale(:es) do
563
- post.set_friendly_id("La guerra de las galaxas")
564
- end
565
-
566
-
567
- ## Reserved Words
568
-
569
- The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
570
- words from use as FriendlyId slugs.
571
-
572
- By default, FriendlyId reserves the words "new" and "edit" when this module is
573
- included. You can configure this globally by using {FriendlyId.defaults
574
- FriendlyId.defaults}:
575
-
576
- FriendlyId.defaults do |config|
577
- config.use :reserved
578
- # Reserve words for English and Spanish URLs
579
- config.reserved_words = %w(new edit nueva nuevo editar)
580
- end
581
-
582
- Note that the error message will appear on the field `:friendly_id`. If you are
583
- using Rails's scaffolded form errors display, then it will have no field to
584
- highlight. If you'd like to change this so that scaffolding works as expected,
585
- one way to accomplish this is to move the error message to a different field.
586
- For example:
587
-
588
- class Person < ActiveRecord::Base
589
- extend FriendlyId
590
- friendly_id :name, use: :slugged
591
-
592
- after_validation :move_friendly_id_error_to_name
593
-
594
- def move_friendly_id_error_to_name
595
- errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
596
- end
597
- end
@@ -1,8 +0,0 @@
1
- source :rubygems
2
-
3
- gem "sqlite3", "~> 1.3.4"
4
- gem "activerecord", "3.0.10"
5
- gem "minitest", "~> 2.4.0"
6
- gem "mocha", "~> 0.9.12"
7
- gem "ancestry"
8
- gem "rake"
@@ -1,34 +0,0 @@
1
- require File.expand_path("../../../helper", __FILE__)
2
-
3
- require "ancestry"
4
-
5
- ActiveRecord::Migration.create_table("things") do |t|
6
- t.string :name
7
- t.string :slug
8
- t.string :ancestry
9
- end
10
- ActiveRecord::Migration.add_index :things, :ancestry
11
-
12
- class Thing < ActiveRecord::Base
13
- extend FriendlyId
14
- friendly_id do |config|
15
- config.use :slugged
16
- config.use :scoped
17
- config.base = :name
18
- config.scope = :ancestry
19
- end
20
- has_ancestry
21
- end
22
-
23
- class AncestryTest < MiniTest::Unit::TestCase
24
- include FriendlyId::Test
25
-
26
- test "should sequence slugs when scoped by ancestry" do
27
- 3.times.inject([]) do |memo, _|
28
- memo << Thing.create!(:name => "a", :parent => memo.last)
29
- end.each do |thing|
30
- assert_equal "a", thing.friendly_id
31
- end
32
- end
33
- end
34
-
@@ -1,8 +0,0 @@
1
- source :rubygems
2
-
3
- gem "pg"
4
- gem "mysql2"
5
- gem "mocha"
6
- gem "activerecord", "3.1.1"
7
- gem "sqlite3"
8
- gem "fatalistic"
@@ -1,45 +0,0 @@
1
- ENV["DB"] = "postgres"
2
-
3
- require "thread"
4
- require File.expand_path("../../../helper", __FILE__)
5
- require "active_record/locking/fatalistic"
6
-
7
- ActiveRecord::Migration.tap do |m|
8
- m.drop_table "things"
9
- m.create_table("things") do |t|
10
- t.string :name
11
- t.string :slug
12
- end
13
- m.add_index :things, :slug, :unique => true
14
- end
15
-
16
- class Thing < ActiveRecord::Base
17
- extend FriendlyId
18
- friendly_id :name, :use => :slugged
19
- end
20
-
21
- $things = 10.times.map do
22
- Thing.new :name => "a b c"
23
- end
24
-
25
- $mutex = Mutex.new
26
-
27
- def save_thing
28
- thing = $mutex.synchronize do
29
- $things.pop
30
- end
31
- if thing.nil? then return end
32
- Thing.lock do
33
- thing.save!
34
- print "#{thing.friendly_id}\n"
35
- end
36
- true
37
- end
38
-
39
- 2.times.map do
40
- Thread.new do
41
- while true do
42
- break unless save_thing
43
- end
44
- end
45
- end.map(&:value)