mongoid_slug 0.8.3 → 0.9.0
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.
- data/README.md +13 -1
- data/lib/mongoid/slug.rb +67 -42
- data/lib/mongoid/slug/version.rb +1 -1
- data/spec/models/magazine.rb +7 -0
- data/spec/mongoid/slug_spec.rb +24 -0
- metadata +13 -17
data/README.md
CHANGED
@@ -81,5 +81,17 @@ specify the `:inverse_of` option on the other side of the assocation.
|
|
81
81
|
|
82
82
|
Embedded objects are automatically scoped by their parent.
|
83
83
|
|
84
|
+
If the value of `:scope` is not an association, it should be the name of a field within the model itself:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class Employee
|
88
|
+
include Mongoid::Document
|
89
|
+
include Mongoid::Slug
|
90
|
+
field :name
|
91
|
+
field :company_id
|
92
|
+
slug :name, :scope => :company_id
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
84
96
|
[1]: https://github.com/rsl/stringex/
|
85
|
-
[2]: https://github.com/
|
97
|
+
[2]: https://github.com/hakanensari/mongoid-slug/blob/master/lib/mongoid/slug.rb
|
data/lib/mongoid/slug.rb
CHANGED
@@ -3,8 +3,8 @@ require 'stringex'
|
|
3
3
|
|
4
4
|
module Mongoid #:nodoc:
|
5
5
|
|
6
|
-
# The slug module helps you generate a URL slug or permalink based on
|
7
|
-
# more fields in a Mongoid model.
|
6
|
+
# The slug module helps you generate a URL slug or permalink based on
|
7
|
+
# one or more fields in a Mongoid model.
|
8
8
|
#
|
9
9
|
# class Person
|
10
10
|
# include Mongoid::Document
|
@@ -18,7 +18,10 @@ module Mongoid #:nodoc:
|
|
18
18
|
extend ActiveSupport::Concern
|
19
19
|
|
20
20
|
included do
|
21
|
-
cattr_accessor :slug_builder,
|
21
|
+
cattr_accessor :slug_builder,
|
22
|
+
:slugged_fields,
|
23
|
+
:slug_name,
|
24
|
+
:slug_scope
|
22
25
|
end
|
23
26
|
|
24
27
|
module ClassMethods
|
@@ -29,26 +32,28 @@ module Mongoid #:nodoc:
|
|
29
32
|
#
|
30
33
|
# The options hash respects the following members:
|
31
34
|
#
|
32
|
-
# * `:as`, which specifies name of the field that stores the
|
33
|
-
# Defaults to `slug`.
|
35
|
+
# * `:as`, which specifies name of the field that stores the
|
36
|
+
# slug. Defaults to `slug`.
|
34
37
|
#
|
35
|
-
# * `:scope`, which specifies a reference association to scope
|
36
|
-
# by. Embedded documents are by default scoped by their
|
38
|
+
# * `:scope`, which specifies a reference association to scope
|
39
|
+
# the slug by. Embedded documents are by default scoped by their
|
40
|
+
# parent.
|
37
41
|
#
|
38
|
-
# * `:permanent`, which specifies whether the slug should be
|
39
|
-
# once created. Defaults to `false`.
|
42
|
+
# * `:permanent`, which specifies whether the slug should be
|
43
|
+
# immutable once created. Defaults to `false`.
|
40
44
|
#
|
41
|
-
# * `:index`, which specifies whether an index should be defined
|
42
|
-
# slug. Defaults to `false` and has no effect if the
|
43
|
-
#
|
44
|
-
# documents to avoid the (very unlikely) race
|
45
|
-
# if two documents with identical
|
45
|
+
# * `:index`, which specifies whether an index should be defined
|
46
|
+
# for the slug. Defaults to `false` and has no effect if the
|
47
|
+
# document is embedded. Make sure you have a unique index on the
|
48
|
+
# slug of root documents to avoid the (very unlikely) race
|
49
|
+
# condition that would ensue if two documents with identical
|
50
|
+
# slugs were to be saved simultaneously.
|
46
51
|
#
|
47
|
-
# Alternatively, this method can be given a block to build a
|
48
|
-
# out of the specified fields.
|
52
|
+
# Alternatively, this method can be given a block to build a
|
53
|
+
# custom slug out of the specified fields.
|
49
54
|
#
|
50
|
-
# The block takes a single argument, the document itself, and
|
51
|
-
# return a string that will serve as the base of the slug.
|
55
|
+
# The block takes a single argument, the document itself, and
|
56
|
+
# should return a string that will serve as the base of the slug.
|
52
57
|
#
|
53
58
|
# Here, for instance, we slug an array field.
|
54
59
|
#
|
@@ -72,7 +77,8 @@ module Mongoid #:nodoc:
|
|
72
77
|
block
|
73
78
|
else
|
74
79
|
lambda do |doc|
|
75
|
-
slugged_fields.map { |f| doc.read_attribute(f) }.
|
80
|
+
slugged_fields.map { |f| doc.read_attribute(f) }.
|
81
|
+
join(' ')
|
76
82
|
end
|
77
83
|
end
|
78
84
|
|
@@ -119,35 +125,54 @@ module Mongoid #:nodoc:
|
|
119
125
|
# TODO: An epic method which calls for refactoring.
|
120
126
|
slug = slug_builder.call(self).to_url
|
121
127
|
|
122
|
-
# Regular expression that matches slug, slug-1,
|
123
|
-
# If slug_name field was indexed, MongoDB will utilize that
|
124
|
-
# match /^.../ pattern
|
128
|
+
# Regular expression that matches slug, slug-1, ... slug-n
|
129
|
+
# If slug_name field was indexed, MongoDB will utilize that
|
130
|
+
# index to match /^.../ pattern.
|
125
131
|
pattern = /^#{Regexp.escape(slug)}(?:-(\d+))?$/
|
126
132
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
133
|
+
if slug_scope &&
|
134
|
+
self.class.reflect_on_association(slug_scope).nil?
|
135
|
+
# scope is not an association, so it's scoped to a local field
|
136
|
+
# (e.g. an association id in a denormalized db design)
|
137
|
+
existing_slugs =
|
138
|
+
self.class.
|
139
|
+
only(slug_name).
|
140
|
+
where(slug_name => pattern,
|
141
|
+
:_id.ne => _id,
|
142
|
+
slug_scope => self[slug_scope])
|
143
|
+
else
|
144
|
+
existing_slugs =
|
145
|
+
uniqueness_scope.
|
146
|
+
only(slug_name).
|
147
|
+
where(slug_name => pattern, :_id.ne => _id)
|
148
|
+
end
|
149
|
+
|
150
|
+
existing_slugs = existing_slugs.map do |obj|
|
151
|
+
obj.try(:read_attribute, slug_name)
|
152
|
+
end
|
132
153
|
|
133
|
-
if existing_slugs.count > 0
|
134
|
-
#
|
135
|
-
# numbers:
|
154
|
+
if existing_slugs.count > 0
|
155
|
+
# Sort the existing_slugs in increasing order by comparing the
|
156
|
+
# suffix numbers:
|
136
157
|
# slug, slug-1, slug-2, ..., slug-n
|
137
158
|
existing_slugs.sort! do |a, b|
|
138
|
-
(pattern.match(a)[1] || -1).to_i <=>
|
159
|
+
(pattern.match(a)[1] || -1).to_i <=>
|
160
|
+
(pattern.match(b)[1] || -1).to_i
|
139
161
|
end
|
140
|
-
|
162
|
+
max = existing_slugs.last.match(/-(\d+)$/).try(:[], 1).to_i
|
141
163
|
|
142
|
-
|
143
|
-
slug += "-#{max_counter + 1}"
|
164
|
+
slug += "-#{max + 1}"
|
144
165
|
end
|
145
166
|
|
146
167
|
slug
|
147
168
|
end
|
148
169
|
|
149
170
|
def generate_slug
|
150
|
-
if
|
171
|
+
# Generate a slug for new records only if the slug was not set.
|
172
|
+
# If we're not a new record generate a slug if our slugged fields
|
173
|
+
# changed on us.
|
174
|
+
if (new_record? && !read_attribute(slug_name)) ||
|
175
|
+
(!new_record? && slugged_fields_changed?)
|
151
176
|
generate_slug!
|
152
177
|
end
|
153
178
|
end
|
@@ -165,20 +190,20 @@ module Mongoid #:nodoc:
|
|
165
190
|
metadata = self.class.reflect_on_association(slug_scope)
|
166
191
|
parent = self.send(metadata.name)
|
167
192
|
|
168
|
-
# Make sure doc is actually associated with something, and that
|
169
|
-
# referenced docs have been persisted to the parent
|
193
|
+
# Make sure doc is actually associated with something, and that
|
194
|
+
# some referenced docs have been persisted to the parent
|
170
195
|
#
|
171
|
-
# TODO: we need better reflection for reference associations,
|
172
|
-
# association_name instead of forcing collection_name here
|
173
|
-
# in the forthcoming Mongoid refactorings?
|
196
|
+
# TODO: we need better reflection for reference associations,
|
197
|
+
# like association_name instead of forcing collection_name here
|
198
|
+
# -- maybe in the forthcoming Mongoid refactorings?
|
174
199
|
inverse = metadata.inverse_of || collection_name
|
175
200
|
parent.respond_to?(inverse) ? parent.send(inverse) : self.class
|
176
201
|
elsif embedded?
|
177
|
-
parent_metadata = reflect_on_all_associations(:embedded_in)
|
202
|
+
parent_metadata = reflect_on_all_associations(:embedded_in)[0]
|
178
203
|
_parent.send(parent_metadata.inverse_of || self.metadata.name)
|
179
204
|
else
|
180
205
|
appropriate_class = self.class
|
181
|
-
while
|
206
|
+
while appropriate_class.superclass.include?(Mongoid::Document)
|
182
207
|
appropriate_class = appropriate_class.superclass
|
183
208
|
end
|
184
209
|
appropriate_class
|
data/lib/mongoid/slug/version.rb
CHANGED
data/spec/mongoid/slug_spec.rb
CHANGED
@@ -236,6 +236,23 @@ module Mongoid
|
|
236
236
|
end
|
237
237
|
end
|
238
238
|
end
|
239
|
+
|
240
|
+
context "when slug is scoped by one of the class's own fields" do
|
241
|
+
let!(:magazine) do
|
242
|
+
Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should scope by local field" do
|
246
|
+
magazine.to_param.should eql 'big-weekly'
|
247
|
+
magazine2 = Magazine.create(:title => "Big Weekly", :publisher_id => "def456")
|
248
|
+
magazine2.to_param.should eql magazine.to_param
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should generate a unique slug by appending a counter to duplicate text" do
|
252
|
+
dup = Magazine.create(:title => "Big Weekly", :publisher_id => "abc123")
|
253
|
+
dup.to_param.should eql 'big-weekly-1'
|
254
|
+
end
|
255
|
+
end
|
239
256
|
|
240
257
|
context "when :slug is given a block" do
|
241
258
|
let(:caption) do
|
@@ -369,5 +386,12 @@ module Mongoid
|
|
369
386
|
book.reload.slug.should eql "proust-and-signs"
|
370
387
|
end
|
371
388
|
end
|
389
|
+
|
390
|
+
context "when the slugged field is set upon creation" do
|
391
|
+
it "respects the provided slug and does not generate a new one" do
|
392
|
+
book = Book.create(:title => "A Thousand Plateaus", :slug => 'not-what-you-expected')
|
393
|
+
book.to_param.should eql "not-what-you-expected"
|
394
|
+
end
|
395
|
+
end
|
372
396
|
end
|
373
397
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid_slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mongoid
|
16
|
-
requirement: &
|
16
|
+
requirement: &70354887937940 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '2.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70354887937940
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: stringex
|
27
|
-
requirement: &
|
27
|
+
requirement: &70354887953780 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.3'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70354887953780
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &70354887953320 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0.9'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70354887953320
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &70354887952860 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '2.6'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70354887952860
|
58
58
|
description: Mongoid Slug generates a URL slug or permalink based on one or more fields
|
59
59
|
in a Mongoid model.
|
60
60
|
email:
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- spec/models/author.rb
|
73
73
|
- spec/models/book.rb
|
74
74
|
- spec/models/caption.rb
|
75
|
+
- spec/models/magazine.rb
|
75
76
|
- spec/models/page.rb
|
76
77
|
- spec/models/partner.rb
|
77
78
|
- spec/models/person.rb
|
@@ -91,21 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
92
|
- - ! '>='
|
92
93
|
- !ruby/object:Gem::Version
|
93
94
|
version: '0'
|
94
|
-
segments:
|
95
|
-
- 0
|
96
|
-
hash: -3230490500102343809
|
97
95
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
96
|
none: false
|
99
97
|
requirements:
|
100
98
|
- - ! '>='
|
101
99
|
- !ruby/object:Gem::Version
|
102
100
|
version: '0'
|
103
|
-
segments:
|
104
|
-
- 0
|
105
|
-
hash: -3230490500102343809
|
106
101
|
requirements: []
|
107
102
|
rubyforge_project: mongoid_slug
|
108
|
-
rubygems_version: 1.8.
|
103
|
+
rubygems_version: 1.8.11
|
109
104
|
signing_key:
|
110
105
|
specification_version: 3
|
111
106
|
summary: Generates a URL slug
|
@@ -114,6 +109,7 @@ test_files:
|
|
114
109
|
- spec/models/author.rb
|
115
110
|
- spec/models/book.rb
|
116
111
|
- spec/models/caption.rb
|
112
|
+
- spec/models/magazine.rb
|
117
113
|
- spec/models/page.rb
|
118
114
|
- spec/models/partner.rb
|
119
115
|
- spec/models/person.rb
|