mongoid-slug 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +20 -20
  3. data/README.md +392 -336
  4. data/lib/mongoid/slug/criteria.rb +111 -107
  5. data/lib/mongoid/slug/{index.rb → index_builder.rb} +69 -45
  6. data/lib/mongoid/slug/railtie.rb +11 -9
  7. data/lib/mongoid/slug/slug_id_strategy.rb +5 -3
  8. data/lib/mongoid/slug/unique_slug.rb +172 -173
  9. data/lib/mongoid/slug/version.rb +7 -5
  10. data/lib/mongoid/slug.rb +333 -328
  11. data/lib/mongoid_slug.rb +4 -2
  12. data/lib/tasks/mongoid_slug.rake +17 -19
  13. metadata +13 -173
  14. data/spec/models/alias.rb +0 -6
  15. data/spec/models/article.rb +0 -9
  16. data/spec/models/artist.rb +0 -8
  17. data/spec/models/artwork.rb +0 -10
  18. data/spec/models/author.rb +0 -15
  19. data/spec/models/author_polymorphic.rb +0 -15
  20. data/spec/models/book.rb +0 -12
  21. data/spec/models/book_polymorphic.rb +0 -12
  22. data/spec/models/caption.rb +0 -17
  23. data/spec/models/entity.rb +0 -11
  24. data/spec/models/friend.rb +0 -7
  25. data/spec/models/incorrect_slug_persistence.rb +0 -9
  26. data/spec/models/integer_id.rb +0 -9
  27. data/spec/models/magazine.rb +0 -7
  28. data/spec/models/page.rb +0 -9
  29. data/spec/models/page_localize.rb +0 -9
  30. data/spec/models/page_slug_localized.rb +0 -9
  31. data/spec/models/page_slug_localized_custom.rb +0 -10
  32. data/spec/models/page_slug_localized_history.rb +0 -9
  33. data/spec/models/partner.rb +0 -7
  34. data/spec/models/person.rb +0 -12
  35. data/spec/models/relationship.rb +0 -8
  36. data/spec/models/string_id.rb +0 -9
  37. data/spec/models/subject.rb +0 -7
  38. data/spec/models/without_slug.rb +0 -5
  39. data/spec/mongoid/criteria_spec.rb +0 -207
  40. data/spec/mongoid/index_spec.rb +0 -33
  41. data/spec/mongoid/slug_spec.rb +0 -1169
  42. data/spec/shared/indexes.rb +0 -41
  43. data/spec/spec_helper.rb +0 -61
  44. data/spec/tasks/mongoid_slug_rake_spec.rb +0 -73
