mongoid-slug 4.0.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/LICENSE +1 -1
- data/README.md +18 -18
- data/lib/mongoid/slug.rb +48 -44
- data/lib/mongoid/slug/criteria.rb +14 -11
- data/lib/mongoid/slug/index.rb +5 -8
- data/lib/mongoid/slug/paranoia.rb +0 -2
- data/lib/mongoid/slug/slug_id_strategy.rb +1 -1
- data/lib/mongoid/slug/unique_slug.rb +27 -29
- data/lib/mongoid/slug/version.rb +1 -1
- data/spec/models/alias.rb +2 -2
- data/spec/models/article.rb +1 -1
- data/spec/models/author.rb +3 -3
- data/spec/models/author_polymorphic.rb +3 -3
- data/spec/models/book.rb +1 -1
- data/spec/models/book_polymorphic.rb +1 -1
- data/spec/models/caption.rb +1 -1
- data/spec/models/entity.rb +2 -2
- data/spec/models/friend.rb +2 -2
- data/spec/models/incorrect_slug_persistence.rb +5 -5
- data/spec/models/integer_id.rb +1 -1
- data/spec/models/magazine.rb +1 -1
- data/spec/models/page.rb +3 -3
- data/spec/models/page_localize.rb +3 -3
- data/spec/models/page_slug_localized.rb +3 -3
- data/spec/models/page_slug_localized_custom.rb +0 -1
- data/spec/models/page_slug_localized_history.rb +3 -3
- data/spec/models/paranoid_document.rb +1 -1
- data/spec/models/paranoid_permanent.rb +1 -1
- data/spec/models/partner.rb +1 -1
- data/spec/models/person.rb +2 -2
- data/spec/models/relationship.rb +1 -1
- data/spec/models/string_id.rb +1 -1
- data/spec/models/subject.rb +1 -1
- data/spec/models/without_slug.rb +1 -1
- data/spec/mongoid/criteria_spec.rb +109 -109
- data/spec/mongoid/index_spec.rb +12 -14
- data/spec/mongoid/paranoia_spec.rb +78 -90
- data/spec/mongoid/slug_spec.rb +493 -492
- data/spec/shared/indexes.rb +13 -13
- data/spec/spec_helper.rb +18 -14
- metadata +51 -26
- data/spec/mongoid/slug_spec.rb.b00 +0 -1101
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzVhMTg3OTVmNzYyZDVmNWQ1MGQxYTNiMDYyZDUzMDhkMTFiNjNkOA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MDNjMmJjZTI4MDE3Njk1NThhYjg4ZTZjNmEwYzE4NzVkMjA3ZTUwNg==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NWIzMWQzYTFlNTNhNDcwY2IwN2RhM2M0M2Q5OGVkNTg1MjUzOTM1ODA5N2Rh
|
10
|
+
ZTA0NjQ0ODk0MGI5MDMzNmM4Y2I0ZDAzZWIxYzM3ZmVjOTZiYTA3NGJhZjJi
|
11
|
+
YmQyOWQxZjkwZmVmYTBkOWM4MTMxMTAzYjRhZDc2MDdhMDBiYzA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MTBkMDU5NDRlYjkxY2MyNDdjMTI1OWY4NThkYTkyNTRmMzUyZmY2ZDU4ODVl
|
14
|
+
MjU2YjNjMTRkZThiNmU3OWRmNGQ0ZTEyZTkxZTFkNDQ2ZTJkMmI2YjJmMTgx
|
15
|
+
OWQxNDEwYTQ5OTYzYzI3ZDk3MTlkMWFlNThjNTcyODJmZTI0Yjc=
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
*IMPORTANT:* If you are upgrading to Mongoid Slug 1.0.0 please migrate in accordance with the instructions in https://github.com/digitalplaywright/mongoid-slug/wiki/How-to-upgrade-to-1.0.0-or-newer.
|
2
|
-
Mongoid Slug 1.0.0 stores the slugs in a single field _slugs of array type, and all previous slugs must be migrated.
|
3
|
-
|
4
1
|
Mongoid Slug
|
5
2
|
============
|
6
3
|
|
7
|
-
Mongoid Slug generates a URL slug or permalink based on one or more fields in a
|
8
|
-
Mongoid model. It sits idly on top of [stringex] [1], supporting non-Latin
|
9
|
-
characters.
|
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.
|
10
5
|
|
11
|
-
[![Build Status](https://secure.travis-ci.org/digitalplaywright/mongoid-slug.png)](http://travis-ci.org/digitalplaywright/mongoid-slug)
|
6
|
+
[![Build Status](https://secure.travis-ci.org/digitalplaywright/mongoid-slug.png)](http://travis-ci.org/digitalplaywright/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/digitalplaywright/mongoid-slug.png)](https://gemnasium.com/digitalplaywright/mongoid-slug)
|
9
|
+
[![Code Climate](https://codeclimate.com/github/digitalplaywright/mongoid-slug.png)](https://codeclimate.com/github/digitalplaywright/mongoid-slug)
|
12
10
|
|
13
11
|
Installation
|
14
12
|
------------
|
@@ -72,7 +70,7 @@ post = Post.find 'a-thousand-plateaus' # Finds by slugs
|
|
72
70
|
post = Post.find '50b1386a0482939864000001' # Finds by bson ids
|
73
71
|
=> ...
|
74
72
|
```
|
75
|
-
[
|
73
|
+
[Examine slug.rb](lib/mongoid/slug.rb) for all available options.
|
76
74
|
|
77
75
|
Custom Slug Generation
|
78
76
|
-------
|
@@ -254,7 +252,7 @@ PS! A migration is needed to use Mongoid localized fields for documents that was
|
|
254
252
|
feature was off. Anything else will cause errors.
|
255
253
|
|
256
254
|
Custom Find Strategies
|
257
|
-
|
255
|
+
----------------------
|
258
256
|
|
259
257
|
By default find will search for the document by the id field if the provided id
|
260
258
|
looks like a BSON::ObjectId, and it will otherwise find by the _slugs field. However,
|
@@ -303,6 +301,7 @@ Mongoid::Paranoia Support
|
|
303
301
|
|
304
302
|
The [Mongoid::Paranoia](http://github.com/simi/mongoid-paranoia) gem adds "soft-destroy" functionality to Mongoid documents.
|
305
303
|
Mongoid::Slug contains special handling for Mongoid::Paranoia:
|
304
|
+
|
306
305
|
- When destroying a paranoid document, the slug will be unset from the database.
|
307
306
|
- When restoring a paranoid document, the slug will be rebuilt. Note that the new slug may not match the old one.
|
308
307
|
- When resaving a destroyed paranoid document, the slug will remain unset in the database.
|
@@ -317,17 +316,18 @@ end
|
|
317
316
|
```
|
318
317
|
|
319
318
|
The following variants of Mongoid Paranoia are officially supported:
|
319
|
+
|
320
320
|
* Mongoid 3 built-in Mongoid::Paranoia
|
321
|
-
* Mongoid 4 gem http://github.com/simi/
|
321
|
+
* Mongoid 4 or 5 gem http://github.com/simi/mongoid_paranoia
|
322
|
+
|
323
|
+
|
324
|
+
Contributing
|
325
|
+
------------
|
322
326
|
|
323
|
-
Mongoid
|
324
|
-
is not officially supported but should also work.
|
327
|
+
Mongoid-slug is work of [many of contributors](https://github.com/digitalplaywright/mongoid-slug/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/digitalplaywright/mongoid-slug/pulls), [propose features, ask questions and discuss issues](https://github.com/digitalplaywright/mongoid-slug/issues). See [CONTRIBUTING](CONTRIBUTING.md) for details.
|
325
328
|
|
329
|
+
Copyright & License
|
330
|
+
-------------------
|
326
331
|
|
327
|
-
|
328
|
-
----------
|
332
|
+
Copyright (c) 2010-2015 Hakan Ensari & Contributors, see [LICENSE](LICENSE) for details.
|
329
333
|
|
330
|
-
[1]: https://github.com/rsl/stringex/
|
331
|
-
[2]: https://secure.travis-ci.org/hakanensari/mongoid-slug.png
|
332
|
-
[3]: http://travis-ci.org/hakanensari/mongoid-slug
|
333
|
-
[4]: https://github.com/digitalplaywright/mongoid-slug/blob/master/lib/mongoid/slug.rb
|
data/lib/mongoid/slug.rb
CHANGED
@@ -5,6 +5,7 @@ require 'mongoid/slug/index'
|
|
5
5
|
require 'mongoid/slug/paranoia'
|
6
6
|
require 'mongoid/slug/unique_slug'
|
7
7
|
require 'mongoid/slug/slug_id_strategy'
|
8
|
+
require 'mongoid-compatibility'
|
8
9
|
|
9
10
|
module Mongoid
|
10
11
|
# Slugs your Mongoid model.
|
@@ -59,8 +60,8 @@ module Mongoid
|
|
59
60
|
options = fields.extract_options!
|
60
61
|
|
61
62
|
self.slug_scope = options[:scope]
|
62
|
-
self.reserved_words = options[:reserve] || Set.new(
|
63
|
-
self.slugged_attributes = fields.map
|
63
|
+
self.reserved_words = options[:reserve] || Set.new(%w(new edit))
|
64
|
+
self.slugged_attributes = fields.map(&:to_s)
|
64
65
|
self.history = options[:history]
|
65
66
|
self.by_model_type = options[:by_model_type]
|
66
67
|
|
@@ -69,7 +70,7 @@ module Mongoid
|
|
69
70
|
|
70
71
|
# Set index
|
71
72
|
unless embedded?
|
72
|
-
index(*Mongoid::Slug::Index.build_index(
|
73
|
+
index(*Mongoid::Slug::Index.build_index(slug_scope_key, by_model_type))
|
73
74
|
end
|
74
75
|
|
75
76
|
#-- Why is it necessary to customize the slug builder?
|
@@ -84,7 +85,7 @@ module Mongoid
|
|
84
85
|
if options[:permanent]
|
85
86
|
set_callback :create, :before, :build_slug
|
86
87
|
else
|
87
|
-
set_callback :save, :before, :build_slug, :
|
88
|
+
set_callback :save, :before, :build_slug, if: :slug_should_be_rebuilt?
|
88
89
|
end
|
89
90
|
|
90
91
|
# If paranoid document:
|
@@ -93,11 +94,11 @@ module Mongoid
|
|
93
94
|
# - recreate the slug on restore
|
94
95
|
# - force reset the slug when saving a destroyed paranoid document, to ensure it stays unset in the database
|
95
96
|
if is_paranoid_doc?
|
96
|
-
|
97
|
+
send(:include, Mongoid::Slug::Paranoia) unless self.respond_to?(:before_restore)
|
97
98
|
set_callback :destroy, :after, :unset_slug!
|
98
99
|
set_callback :restore, :before, :set_slug!
|
99
|
-
set_callback :save, :before, :reset_slug!, :
|
100
|
-
set_callback :save, :after, :clear_slug!, :
|
100
|
+
set_callback :save, :before, :reset_slug!, if: :paranoid_deleted?
|
101
|
+
set_callback :save, :after, :clear_slug!, if: :paranoid_deleted?
|
101
102
|
end
|
102
103
|
end
|
103
104
|
|
@@ -109,8 +110,8 @@ module Mongoid
|
|
109
110
|
#
|
110
111
|
# @return [ Array<Document>, Document ] Whether the document is paranoid
|
111
112
|
def slug_scope_key
|
112
|
-
return nil unless
|
113
|
-
|
113
|
+
return nil unless slug_scope
|
114
|
+
reflect_on_association(slug_scope).try(:key) || slug_scope
|
114
115
|
end
|
115
116
|
|
116
117
|
# Find documents by slugs.
|
@@ -135,7 +136,8 @@ module Mongoid
|
|
135
136
|
end
|
136
137
|
|
137
138
|
def queryable
|
138
|
-
|
139
|
+
scope = Mongoid::Compatibility::Version.mongoid5? ? Threaded.current_scope : scope_stack.last
|
140
|
+
scope || Criteria.new(self) # Use Mongoid::Slug::Criteria for slugged documents.
|
139
141
|
end
|
140
142
|
|
141
143
|
# Indicates whether or not the document includes Mongoid::Paranoia
|
@@ -171,19 +173,19 @@ module Mongoid
|
|
171
173
|
end
|
172
174
|
|
173
175
|
def apply_slug
|
174
|
-
|
176
|
+
new_slug = find_unique_slug
|
175
177
|
|
176
|
-
#skip slug generation and use Mongoid id
|
177
|
-
#to find document instead
|
178
|
-
return true if
|
178
|
+
# skip slug generation and use Mongoid id
|
179
|
+
# to find document instead
|
180
|
+
return true if new_slug.size == 0
|
179
181
|
|
180
182
|
# avoid duplicate slugs
|
181
|
-
|
183
|
+
_slugs.delete(new_slug) if _slugs
|
182
184
|
|
183
|
-
if !!
|
184
|
-
append_slug(
|
185
|
+
if !!history && _slugs.is_a?(Array)
|
186
|
+
append_slug(new_slug)
|
185
187
|
else
|
186
|
-
self._slugs = [
|
188
|
+
self._slugs = [new_slug]
|
187
189
|
end
|
188
190
|
end
|
189
191
|
|
@@ -194,7 +196,7 @@ module Mongoid
|
|
194
196
|
# Mongoid 3 (two args) and Mongoid 4 (hash arg)
|
195
197
|
def set_slug!
|
196
198
|
build_slug
|
197
|
-
|
199
|
+
method(:set).arity == 1 ? set(_slugs: _slugs) : set(:_slugs, _slugs)
|
198
200
|
end
|
199
201
|
|
200
202
|
# Atomically unsets the slug field in the database. It is important to unset
|
@@ -228,7 +230,7 @@ module Mongoid
|
|
228
230
|
|
229
231
|
# @return [Boolean] Whether the slug requires to be rebuilt
|
230
232
|
def slug_should_be_rebuilt?
|
231
|
-
(new_record?
|
233
|
+
(new_record? || _slugs_changed? || slugged_attributes_changed?) && !paranoid_deleted?
|
232
234
|
end
|
233
235
|
|
234
236
|
# Indicates whether or not the document has been deleted in paranoid fashion
|
@@ -236,7 +238,7 @@ module Mongoid
|
|
236
238
|
#
|
237
239
|
# @return [Boolean] Whether or not the document has been deleted in paranoid fashion
|
238
240
|
def paranoid_deleted?
|
239
|
-
!!(self.class.is_paranoid_doc?
|
241
|
+
!!(self.class.is_paranoid_doc? && !deleted_at.nil?)
|
240
242
|
end
|
241
243
|
|
242
244
|
def slugged_attributes_changed?
|
@@ -252,17 +254,17 @@ module Mongoid
|
|
252
254
|
# @return [String] the slug, or nil if the document does not have a slug.
|
253
255
|
def slug
|
254
256
|
return _slugs.last if _slugs
|
255
|
-
|
257
|
+
_id.to_s
|
256
258
|
end
|
257
259
|
|
258
260
|
def slug_builder
|
259
|
-
|
260
|
-
if new_with_slugs?
|
261
|
-
#user defined slug
|
262
|
-
|
261
|
+
cur_slug = nil
|
262
|
+
if new_with_slugs? || persisted_with_slug_changes?
|
263
|
+
# user defined slug
|
264
|
+
cur_slug = _slugs.last
|
263
265
|
end
|
264
|
-
#generate slug if the slug is not user defined or does not exist
|
265
|
-
|
266
|
+
# generate slug if the slug is not user defined or does not exist
|
267
|
+
cur_slug || pre_slug_string
|
266
268
|
end
|
267
269
|
|
268
270
|
def self.mongoid3?
|
@@ -271,16 +273,16 @@ module Mongoid
|
|
271
273
|
|
272
274
|
private
|
273
275
|
|
274
|
-
def append_slug(
|
276
|
+
def append_slug(value)
|
275
277
|
if localized?
|
276
278
|
# This is necessary for the scenario in which the slugged locale is not yet present
|
277
279
|
# but the default locale is. In this situation, self._slugs falls back to the default
|
278
280
|
# which is undesired
|
279
|
-
current_slugs =
|
280
|
-
current_slugs <<
|
281
|
-
self._slugs_translations =
|
281
|
+
current_slugs = _slugs_translations.fetch(I18n.locale.to_s, [])
|
282
|
+
current_slugs << value
|
283
|
+
self._slugs_translations = _slugs_translations.merge(I18n.locale.to_s => current_slugs)
|
282
284
|
else
|
283
|
-
|
285
|
+
_slugs << value
|
284
286
|
end
|
285
287
|
end
|
286
288
|
|
@@ -289,29 +291,31 @@ module Mongoid
|
|
289
291
|
if localized?
|
290
292
|
# We need to check if slugs are present for the locale without falling back
|
291
293
|
# to a default
|
292
|
-
new_record?
|
294
|
+
new_record? && _slugs_translations.fetch(I18n.locale.to_s, []).any?
|
293
295
|
else
|
294
|
-
new_record?
|
296
|
+
new_record? && _slugs.present?
|
295
297
|
end
|
296
298
|
end
|
297
299
|
|
298
300
|
# Returns true if object has been persisted and has changes in the slug
|
299
301
|
def persisted_with_slug_changes?
|
300
302
|
if localized?
|
301
|
-
changes =
|
302
|
-
return (persisted?
|
303
|
+
changes = _slugs_change
|
304
|
+
return (persisted? && false) if changes.nil?
|
303
305
|
|
304
306
|
# ensure we check for changes only between the same locale
|
305
307
|
original = changes.first.try(:fetch, I18n.locale.to_s, nil)
|
306
308
|
compare = changes.last.try(:fetch, I18n.locale.to_s, nil)
|
307
|
-
persisted?
|
309
|
+
persisted? && original != compare
|
308
310
|
else
|
309
|
-
persisted?
|
311
|
+
persisted? && _slugs_changed?
|
310
312
|
end
|
311
313
|
end
|
312
314
|
|
313
315
|
def localized?
|
314
|
-
|
316
|
+
fields['_slugs'].options[:localize]
|
317
|
+
rescue
|
318
|
+
false
|
315
319
|
end
|
316
320
|
|
317
321
|
# Return all possible locales for model
|
@@ -319,15 +323,15 @@ module Mongoid
|
|
319
323
|
# doing something crazy, but at the same time we need a fallback in case the model doesn't
|
320
324
|
# have any localized attributes at all (extreme edge case).
|
321
325
|
def all_locales
|
322
|
-
locales =
|
323
|
-
|
324
|
-
|
326
|
+
locales = slugged_attributes
|
327
|
+
.map { |attr| send("#{attr}_translations").keys if self.respond_to?("#{attr}_translations") }
|
328
|
+
.flatten.compact.uniq
|
325
329
|
locales = I18n.available_locales if locales.empty?
|
326
330
|
locales
|
327
331
|
end
|
328
332
|
|
329
333
|
def pre_slug_string
|
330
|
-
|
334
|
+
slugged_attributes.map { |f| send f }.join ' '
|
331
335
|
end
|
332
336
|
end
|
333
337
|
end
|
@@ -60,13 +60,13 @@ module Mongoid
|
|
60
60
|
# unless a :slug_id_strategy option is defined on the id field,
|
61
61
|
# use object_id or string strategy depending on the id_type
|
62
62
|
# otherwise default for all other id_types
|
63
|
-
def build_slug_strategy
|
64
|
-
type_method = id_type.to_s.downcase.split('::').last +
|
65
|
-
self.respond_to?(type_method, true) ? method(type_method) :
|
63
|
+
def build_slug_strategy(id_type)
|
64
|
+
type_method = id_type.to_s.downcase.split('::').last + '_slug_strategy'
|
65
|
+
self.respond_to?(type_method, true) ? method(type_method) : ->(_id) { false }
|
66
66
|
end
|
67
67
|
|
68
68
|
# a string will not look like a slug if it looks like a legal BSON::ObjectId
|
69
|
-
def objectid_slug_strategy
|
69
|
+
def objectid_slug_strategy(id)
|
70
70
|
if Mongoid::Slug.mongoid3?
|
71
71
|
Moped::BSON::ObjectId.legal? id
|
72
72
|
else
|
@@ -75,20 +75,23 @@ module Mongoid
|
|
75
75
|
end
|
76
76
|
|
77
77
|
# a string will always look like a slug
|
78
|
-
def string_slug_strategy
|
78
|
+
def string_slug_strategy(_id)
|
79
79
|
true
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
82
|
def for_slugs(slugs)
|
84
|
-
#_translations
|
85
|
-
localized = (
|
83
|
+
# _translations
|
84
|
+
localized = (begin
|
85
|
+
@klass.fields['_slugs'].options[:localize]
|
86
|
+
rescue
|
87
|
+
false
|
88
|
+
end)
|
86
89
|
if localized
|
87
90
|
def_loc = I18n.default_locale
|
88
91
|
query = { '$in' => slugs }
|
89
|
-
where({'$or' => [{ _slugs: query }, { "_slugs.#{def_loc}" => query }]}).limit(slugs.length)
|
92
|
+
where({ '$or' => [{ _slugs: query }, { "_slugs.#{def_loc}" => query }] }).limit(slugs.length)
|
90
93
|
else
|
91
|
-
where(
|
94
|
+
where(_slugs: { '$in' => slugs }).limit(slugs.length)
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
@@ -102,7 +105,7 @@ module Mongoid
|
|
102
105
|
missing_slugs = slugs - result.map(&:slugs).flatten
|
103
106
|
|
104
107
|
if !missing_slugs.blank? && Mongoid.raise_not_found_error
|
105
|
-
|
108
|
+
fail Errors::DocumentNotFound.new(klass, slugs, missing_slugs)
|
106
109
|
end
|
107
110
|
end
|
108
111
|
end
|
data/lib/mongoid/slug/index.rb
CHANGED
@@ -1,26 +1,23 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Slug
|
3
3
|
module Index
|
4
|
-
|
5
4
|
# @param [ String or Symbol ] scope_key The optional scope key for the index
|
6
5
|
# @param [ Boolean ] by_model_type Whether or not
|
7
6
|
#
|
8
7
|
# @return [ Array(Hash, Hash) ] the indexable fields and index options.
|
9
8
|
def self.build_index(scope_key = nil, by_model_type = false)
|
10
|
-
fields = {_slugs: 1}
|
9
|
+
fields = { _slugs: 1 }
|
11
10
|
options = {}
|
12
11
|
|
13
|
-
if scope_key
|
14
|
-
fields.merge!({scope_key => 1})
|
15
|
-
end
|
12
|
+
fields.merge!(scope_key => 1) if scope_key
|
16
13
|
|
17
14
|
if by_model_type
|
18
|
-
fields.merge!(
|
15
|
+
fields.merge!(_type: 1)
|
19
16
|
else
|
20
|
-
options.merge!(
|
17
|
+
options.merge!(unique: true, sparse: true)
|
21
18
|
end
|
22
19
|
|
23
|
-
|
20
|
+
[fields, options]
|
24
21
|
end
|
25
22
|
end
|
26
23
|
end
|
@@ -1,13 +1,11 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Slug
|
3
|
-
|
4
3
|
# Lightweight compatibility shim which adds the :restore callback to
|
5
4
|
# older versions of Mongoid::Paranoia
|
6
5
|
module Paranoia
|
7
6
|
extend ActiveSupport::Concern
|
8
7
|
|
9
8
|
included do
|
10
|
-
|
11
9
|
define_model_callbacks :restore
|
12
10
|
|
13
11
|
def restore_with_callbacks
|
@@ -8,7 +8,7 @@ module Mongoid
|
|
8
8
|
class SlugState
|
9
9
|
attr_reader :last_entered_slug, :existing_slugs, :existing_history_slugs, :sorted_existing
|
10
10
|
|
11
|
-
def initialize
|
11
|
+
def initialize(slug, documents, pattern)
|
12
12
|
@slug = slug
|
13
13
|
@documents = documents
|
14
14
|
@pattern = pattern
|
@@ -21,7 +21,7 @@ module Mongoid
|
|
21
21
|
next if history_slugs.nil?
|
22
22
|
existing_slugs.push(*history_slugs.find_all { |cur_slug| cur_slug =~ @pattern })
|
23
23
|
last_entered_slug.push(*history_slugs.last) if history_slugs.last =~ @pattern
|
24
|
-
existing_history_slugs.push(*history_slugs.first(history_slugs.length
|
24
|
+
existing_history_slugs.push(*history_slugs.first(history_slugs.length - 1).find_all { |cur_slug| cur_slug =~ @pattern })
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -40,19 +40,19 @@ module Mongoid
|
|
40
40
|
|
41
41
|
def sort_existing_slugs
|
42
42
|
# remove the slug part and leave the absolute integer part and sort
|
43
|
-
re =
|
43
|
+
re = /^#{Regexp.escape(@slug)}/
|
44
44
|
@sorted_existing = existing_slugs.map do |s|
|
45
|
-
s.sub(re,'').to_i.abs
|
45
|
+
s.sub(re, '').to_i.abs
|
46
46
|
end.sort
|
47
47
|
end
|
48
48
|
|
49
49
|
def inspect
|
50
50
|
{
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
51
|
+
slug: @slug,
|
52
|
+
existing_slugs: existing_slugs,
|
53
|
+
last_entered_slug: last_entered_slug,
|
54
|
+
existing_history_slugs: existing_history_slugs,
|
55
|
+
sorted_existing: sorted_existing
|
56
56
|
}
|
57
57
|
end
|
58
58
|
end
|
@@ -62,12 +62,12 @@ module Mongoid
|
|
62
62
|
attr_reader :model, :_slug
|
63
63
|
|
64
64
|
def_delegators :@model, :slug_scope, :reflect_on_association, :read_attribute,
|
65
|
-
|
66
|
-
|
65
|
+
:check_against_id, :reserved_words, :url_builder, :collection_name,
|
66
|
+
:embedded?, :reflect_on_all_associations, :by_model_type
|
67
67
|
|
68
|
-
def initialize
|
68
|
+
def initialize(model)
|
69
69
|
@model = model
|
70
|
-
@_slug =
|
70
|
+
@_slug = ''
|
71
71
|
@state = nil
|
72
72
|
end
|
73
73
|
|
@@ -75,40 +75,40 @@ module Mongoid
|
|
75
75
|
@model.respond_to?(:relation_metadata) ? @model.relation_metadata : @model.metadata
|
76
76
|
end
|
77
77
|
|
78
|
-
def find_unique
|
78
|
+
def find_unique(attempt = nil)
|
79
79
|
MUTEX_FOR_SLUG.synchronize do
|
80
80
|
@_slug = if attempt
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
81
|
+
attempt.to_url
|
82
|
+
else
|
83
|
+
url_builder.call(model)
|
84
|
+
end
|
85
85
|
# Regular expression that matches slug, slug-1, ... slug-n
|
86
86
|
# If slug_name field was indexed, MongoDB will utilize that
|
87
87
|
# index to match /^.../ pattern.
|
88
88
|
pattern = /^#{Regexp.escape(@_slug)}(?:-(\d+))?$/
|
89
|
-
|
89
|
+
|
90
90
|
where_hash = {}
|
91
91
|
where_hash[:_slugs.all] = [pattern]
|
92
92
|
where_hash[:_id.ne] = model._id
|
93
|
-
|
93
|
+
|
94
94
|
if (scope = slug_scope) && reflect_on_association(scope).nil?
|
95
95
|
# scope is not an association, so it's scoped to a local field
|
96
96
|
# (e.g. an association id in a denormalized db design)
|
97
97
|
where_hash[scope] = model.try(:read_attribute, scope)
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
if by_model_type == true
|
101
101
|
where_hash[:_type] = model.try(:read_attribute, :_type)
|
102
102
|
end
|
103
|
-
|
103
|
+
|
104
104
|
@state = SlugState.new @_slug, uniqueness_scope.unscoped.where(where_hash), pattern
|
105
|
-
|
105
|
+
|
106
106
|
# do not allow a slug that can be interpreted as the current document id
|
107
107
|
@state.include_slug unless model.class.look_like_slugs?([@_slug])
|
108
|
-
|
108
|
+
|
109
109
|
# make sure that the slug is not equal to a reserved word
|
110
110
|
@state.include_slug if reserved_words.any? { |word| word === @_slug }
|
111
|
-
|
111
|
+
|
112
112
|
# only look for a new unique slug if the existing slugs contains the current slug
|
113
113
|
# - e.g if the slug 'foo-2' is taken, but 'foo' is available, the user can use 'foo'.
|
114
114
|
if @state.slug_included?
|
@@ -120,9 +120,7 @@ module Mongoid
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def uniqueness_scope
|
123
|
-
|
124
|
-
if slug_scope &&
|
125
|
-
metadata = reflect_on_association(slug_scope)
|
123
|
+
if slug_scope && (metadata = reflect_on_association(slug_scope))
|
126
124
|
|
127
125
|
parent = model.send(metadata.name)
|
128
126
|
|
@@ -141,7 +139,7 @@ module Mongoid
|
|
141
139
|
return model._parent.send(parent_metadata.inverse_of || self.metadata.name)
|
142
140
|
end
|
143
141
|
|
144
|
-
#unless embedded or slug scope, return the deepest document superclass
|
142
|
+
# unless embedded or slug scope, return the deepest document superclass
|
145
143
|
appropriate_class = model.class
|
146
144
|
while appropriate_class.superclass.include?(Mongoid::Document)
|
147
145
|
appropriate_class = appropriate_class.superclass
|