data/README.md CHANGED
@@ -1,336 +1,392 @@
1
- Mongoid Slug
2
- ============
3
-
4
- Mongoid Slug generates a URL slug or permalink based on one or more fields in a Mongoid model. It sits idly on top of [stringex](https://github.com/rsl/stringex), supporting non-Latin characters.
5
-
6
- [![Build Status](https://secure.travis-ci.org/mongoid/mongoid-slug.svg)](http://travis-ci.org/mongoid/mongoid-slug)
7
- [![Gem Version](https://badge.fury.io/rb/mongoid-slug.svg)](http://badge.fury.io/rb/mongoid-slug)
8
- [![Dependency Status](https://gemnasium.com/mongoid/mongoid-slug.svg)](https://gemnasium.com/mongoid/mongoid-slug)
9
- [![Code Climate](https://codeclimate.com/github/mongoid/mongoid-slug.svg)](https://codeclimate.com/github/mongoid/mongoid-slug)
10
-
11
- ### Installation
12
-
13
- Add to your Gemfile:
14
-
15
- ```ruby
16
- gem 'mongoid-slug'
17
- ```
18
-
19
- ### Usage
20
-
21
- ### Set Up a Slug
22
-
23
- ```ruby
24
- class Book
25
- include Mongoid::Document
26
- include Mongoid::Slug
27
-
28
- field :title
29
- slug :title
30
- end
31
- ```
32
-
33
- ### Find a Document by its Slug
34
-
35
- ```ruby
36
- # GET /books/a-thousand-plateaus
37
- book = Book.find params[:book_id]
38
- ```
39
-
40
- Mongoid Slug will attempt to determine whether you want to find using the `slugs` field or the `_id` field by inspecting the supplied parameters.
41
-
42
- * Mongoid Slug will perform a find based on `slugs` only if all arguments passed to `find` are of the type `String`.
43
- * If your document uses `BSON::ObjectId` identifiers, and all arguments look like valid `BSON::ObjectId`, then Mongoid Slug will perform a find based on `_id`.
44
- * If your document uses any other type of identifiers, and all arguments passed to `find` are of the same type, then Mongoid Slug will perform a find based on `_id`.
45
- * If your document uses `String` identifiers and you want to be able find by slugs or ids, to get the correct behaviour, you should add a `slug_id_strategy` option to your `_id` field definition. This option should return something that responds to `call` (a callable) and takes one string argument, e.g. a lambda. This callable must return true if the string looks like one of your ids.
46
-
47
- ```ruby
48
- Book.fields['_id'].type
49
- => String
50
- book = Book.find 'a-thousand-plateaus' # Finds by slugs
51
- => ...
52
-
53
- class Post
54
- include Mongoid::Document
55
- include Mongoid::Slug
56
-
57
- field :_id, type: String, slug_id_strategy: lambda { |id| id.start_with?('...') }
58
-
59
- field :name
60
- slug :name, history: true
61
- end
62
-
63
- Post.fields['_id'].type
64
- => String
65
- post = Post.find 'a-thousand-plateaus' # Finds by slugs
66
- => ...
67
- post = Post.find '50b1386a0482939864000001' # Finds by bson ids
68
- => ...
69
- ```
70
- [Examine slug.rb](lib/mongoid/slug.rb) for all available options.
71
-
72
- ### Updating Existing Records
73
-
74
- To set slugs for existing records run following rake task:
75
-
76
- ```ruby
77
- rake mongoid_slug:set
78
- ```
79
-
80
- You can pass model names as an option for which you want to set slugs:
81
-
82
- ```ruby
83
- rake mongoid_slug:set[Model1,Model2]
84
- ```
85
-
86
- ### Nil Slugs
87
-
88
- Empty slugs are possible and generate a `nil` value for the `_slugs` field. In the `Post` example above, a blank post `name` will cause the document record not to contain a `_slugs` field in the database. The default `_slugs` index is `sparse`, allowing that. If you wish to change this behavior add a custom `validates_presence_of :_slugs` validator to the document or change the database index to `sparse: false`.
89
-
90
- ### Custom Slug Generation
91
-
92
- By default Mongoid Slug generates slugs with stringex. If this is not desired you can define your own slug generator.
93
-
94
- There are two ways to define slug generator.
95
-
96
- #### Globally
97
-
98
- Configure a block in `config/initializers/mongoid_slug.rb` as follows:
99
-
100
- ```ruby
101
- Mongoid::Slug.configure do |c|
102
- # create a block that takes the current object as an argument and return the slug
103
- c.slug = proc { |cur_obj|
104
- cur_object.slug_builder.to_url
105
- }
106
- end
107
- ```
108
-
109
- #### On Model
110
-
111
- ```ruby
112
- class Caption
113
- include Mongoid::Document
114
- include Mongoid::Slug
115
-
116
- # create a block that takes the current object as an argument and returns the slug
117
- slug do |cur_object|
118
- cur_object.slug_builder.to_url
119
- end
120
- end
121
- ```
122
-
123
- The `to_url` method comes from [stringex](https://github.com/rsl/stringex).
124
-
125
- You can define a slug builder globally and/or override it per model.
126
-
127
- ### Scoping
128
-
129
- To scope a slug by a reference association, pass `:scope`:
130
-
131
- ```ruby
132
- class Company
133
- include Mongoid::Document
134
-
135
- references_many :employees
136
- end
137
-
138
- class Employee
139
- include Mongoid::Document
140
- include Mongoid::Slug
141
-
142
- field :name
143
- referenced_in :company
144
-
145
- slug :name, scope: :company
146
- end
147
- ```
148
-
149
- In this example, if you create an employee without associating it with any company, the scope will fall back to the root employees collection.
150
-
151
- Currently, if you have an irregular association name, you **must** specify the `:inverse_of` option on the other side of the assocation.
152
-
153
- Embedded objects are automatically scoped by their parent.
154
-
155
- Note that the unique index on the `Employee` collection in this example is derived from the `scope` value and is `{ _slugs: 1, company_id: 1}`. Therefore `:company` must be `referenced_in` above the definition of `slug` or it will not be able to resolve the association and mistakenly create a `{ _slugs: 1, company: 1}` index. An alternative is to scope to the field itself as follows:
156
-
157
- ```ruby
158
- class Employee
159
- include Mongoid::Document
160
- include Mongoid::Slug
161
-
162
- field :name
163
- field :company_id
164
-
165
- slug :name, scope: :company_id
166
- end
167
- ```
168
-
169
- ### Limit Slug Length
170
-
171
- MongoDB has a default limit around 1KB to the size of the index keys and will raise error 17280, `key too large to index` when trying to create a record that causes an index key to exceed that limit. By default slugs are of the form `text[-number]` and the text portion is limited in size to `Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32` bytes. You can change this limit with `max_length` or set it to `nil` if you're running MongoDB with [failIndexKeyTooLong](https://docs.mongodb.org/manual/reference/parameters/#param.failIndexKeyTooLong) set to `false`.
172
-
173
- ```ruby
174
- class Company
175
- include Mongoid::Document
176
- include Mongoid::Slug
177
-
178
- field :name
179
-
180
- slug :name, max_length: 24
181
- end
182
- ```
183
-
184
- ### Optionally Find and Create Slugs per Model Type
185
-
186
- By default when using STI, the scope will be around the super-class.
187
-
188
- ```ruby
189
- class Book
190
- include Mongoid::Document
191
- include Mongoid::Slug
192
- field :title
193
-
194
- slug :title, history: true
195
- embeds_many :subjects
196
- has_many :authors
197
- end
198
-
199
- class ComicBook < Book
200
- end
201
-
202
- book = Book.create(title: 'Anti Oedipus')
203
- comic_book = ComicBook.create(title: 'Anti Oedipus')
204
- comic_book.slugs.should_not eql(book.slugs)
205
- ```
206
-
207
- If you want the scope to be around the subclass, then set the option `by_model_type: true`.
208
-
209
- ```ruby
210
- class Book
211
- include Mongoid::Document
212
- include Mongoid::Slug
213
- field :title
214
-
215
- slug :title, history: true, by_model_type: true
216
- embeds_many :subjects
217
- has_many :authors
218
- end
219
-
220
- class ComicBook < Book
221
- end
222
-
223
- book = Book.create(title: 'Anti Oedipus')
224
- comic_book = ComicBook.create(title: 'Anti Oedipus')
225
- comic_book.slugs.should eql(book.slugs)
226
- ```
227
-
228
- ### History
229
-
230
- Enable slug history tracking by setting `history: true`.
231
-
232
- ```ruby
233
- class Page
234
- include Mongoid::Document
235
- include Mongoid::Slug
236
-
237
- field :title
238
-
239
- slug :title, history: true
240
- end
241
- ```
242
-
243
- The document will then be returned for any of the saved slugs:
244
-
245
- ```ruby
246
- page = Page.new title: "Home"
247
- page.save
248
- page.update_attributes title: "Welcome"
249
-
250
- Page.find("welcome") == Page.find("home") # => true
251
- ```
252
-
253
- ### Reserved Slugs
254
-
255
- Pass words you do not want to be slugged using the `reserve` option:
256
-
257
- ```ruby
258
- class Friend
259
- include Mongoid::Document
260
-
261
- field :name
262
- slug :name, reserve: ['admin', 'root']
263
- end
264
-
265
- friend = Friend.create name: 'admin'
266
- Friend.find('admin') # => nil
267
- friend.slug # => 'admin-1'
268
- ```
269
-
270
- When reserved words are not specified, the words 'new' and 'edit' are considered reserved by default.
271
- Specifying an array of custom reserved words will overwrite these defaults.
272
-
273
- ### Localize Slugs
274
-
275
- The slugs can be localized:
276
-
277
- ```ruby
278
- class PageSlugLocalize
279
- include Mongoid::Document
280
- include Mongoid::Slug
281
-
282
- field :title, localize: true
283
- slug :title, localize: true
284
- end
285
- ```
286
-
287
- This feature is built upon Mongoid localized fields, so fallbacks and localization works as documented in the Mongoid manual.
288
-
289
- ### Custom Find Strategies
290
-
291
- By default find will search for the document by the id field if the provided id looks like a `BSON::ObjectId`, and it will otherwise find by the _slugs field. However, custom strategies can ovveride the default behavior, like e.g:
292
-
293
- ```ruby
294
- module Mongoid::Slug::UuidIdStrategy
295
- def self.call id
296
- id =~ /\A([0-9a-fA-F]){8}-(([0-9a-fA-F]){4}-){3}([0-9a-fA-F]){12}\z/
297
- end
298
- end
299
- ```
300
-
301
- Use a custom strategy by adding the `slug_id_strategy` annotation to the `_id` field:
302
-
303
- ```ruby
304
- class Entity
305
- include Mongoid::Document
306
- include Mongoid::Slug
307
-
308
- field :_id, type: String, slug_id_strategy: UuidIdStrategy
309
-
310
- field :user_edited_variation
311
- slug :user_edited_variation, history: true
312
- end
313
- ```
314
-
315
- ### Adhoc Checking Whether a Slug is Unique
316
-
317
- Lets say you want to have a auto-suggest function on your GUI that could provide a preview of what the url or slug could be before the form to create the record was submitted.
318
-
319
- You can use the UniqueSlug class in your server side code to do this, e.g.
320
-
321
- ```ruby
322
- title = params[:title]
323
- unique = Mongoid::Slug::UniqueSlug.new(Book.new).find_unique(title)
324
- ...
325
- # return some representation of unique
326
- ```
327
-
328
- Contributing
329
- ------------
330
-
331
- Mongoid-slug is work of [many of contributors](https://github.com/mongoid/mongoid-slug/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/mongoid/mongoid-slug/pulls), [propose features, ask questions and discuss issues](https://github.com/mongoid/mongoid-slug/issues). See [CONTRIBUTING](CONTRIBUTING.md) for details.
332
-
333
- Copyright & License
334
- -------------------
335
-
336
- Copyright (c) 2010-2017 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.
1
+ Mongoid Slug
2
+ ============
3
+
4
+ Mongoid Slug generates a URL slug or permalink based on one or more fields in a Mongoid model.
5
+ It sits idly on top of [stringex](https://github.com/rsl/stringex), supporting non-Latin characters.
6
+
7
+ [![Build Status](https://github.com/mongoid/mongoid-slug/actions/workflows/test.yml/badge.svg?query=branch%3Amaster)](https://github.com/mongoid/mongoid-slug/actions/workflows/test.ym?query=branch%3Amaster)
8
+ [![Gem Version](https://badge.fury.io/rb/mongoid-slug.svg)](http://badge.fury.io/rb/mongoid-slug)
9
+ [![Code Climate](https://codeclimate.com/github/mongoid/mongoid-slug.svg)](https://codeclimate.com/github/mongoid/mongoid-slug)
10
+
11
+ ### Version Support
12
+
13
+ Mongoid Slug 7.x requires at least Mongoid 7.0.0 and Ruby 2.7.0. For earlier Mongoid and Ruby version support, please use an earlier version of Mongoid Slug.
14
+
15
+ Mongoid Slug is compatible with all MongoDB versions which Mongoid supports, however, please see "Slug Max Length" section below for MongoDB 4.0 and earlier.
16
+
17
+ ### Installation
18
+
19
+ Add to your Gemfile:
20
+
21
+ ```ruby
22
+ gem 'mongoid-slug'
23
+ ```
24
+
25
+ ### Usage
26
+
27
+ ### Set Up a Slug
28
+
29
+ ```ruby
30
+ class Book
31
+ include Mongoid::Document
32
+ include Mongoid::Slug
33
+
34
+ field :title
35
+ slug :title
36
+ end
37
+ ```
38
+
39
+ ### Find a Document by its Slug
40
+
41
+ ```ruby
42
+ # GET /books/a-thousand-plateaus
43
+ book = Book.find params[:book_id]
44
+ ```
45
+
46
+ Mongoid Slug will attempt to determine whether you want to find using the `slugs` field or the `_id` field by inspecting the supplied parameters.
47
+
48
+ * Mongoid Slug will perform a find based on `slugs` only if all arguments passed to `find` are of the type `String`.
49
+ * If your document uses `BSON::ObjectId` identifiers, and all arguments look like valid `BSON::ObjectId`, then Mongoid Slug will perform a find based on `_id`.
50
+ * If your document uses any other type of identifiers, and all arguments passed to `find` are of the same type, then Mongoid Slug will perform a find based on `_id`.
51
+ * If your document uses `String` identifiers and you want to be able find by slugs or ids, to get the correct behaviour, you should add a `slug_id_strategy` option to your `_id` field definition. This option should return something that responds to `call` (a callable) and takes one string argument, e.g. a lambda. This callable must return true if the string looks like one of your ids.
52
+
53
+ ```ruby
54
+ Book.fields['_id'].type
55
+ => String
56
+
57
+ book = Book.find 'a-thousand-plateaus' # Finds by slugs
58
+ => ...
59
+
60
+ class Post
61
+ include Mongoid::Document
62
+ include Mongoid::Slug
63
+
64
+ field :_id, type: String, slug_id_strategy: lambda { |id| id.start_with?('...') }
65
+
66
+ field :name
67
+ slug :name, history: true
68
+ end
69
+
70
+ Post.fields['_id'].type
71
+ => String
72
+
73
+ post = Post.find 'a-thousand-plateaus' # Finds by slugs
74
+ => ...
75
+
76
+ post = Post.find '50b1386a0482939864000001' # Finds by bson ids
77
+ => ...
78
+ ```
79
+
80
+ [Examine slug.rb](lib/mongoid/slug.rb) for all available options.
81
+
82
+ ### Updating Existing Records
83
+
84
+ To set slugs for existing records run following rake task:
85
+
86
+ ```ruby
87
+ rake mongoid_slug:set
88
+ ```
89
+
90
+ You can pass model names as an option for which you want to set slugs:
91
+
92
+ ```ruby
93
+ rake mongoid_slug:set[Model1,Model2]
94
+ ```
95
+
96
+ ### Nil Slugs
97
+
98
+ Empty slugs are possible and generate a `nil` value for the `_slugs` field. In the `Post` example above, a blank post `name` will cause the document record not to contain a `_slugs` field in the database. The default `_slugs` index is `sparse`, allowing that. If you wish to change this behavior add a custom `validates_presence_of :_slugs` validator to the document or change the database index to `sparse: false`.
99
+
100
+ ### Custom Slug Generation
101
+
102
+ By default Mongoid Slug generates slugs with stringex. If this is not desired you can define your own slug generator.
103
+
104
+ There are two ways to define slug generator.
105
+
106
+ #### Globally
107
+
108
+ Configure a block in `config/initializers/mongoid_slug.rb` as follows:
109
+
110
+ ```ruby
111
+ Mongoid::Slug.configure do |c|
112
+ # create a block that takes the current object as an argument and return the slug
113
+ c.slug = proc { |cur_obj|
114
+ cur_object.slug_builder.to_url
115
+ }
116
+ end
117
+ ```
118
+
119
+ #### On Model
120
+
121
+ ```ruby
122
+ class Caption
123
+ include Mongoid::Document
124
+ include Mongoid::Slug
125
+
126
+ # create a block that takes the current object as an argument and returns the slug
127
+ slug do |cur_object|
128
+ cur_object.slug_builder.to_url
129
+ end
130
+ end
131
+ ```
132
+
133
+ The `to_url` method comes from [stringex](https://github.com/rsl/stringex).
134
+
135
+ You can define a slug builder globally and/or override it per model.
136
+
137
+ ### Indexing
138
+
139
+ By default, Mongoid Slug will automatically generate an index for the slug, which will be created when you run `rake db:create_indexes`. This index will take into account scoping and other options described below.
140
+
141
+ To skip this index generation, you may set `index: false` as follows:
142
+
143
+ ```ruby
144
+ class Employee
145
+ include Mongoid::Document
146
+ include Mongoid::Slug
147
+
148
+ field :name
149
+
150
+ slug :name, index: :false
151
+ end
152
+ ```
153
+
154
+ ### Scoping
155
+
156
+ To scope a slug by a reference association, pass `:scope`:
157
+
158
+ ```ruby
159
+ class Company
160
+ include Mongoid::Document
161
+
162
+ references_many :employees
163
+ end
164
+
165
+ class Employee
166
+ include Mongoid::Document
167
+ include Mongoid::Slug
168
+
169
+ field :name
170
+ referenced_in :company
171
+
172
+ slug :name, scope: :company
173
+ end
174
+ ```
175
+
176
+ In this example, if you create an employee without associating it with any company, the scope will fall back to the root employees collection.
177
+
178
+ Currently, if you have an irregular association name, you **must** specify the `:inverse_of` option on the other side of the assocation.
179
+
180
+ Embedded objects are automatically scoped by their parent.
181
+
182
+ Note that the unique index on the `Employee` collection in this example is derived from the `scope` value and is `{ _slugs: 1, company_id: 1}`. Therefore `:company` must be `referenced_in` above the definition of `slug` or it will not be able to resolve the association and mistakenly create a `{ _slugs: 1, company: 1}` index. An alternative is to scope to the field itself as follows:
183
+
184
+ ```ruby
185
+ class Employee
186
+ include Mongoid::Document
187
+ include Mongoid::Slug
188
+
189
+ field :name
190
+ field :company_id
191
+
192
+ slug :name, scope: :company_id
193
+ end
194
+ ```
195
+
196
+ ### Slug Max Length
197
+
198
+ MongoDB [featureCompatibilityVersion](https://docs.mongodb.com/manual/reference/command/setFeatureCompatibilityVersion/#std-label-view-fcv)
199
+ "4.0" and earlier applies an [Index Key Limit](https://docs.mongodb.com/manual/reference/limits/#mongodb-limit-Index-Key-Limit)
200
+ which limits the total size of an index entry to around 1KB and will raise error,
201
+ `17280 - key too large to index` when trying to create a record that causes an index key to exceed that limit.
202
+ By default slugs are of the form `text[-number]` and the text portion is limited in size
203
+ to `Mongoid::Slug::MONGO_INDEX_KEY_LIMIT_BYTES - 32` bytes.
204
+ You can change this limit with `max_length` or set it to `nil` if you're running MongoDB
205
+ with [failIndexKeyTooLong](https://docs.mongodb.org/manual/reference/parameters/#param.failIndexKeyTooLong) set to `false`.
206
+
207
+ ```ruby
208
+ class Company
209
+ include Mongoid::Document
210
+ include Mongoid::Slug
211
+
212
+ field :name
213
+
214
+ slug :name, max_length: 24
215
+ end
216
+ ```
217
+
218
+ ### Optionally Find and Create Slugs per Model Type
219
+
220
+ By default when using STI, the scope will be around the super-class.
221
+
222
+ ```ruby
223
+ class Book
224
+ include Mongoid::Document
225
+ include Mongoid::Slug
226
+ field :title
227
+
228
+ slug :title, history: true
229
+ embeds_many :subjects
230
+ has_many :authors
231
+ end
232
+
233
+ class ComicBook < Book
234
+ end
235
+
236
+ book = Book.create(title: 'Anti Oedipus')
237
+ comic_book = ComicBook.create(title: 'Anti Oedipus')
238
+ comic_book.slugs.should_not eql(book.slugs)
239
+ ```
240
+
241
+ If you want the scope to be around the subclass, then set the option `by_model_type: true`.
242
+
243
+ ```ruby
244
+ class Book
245
+ include Mongoid::Document
246
+ include Mongoid::Slug
247
+ field :title
248
+
249
+ slug :title, history: true, by_model_type: true
250
+ embeds_many :subjects
251
+ has_many :authors
252
+ end
253
+
254
+ class ComicBook < Book
255
+ end
256
+
257
+ book = Book.create(title: 'Anti Oedipus')
258
+ comic_book = ComicBook.create(title: 'Anti Oedipus')
259
+ comic_book.slugs.should eql(book.slugs)
260
+ ```
261
+
262
+ ### History
263
+
264
+ Enable slug history tracking by setting `history: true`.
265
+
266
+ ```ruby
267
+ class Page
268
+ include Mongoid::Document
269
+ include Mongoid::Slug
270
+
271
+ field :title
272
+
273
+ slug :title, history: true
274
+ end
275
+ ```
276
+
277
+ The document will then be returned for any of the saved slugs:
278
+
279
+ ```ruby
280
+ page = Page.new title: "Home"
281
+ page.save
282
+ page.update_attributes title: "Welcome"
283
+
284
+ Page.find("welcome") == Page.find("home") # => true
285
+ ```
286
+
287
+ ### Reserved Slugs
288
+
289
+ Pass words you do not want to be slugged using the `reserve` option:
290
+
291
+ ```ruby
292
+ class Friend
293
+ include Mongoid::Document
294
+
295
+ field :name
296
+ slug :name, reserve: ['admin', 'root']
297
+ end
298
+
299
+ friend = Friend.create name: 'admin'
300
+ Friend.find('admin') # => nil
301
+ friend.slug # => 'admin-1'
302
+ ```
303
+
304
+ When reserved words are not specified, the words 'new' and 'edit' are considered reserved by default.
305
+ Specifying an array of custom reserved words will overwrite these defaults.
306
+
307
+ ### Localize Slugs
308
+
309
+ The slugs can be localized. This feature is built upon Mongoid localized fields,
310
+ so fallbacks and localization works as documented in the Mongoid manual.
311
+
312
+ ```ruby
313
+ class PageSlugLocalize
314
+ include Mongoid::Document
315
+ include Mongoid::Slug
316
+
317
+ field :title, localize: true
318
+ slug :title, localize: true
319
+ end
320
+ ```
321
+
322
+ By specifying `localize: true`, the slug index will be created on the
323
+ [I18n.default_locale](http://guides.rubyonrails.org/i18n.html#the-public-i18n-api) field only.
324
+ For example, if `I18n.default_locale` is `:en`, the index will be generated as follows:
325
+
326
+ ```ruby
327
+ slug :title, localize: true
328
+
329
+ # The following index is auto-generated:
330
+ index({ '_slugs.en' => 1 }, { unique: true, sparse: true })
331
+ ```
332
+
333
+ If you are supporting multiple locales, you may specify the list of locales on which
334
+ to create indexes as an `Array`.
335
+
336
+ ```ruby
337
+ slug :title, localize: [:fr, :es, :de]
338
+
339
+ # The following indexes are auto-generated:
340
+ index({ '_slugs.fr' => 1 }, { unique: true, sparse: true })
341
+ index({ '_slugs.es' => 1 }, { unique: true, sparse: true })
342
+ index({ '_slugs.de' => 1 }, { unique: true, sparse: true })
343
+ ```
344
+
345
+ ### Custom Find Strategies
346
+
347
+ By default find will search for the document by the id field if the provided id looks like a `BSON::ObjectId`, and it will otherwise find by the _slugs field. However, custom strategies can ovveride the default behavior, like e.g:
348
+
349
+ ```ruby
350
+ module Mongoid::Slug::UuidIdStrategy
351
+ def self.call id
352
+ id =~ /\A([0-9a-fA-F]){8}-(([0-9a-fA-F]){4}-){3}([0-9a-fA-F]){12}\z/
353
+ end
354
+ end
355
+ ```
356
+
357
+ Use a custom strategy by adding the `slug_id_strategy` annotation to the `_id` field:
358
+
359
+ ```ruby
360
+ class Entity
361
+ include Mongoid::Document
362
+ include Mongoid::Slug
363
+
364
+ field :_id, type: String, slug_id_strategy: UuidIdStrategy
365
+
366
+ field :user_edited_variation
367
+ slug :user_edited_variation, history: true
368
+ end
369
+ ```
370
+
371
+ ### Adhoc Checking Whether a Slug is Unique
372
+
373
+ Lets say you want to have a auto-suggest function on your GUI that could provide a preview of what the url or slug could be before the form to create the record was submitted.
374
+
375
+ You can use the UniqueSlug class in your server side code to do this, e.g.
376
+
377
+ ```ruby
378
+ title = params[:title]
379
+ unique = Mongoid::Slug::UniqueSlug.new(Book.new).find_unique(title)
380
+ ...
381
+ # return some representation of unique
382
+ ```
383
+
384
+ Contributing
385
+ ------------
386
+
387
+ Mongoid-slug is work of [many of contributors](https://github.com/mongoid/mongoid-slug/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/mongoid/mongoid-slug/pulls), [propose features, ask questions and discuss issues](https://github.com/mongoid/mongoid-slug/issues). See [CONTRIBUTING](CONTRIBUTING.md) for details.
388
+
389
+ Copyright & License
390
+ -------------------
391
+
392
+ Copyright (c) 2010-2017 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.