mongoid_slug 0.4.6 → 0.4.7
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/LICENSE +1 -1
- data/README.md +10 -3
- data/lib/mongoid/slug/version.rb +5 -0
- data/lib/mongoid/slug.rb +93 -0
- data/spec/models/permanent.rb +6 -0
- data/spec/mongoid/slug_spec.rb +318 -0
- data/spec/spec_helper.rb +7 -16
- metadata +71 -19
- data/lib/mongoid_slug.rb +0 -81
- data/spec/mongoid_slug_spec.rb +0 -314
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -3,8 +3,15 @@ Mongoid Slug
|
|
3
3
|
|
4
4
|
Mongoid Slug generates a URL slug or permalink based on a field or set of fields in a Mongoid model.
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
Install
|
7
|
+
-------
|
8
|
+
|
9
|
+
To install mongoid_slug, add it to your Gemfile:
|
10
|
+
|
11
|
+
gem 'mongoid_slug', :require => 'mongoid/slug'
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
8
15
|
|
9
16
|
Here's a book that embeds many authors:
|
10
17
|
|
@@ -57,4 +64,4 @@ To demo some more functionality in the console:
|
|
57
64
|
>> book.authors.where(:slug => 'felix-guattari).first
|
58
65
|
=> #<Author _id: 4c31e362faa4a7050e000003, slug: "félix-guattari", last_name: "Guattari", first_name: "Félix">
|
59
66
|
|
60
|
-
|
67
|
+
See [sample models in specs]("http://github.com/papercavalier/mongoid-slug/tree/master/spec/models/") for various configuration options.
|
data/lib/mongoid/slug.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
|
4
|
+
# Generates a URL slug/permalink based on fields in a Mongoid model
|
5
|
+
module Slug
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
cattr_accessor :slug_name, :slugged_fields, :slug_scoped
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# Set a field or a number of fields as source of slug
|
15
|
+
def slug(*args)
|
16
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
17
|
+
|
18
|
+
self.slug_name = options[:as] || :slug
|
19
|
+
self.slug_scoped = options[:scoped] || false
|
20
|
+
self.slugged_fields = args
|
21
|
+
|
22
|
+
field slug_name
|
23
|
+
|
24
|
+
if slug_scoped
|
25
|
+
index slug_name
|
26
|
+
else
|
27
|
+
index slug_name, :unique => true
|
28
|
+
end
|
29
|
+
|
30
|
+
if options[:permanent]
|
31
|
+
before_create :generate_slug
|
32
|
+
else
|
33
|
+
before_save :generate_slug
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_param
|
39
|
+
self.send slug_name
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def find_(slug, stack=[])
|
45
|
+
if embedded?
|
46
|
+
if slug_scoped && stack.empty?
|
47
|
+
_parent.send(association_name).where( slug_name => slug ).to_a
|
48
|
+
else
|
49
|
+
stack << association_name
|
50
|
+
_parent.send :find_, slug, stack
|
51
|
+
end
|
52
|
+
else
|
53
|
+
stack.reverse!
|
54
|
+
path = (stack + [slug_name]).join(".")
|
55
|
+
found = collection.find(path => slug).to_a
|
56
|
+
|
57
|
+
stack.each do |name|
|
58
|
+
if found.any?
|
59
|
+
found = found.first.send(name).to_a
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
found
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_unique_slug(suffix='')
|
68
|
+
slug = ("#{slug_base} #{suffix}").parameterize
|
69
|
+
if find_(slug).reject{ |doc| doc.id == self.id }.empty?
|
70
|
+
slug
|
71
|
+
else
|
72
|
+
suffix = suffix.blank? ? '1' : "#{suffix.to_i + 1}"
|
73
|
+
find_unique_slug(suffix)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def generate_slug
|
78
|
+
if new_record? || slugged_fields_changed?
|
79
|
+
self.send("#{slug_name}=", find_unique_slug)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def slug_base
|
84
|
+
self.class.slugged_fields.collect{ |field| self.send(field) }.join(" ")
|
85
|
+
end
|
86
|
+
|
87
|
+
def slugged_fields_changed?
|
88
|
+
self.class.slugged_fields.any? do |field|
|
89
|
+
self.send(field.to_s + '_changed?')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
|
4
|
+
module Mongoid
|
5
|
+
|
6
|
+
describe Slug do
|
7
|
+
|
8
|
+
let(:book) { Book.create(:title => "A Thousand Plateaus", :isbn => "9789245242475") }
|
9
|
+
|
10
|
+
context "when document is root" do
|
11
|
+
|
12
|
+
it "generates slug" do
|
13
|
+
book.to_param.should eql book.title.parameterize
|
14
|
+
end
|
15
|
+
|
16
|
+
it "updates slug" do
|
17
|
+
book.update_attributes(:title => "Anti Oedipus")
|
18
|
+
book.to_param.should eql "Anti Oedipus".parameterize
|
19
|
+
end
|
20
|
+
|
21
|
+
it "generates a unique slug" do
|
22
|
+
similar_book = Book.create(:title => book.title)
|
23
|
+
similar_book.to_param.should_not eql book.to_param
|
24
|
+
end
|
25
|
+
|
26
|
+
it "appends a counter when slug is not unique" do
|
27
|
+
similar_book = Book.create(:title => book.title)
|
28
|
+
similar_book.to_param.should match /\d$/
|
29
|
+
end
|
30
|
+
|
31
|
+
it "does not append a counter when slug is unique" do
|
32
|
+
book.to_param.should_not match /\d$/
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does not update slug if slugged fields have not changed" do
|
36
|
+
former_slug = book.to_param
|
37
|
+
book.update_attributes(:isbn => "9785545858118")
|
38
|
+
book.to_param.should eql former_slug
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
42
|
+
former_slug = book.to_param
|
43
|
+
book.update_attributes(:title => "A thousand plateaus")
|
44
|
+
book.to_param.should eql former_slug
|
45
|
+
end
|
46
|
+
|
47
|
+
it "finds by slug" do
|
48
|
+
Book.where(:slug => book.to_param).first.should eql book
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when document is an embedded has-many" do
|
54
|
+
|
55
|
+
let(:subject) { book.subjects.create(:name => "Psychoanalysis") }
|
56
|
+
|
57
|
+
it "generates slug" do
|
58
|
+
subject.to_param.should eql(subject.name.parameterize)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "updates slug" do
|
62
|
+
subject.update_attributes(:name => "Schizoanalysis")
|
63
|
+
subject.to_param.should eql "Schizoanalysis".parameterize
|
64
|
+
end
|
65
|
+
|
66
|
+
it "generates a unique slug" do
|
67
|
+
similar_subject = book.subjects.create(:model => subject.name)
|
68
|
+
similar_subject.to_param.should_not eql subject.to_param
|
69
|
+
end
|
70
|
+
|
71
|
+
it "appends a counter when slug is not unique" do
|
72
|
+
similar_subject = book.subjects.create(:name => subject.name)
|
73
|
+
similar_subject.to_param.should match /\d$/
|
74
|
+
end
|
75
|
+
|
76
|
+
it "does not append a counter when slug is unique" do
|
77
|
+
subject.to_param.should_not match /\d$/
|
78
|
+
end
|
79
|
+
|
80
|
+
it "does not update slug if slugged fields have not changed" do
|
81
|
+
former_slug = subject.to_param
|
82
|
+
subject.update_attributes(:description => "Lorem ipsum dolor sit amet")
|
83
|
+
subject.to_param.should eql former_slug
|
84
|
+
end
|
85
|
+
|
86
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
87
|
+
former_slug = subject.to_param
|
88
|
+
subject.update_attributes(:title => "PSYCHOANALYSIS")
|
89
|
+
subject.to_param.should eql former_slug
|
90
|
+
end
|
91
|
+
|
92
|
+
it "finds by slug" do
|
93
|
+
book.subjects.where(:slug => subject.to_param).first.should eql subject
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
context "when document is an embedded has-one" do
|
99
|
+
|
100
|
+
let(:publisher) { book.create_publisher(:name => "OUP") }
|
101
|
+
|
102
|
+
it "generates slug" do
|
103
|
+
publisher.to_param.should eql(publisher.name.parameterize)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "updates slug" do
|
107
|
+
publisher.update_attributes(:name => "Harvard UP")
|
108
|
+
publisher.to_param.should eql "Harvard UP".parameterize
|
109
|
+
end
|
110
|
+
|
111
|
+
it "does not update slug if slugged fields have not changed" do
|
112
|
+
former_slug = publisher.to_param
|
113
|
+
publisher.update_attributes(:year => 2001)
|
114
|
+
publisher.to_param.should eql former_slug
|
115
|
+
end
|
116
|
+
|
117
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
118
|
+
former_slug = publisher.to_param
|
119
|
+
publisher.update_attributes(:name => "oup")
|
120
|
+
publisher.to_param.should eql former_slug
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
context "when the slug is composed of multiple fields" do
|
126
|
+
|
127
|
+
let(:author) { Author.create(:first_name => "Gilles", :last_name => "Deleuze") }
|
128
|
+
|
129
|
+
it "generates slug" do
|
130
|
+
author.to_param.should eql("Gilles Deleuze".parameterize)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "updates slug" do
|
134
|
+
author.update_attributes(:first_name => "Félix", :last_name => "Guattari")
|
135
|
+
author.to_param.should eql "Félix Guattari".parameterize
|
136
|
+
end
|
137
|
+
|
138
|
+
it "generates a unique slug" do
|
139
|
+
similar_author = Author.create(:first_name => author.first_name,
|
140
|
+
:last_name => author.last_name)
|
141
|
+
similar_author.to_param.should_not eql author.to_param
|
142
|
+
end
|
143
|
+
|
144
|
+
it "appends a counter when slug is not unique" do
|
145
|
+
similar_author = Author.create(:first_name => author.first_name,
|
146
|
+
:last_name => author.last_name)
|
147
|
+
similar_author.to_param.should match /\d$/
|
148
|
+
end
|
149
|
+
|
150
|
+
it "does not append a counter when slug is unique" do
|
151
|
+
author.to_param.should_not match /\d$/
|
152
|
+
end
|
153
|
+
|
154
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
155
|
+
former_slug = author.to_param
|
156
|
+
author.update_attributes(:first_name => "gilles", :last_name => "DELEUZE")
|
157
|
+
author.to_param.should eql former_slug
|
158
|
+
end
|
159
|
+
|
160
|
+
it "finds by slug" do
|
161
|
+
author
|
162
|
+
Author.where(:slug => "gilles-deleuze").first.should eql author
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when the document is embedded in another embedded document" do
|
168
|
+
|
169
|
+
let(:foo) { Foo.create(:name => "foo") }
|
170
|
+
let(:bar) { foo.bars.create(:name => "bar") }
|
171
|
+
let(:baz) do
|
172
|
+
bar.bazes.create(:name => "baz")
|
173
|
+
Foo.first.bars.first.bazes.first # Better to be paranoid and reload from db
|
174
|
+
end
|
175
|
+
|
176
|
+
it "generates slug" do
|
177
|
+
baz.to_param.should eql(baz.name.parameterize)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "updates slug" do
|
181
|
+
baz.update_attributes(:name => "lorem")
|
182
|
+
baz.to_param.should eql "lorem".parameterize
|
183
|
+
end
|
184
|
+
|
185
|
+
it "generates a unique slug" do
|
186
|
+
similar_baz = bar.bazes.create(:name => baz.name)
|
187
|
+
similar_baz.to_param.should_not eql baz.to_param
|
188
|
+
end
|
189
|
+
|
190
|
+
it "appends a counter when slug is not unique" do
|
191
|
+
similar_baz = bar.bazes.create(:name => baz.name)
|
192
|
+
similar_baz.to_param.should match /\d$/
|
193
|
+
end
|
194
|
+
|
195
|
+
it "does not append a counter when slug is unique" do
|
196
|
+
baz.to_param.should_not match /\d$/
|
197
|
+
end
|
198
|
+
|
199
|
+
it "does not update slug if slugged fields have not changed" do
|
200
|
+
former_slug = baz.to_param
|
201
|
+
baz.update_attributes(:other => "Lorem ipsum dolor sit amet")
|
202
|
+
baz.to_param.should eql former_slug
|
203
|
+
end
|
204
|
+
|
205
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
206
|
+
former_slug = baz.to_param
|
207
|
+
baz.update_attributes(:name => "BAZ")
|
208
|
+
baz.to_param.should eql former_slug
|
209
|
+
end
|
210
|
+
|
211
|
+
it "finds by slug" do
|
212
|
+
bar.bazes.where(:slug => baz.to_param).first.should eql baz
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
context "when the slug field name is customized" do
|
218
|
+
|
219
|
+
let(:person) { Person.create(:name => "John Doe") }
|
220
|
+
|
221
|
+
it "sets the slug field name" do
|
222
|
+
person.respond_to?(:permalink).should be_true
|
223
|
+
person.send(:permalink).should eql "john-doe"
|
224
|
+
end
|
225
|
+
|
226
|
+
it "generates slug" do
|
227
|
+
person.to_param.should eql(person.name.parameterize)
|
228
|
+
end
|
229
|
+
|
230
|
+
it "updates slug" do
|
231
|
+
person.update_attributes(:name => "Jane Doe")
|
232
|
+
person.to_param.should eql "Jane Doe".parameterize
|
233
|
+
end
|
234
|
+
|
235
|
+
it "generates a unique slug" do
|
236
|
+
similar_person = Person.create(:name => person.name)
|
237
|
+
similar_person.to_param.should_not eql person.to_param
|
238
|
+
end
|
239
|
+
|
240
|
+
it "appends a counter when slug is not unique" do
|
241
|
+
similar_person = Person.create(:name => person.name)
|
242
|
+
similar_person.to_param.should match /\d$/
|
243
|
+
end
|
244
|
+
|
245
|
+
it "does not append a counter when slug is unique" do
|
246
|
+
person.to_param.should_not match /\d$/
|
247
|
+
end
|
248
|
+
|
249
|
+
it "does not update slug if slugged fields have not changed" do
|
250
|
+
former_slug = person.to_param
|
251
|
+
person.update_attributes(:age => 31)
|
252
|
+
person.to_param.should eql former_slug
|
253
|
+
end
|
254
|
+
|
255
|
+
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
256
|
+
former_slug = person.to_param
|
257
|
+
person.update_attributes(:name => "JOHN DOE")
|
258
|
+
person.to_param.should eql former_slug
|
259
|
+
end
|
260
|
+
|
261
|
+
it "finds by slug" do
|
262
|
+
Person.where(:permalink => person.to_param).first.should eql person
|
263
|
+
end
|
264
|
+
|
265
|
+
end
|
266
|
+
|
267
|
+
describe "#find_" do
|
268
|
+
|
269
|
+
let(:foo) { Foo.create(:name => "foo") }
|
270
|
+
let(:bar) { foo.bars.create(:name => "bar") }
|
271
|
+
let(:baz) { bar.bazes.create(:name => "baz") }
|
272
|
+
|
273
|
+
it "finds duplicate slug of a root document" do
|
274
|
+
foo.send(:find_, foo.to_param).count.should eql 1
|
275
|
+
end
|
276
|
+
|
277
|
+
it "finds duplicate slug of an embedded document" do
|
278
|
+
bar.send(:find_, bar.to_param).count.should eql 1
|
279
|
+
end
|
280
|
+
|
281
|
+
it "finds duplicate slug of a deeply-embedded document" do
|
282
|
+
baz.send(:find_, baz.to_param).count.should eql 1
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
context "when slug is scoped" do
|
288
|
+
|
289
|
+
let(:foo) { Foo.create(:name => "foo") }
|
290
|
+
let(:sar) { foo.sars.create(:name => "sar") }
|
291
|
+
let(:foo2) { Foo.create(:name => "foo") }
|
292
|
+
|
293
|
+
it "generates a unique slug inside the same parent object" do
|
294
|
+
similar_sar = foo.sars.create(:name => sar.name)
|
295
|
+
similar_sar.to_param.should_not eql sar.to_param
|
296
|
+
end
|
297
|
+
|
298
|
+
it "generates the same slug in different parent object" do
|
299
|
+
other_sar = foo2.sars.create(:name => sar.name)
|
300
|
+
other_sar.to_param.should eql sar.to_param
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
context "when slug is permanent" do
|
306
|
+
|
307
|
+
let(:permanent) { Permanent.create(:title => "This is a nodding song") }
|
308
|
+
|
309
|
+
it "doesn't change slug when document is updated" do
|
310
|
+
permanent.update_attributes(:title => "This is not a nodding song")
|
311
|
+
permanent.to_param.should eql "This is a nodding song".parameterize
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,30 +1,21 @@
|
|
1
1
|
require "rubygems"
|
2
2
|
require "bundler/setup"
|
3
3
|
|
4
|
-
require "rspec"
|
5
4
|
require "database_cleaner"
|
6
5
|
require "mongoid"
|
6
|
+
require "rspec"
|
7
7
|
|
8
8
|
Mongoid.configure do |config|
|
9
9
|
name = "mongoid_slug_test"
|
10
10
|
config.master = Mongo::Connection.new.db(name)
|
11
11
|
end
|
12
12
|
|
13
|
-
require File.expand_path("../../lib/
|
14
|
-
Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |f| require f }
|
15
|
-
|
16
|
-
DatabaseCleaner.orm = "mongoid"
|
17
|
-
|
18
|
-
Rspec.configure do |config|
|
19
|
-
config.before(:all) do
|
20
|
-
DatabaseCleaner.strategy = :truncation
|
21
|
-
end
|
13
|
+
require File.expand_path("../../lib/mongoid/slug", __FILE__)
|
22
14
|
|
23
|
-
|
24
|
-
DatabaseCleaner.start
|
25
|
-
end
|
15
|
+
Dir["#{File.dirname(__FILE__)}/models/*.rb"].each { |f| require f }
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
17
|
+
Rspec.configure do |c|
|
18
|
+
c.before(:all) { DatabaseCleaner.strategy = :truncation }
|
19
|
+
c.before(:each) { DatabaseCleaner.start }
|
20
|
+
c.after(:each) { DatabaseCleaner.clean }
|
30
21
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid_slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 7
|
10
|
+
version: 0.4.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Hakan Ensari
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-10-31 01:00:00 +01:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -27,46 +27,97 @@ dependencies:
|
|
27
27
|
requirements:
|
28
28
|
- - ~>
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
hash:
|
30
|
+
hash: 62196421
|
31
31
|
segments:
|
32
32
|
- 2
|
33
33
|
- 0
|
34
34
|
- 0
|
35
35
|
- beta
|
36
|
-
|
36
|
+
- 19
|
37
|
+
version: 2.0.0.beta.19
|
37
38
|
type: :runtime
|
38
39
|
version_requirements: *id001
|
39
|
-
|
40
|
-
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: bson_ext
|
42
|
+
prerelease: false
|
43
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
hash: 17
|
49
|
+
segments:
|
50
|
+
- 1
|
51
|
+
- 1
|
52
|
+
- 1
|
53
|
+
version: 1.1.1
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id002
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: database_cleaner
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ~>
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
hash: 7
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
- 6
|
68
|
+
- 0
|
69
|
+
version: 0.6.0
|
70
|
+
type: :development
|
71
|
+
version_requirements: *id003
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: rspec
|
74
|
+
prerelease: false
|
75
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ~>
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 13
|
81
|
+
segments:
|
82
|
+
- 2
|
83
|
+
- 0
|
84
|
+
- 1
|
85
|
+
version: 2.0.1
|
86
|
+
type: :development
|
87
|
+
version_requirements: *id004
|
88
|
+
description: Generates a URL slug or permalink based on fields in a Mongoid model.
|
89
|
+
email:
|
90
|
+
- code@papercavalier.com
|
41
91
|
executables: []
|
42
92
|
|
43
93
|
extensions: []
|
44
94
|
|
45
|
-
extra_rdoc_files:
|
46
|
-
|
47
|
-
- README.md
|
95
|
+
extra_rdoc_files: []
|
96
|
+
|
48
97
|
files:
|
98
|
+
- lib/mongoid/slug/version.rb
|
99
|
+
- lib/mongoid/slug.rb
|
49
100
|
- LICENSE
|
50
|
-
- lib/mongoid_slug.rb
|
51
101
|
- README.md
|
52
102
|
- spec/models/author.rb
|
53
103
|
- spec/models/bar.rb
|
54
104
|
- spec/models/baz.rb
|
55
105
|
- spec/models/book.rb
|
56
106
|
- spec/models/foo.rb
|
107
|
+
- spec/models/permanent.rb
|
57
108
|
- spec/models/person.rb
|
58
109
|
- spec/models/publisher.rb
|
59
110
|
- spec/models/sar.rb
|
60
111
|
- spec/models/subject.rb
|
61
|
-
- spec/
|
112
|
+
- spec/mongoid/slug_spec.rb
|
62
113
|
- spec/spec_helper.rb
|
63
114
|
has_rdoc: true
|
64
|
-
homepage: http://github.com/papercavalier/
|
115
|
+
homepage: http://github.com/papercavalier/mongoid_slug
|
65
116
|
licenses: []
|
66
117
|
|
67
118
|
post_install_message:
|
68
|
-
rdoc_options:
|
69
|
-
|
119
|
+
rdoc_options: []
|
120
|
+
|
70
121
|
require_paths:
|
71
122
|
- lib
|
72
123
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -89,20 +140,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
140
|
version: "0"
|
90
141
|
requirements: []
|
91
142
|
|
92
|
-
rubyforge_project:
|
143
|
+
rubyforge_project: mongoid_slug
|
93
144
|
rubygems_version: 1.3.7
|
94
145
|
signing_key:
|
95
146
|
specification_version: 3
|
96
|
-
summary: Generates a URL slug
|
147
|
+
summary: Generates a URL slug or permalink
|
97
148
|
test_files:
|
98
149
|
- spec/models/author.rb
|
99
150
|
- spec/models/bar.rb
|
100
151
|
- spec/models/baz.rb
|
101
152
|
- spec/models/book.rb
|
102
153
|
- spec/models/foo.rb
|
154
|
+
- spec/models/permanent.rb
|
103
155
|
- spec/models/person.rb
|
104
156
|
- spec/models/publisher.rb
|
105
157
|
- spec/models/sar.rb
|
106
158
|
- spec/models/subject.rb
|
107
|
-
- spec/
|
159
|
+
- spec/mongoid/slug_spec.rb
|
108
160
|
- spec/spec_helper.rb
|
data/lib/mongoid_slug.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
# Generates a URL slug/permalink based on fields in a Mongoid model.
|
2
|
-
module Mongoid::Slug
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
cattr_accessor :slug_name, :slugged_fields, :slug_scoped
|
7
|
-
end
|
8
|
-
|
9
|
-
module ClassMethods #:nodoc:
|
10
|
-
# Set a field or a number of fields as source of slug
|
11
|
-
def slug(*args)
|
12
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
13
|
-
self.slug_name = options[:as] || :slug
|
14
|
-
self.slug_scoped = options[:scoped] || false
|
15
|
-
self.slugged_fields = args
|
16
|
-
|
17
|
-
field slug_name
|
18
|
-
if slug_scoped
|
19
|
-
index slug_name
|
20
|
-
else
|
21
|
-
index slug_name, :unique => true
|
22
|
-
end
|
23
|
-
before_save :generate_slug
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def to_param
|
28
|
-
self.send slug_name
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def find_(slug, stack=[])
|
34
|
-
if embedded?
|
35
|
-
if slug_scoped && stack.empty?
|
36
|
-
_parent.send(association_name).where( slug_name => slug ).to_a
|
37
|
-
else
|
38
|
-
stack << association_name
|
39
|
-
_parent.send :find_, slug, stack
|
40
|
-
end
|
41
|
-
else
|
42
|
-
stack.reverse!
|
43
|
-
path = (stack + [slug_name]).join(".")
|
44
|
-
found = collection.find(path => slug).to_a
|
45
|
-
|
46
|
-
stack.each do |name|
|
47
|
-
if found.any?
|
48
|
-
found = found.first.send(name).to_a
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
found
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def find_unique_slug(suffix='')
|
57
|
-
slug = ("#{slug_base} #{suffix}").parameterize
|
58
|
-
if find_(slug).reject{ |doc| doc.id == self.id }.empty?
|
59
|
-
slug
|
60
|
-
else
|
61
|
-
suffix = suffix.blank? ? '1' : "#{suffix.to_i + 1}"
|
62
|
-
find_unique_slug(suffix)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def generate_slug
|
67
|
-
if new_record? || slugged_fields_changed?
|
68
|
-
self.send("#{slug_name}=", find_unique_slug)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def slug_base
|
73
|
-
self.class.slugged_fields.collect{ |field| self.send(field) }.join(" ")
|
74
|
-
end
|
75
|
-
|
76
|
-
def slugged_fields_changed?
|
77
|
-
self.class.slugged_fields.any? do |field|
|
78
|
-
self.send(field.to_s + '_changed?')
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
data/spec/mongoid_slug_spec.rb
DELETED
@@ -1,314 +0,0 @@
|
|
1
|
-
#encoding: utf-8
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
describe Mongoid::Slug do
|
6
|
-
|
7
|
-
before(:each) do
|
8
|
-
@book = Book.create(:title => "A Thousand Plateaus", :isbn => "9789245242475")
|
9
|
-
end
|
10
|
-
|
11
|
-
context "root document" do
|
12
|
-
|
13
|
-
it "generates slug" do
|
14
|
-
@book.to_param.should eql @book.title.parameterize
|
15
|
-
end
|
16
|
-
|
17
|
-
it "updates slug" do
|
18
|
-
@book.update_attributes(:title => "Anti Oedipus")
|
19
|
-
@book.to_param.should eql "Anti Oedipus".parameterize
|
20
|
-
end
|
21
|
-
|
22
|
-
it "generates a unique slug" do
|
23
|
-
similar_book = Book.create(:title => @book.title)
|
24
|
-
similar_book.to_param.should_not eql @book.to_param
|
25
|
-
end
|
26
|
-
|
27
|
-
it "appends a counter when slug is not unique" do
|
28
|
-
similar_book = Book.create(:title => @book.title)
|
29
|
-
similar_book.to_param.should match /\d$/
|
30
|
-
end
|
31
|
-
|
32
|
-
it "does not append a counter when slug is unique" do
|
33
|
-
@book.to_param.should_not match /\d$/
|
34
|
-
end
|
35
|
-
|
36
|
-
it "does not update slug if slugged fields have not changed" do
|
37
|
-
former_slug = @book.to_param
|
38
|
-
@book.update_attributes(:isbn => "9785545858118")
|
39
|
-
@book.to_param.should eql former_slug
|
40
|
-
end
|
41
|
-
|
42
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
43
|
-
former_slug = @book.to_param
|
44
|
-
@book.update_attributes(:title => "A thousand plateaus")
|
45
|
-
@book.to_param.should eql former_slug
|
46
|
-
end
|
47
|
-
|
48
|
-
it "finds by slug" do
|
49
|
-
Book.where(:slug => @book.to_param).first.should eql @book
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
context "embedded has many" do
|
55
|
-
|
56
|
-
before(:each) do
|
57
|
-
@subject = @book.subjects.create(:name => "Psychoanalysis")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "generates slug" do
|
61
|
-
@subject.to_param.should eql(@subject.name.parameterize)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "updates slug" do
|
65
|
-
@subject.update_attributes(:name => "Schizoanalysis")
|
66
|
-
@subject.to_param.should eql "Schizoanalysis".parameterize
|
67
|
-
end
|
68
|
-
|
69
|
-
it "generates a unique slug" do
|
70
|
-
similar_subject = @book.subjects.create(:model => @subject.name)
|
71
|
-
similar_subject.to_param.should_not eql @subject.to_param
|
72
|
-
end
|
73
|
-
|
74
|
-
it "appends a counter when slug is not unique" do
|
75
|
-
similar_subject = @book.subjects.create(:name => @subject.name)
|
76
|
-
similar_subject.to_param.should match /\d$/
|
77
|
-
end
|
78
|
-
|
79
|
-
it "does not append a counter when slug is unique" do
|
80
|
-
@subject.to_param.should_not match /\d$/
|
81
|
-
end
|
82
|
-
|
83
|
-
it "does not update slug if slugged fields have not changed" do
|
84
|
-
former_slug = @subject.to_param
|
85
|
-
@subject.update_attributes(:description => "Lorem ipsum dolor sit amet")
|
86
|
-
@subject.to_param.should eql former_slug
|
87
|
-
end
|
88
|
-
|
89
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
90
|
-
former_slug = @subject.to_param
|
91
|
-
@subject.update_attributes(:title => "PSYCHOANALYSIS")
|
92
|
-
@subject.to_param.should eql former_slug
|
93
|
-
end
|
94
|
-
|
95
|
-
it "finds by slug" do
|
96
|
-
@book.subjects.where(:slug => @subject.to_param).first.should eql @subject
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
100
|
-
|
101
|
-
context "embedded has one" do
|
102
|
-
|
103
|
-
before(:each) do
|
104
|
-
@publisher = @book.create_publisher(:name => "OUP")
|
105
|
-
end
|
106
|
-
|
107
|
-
it "generates slug" do
|
108
|
-
@publisher.to_param.should eql(@publisher.name.parameterize)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "updates slug" do
|
112
|
-
@publisher.update_attributes(:name => "Harvard UP")
|
113
|
-
@publisher.to_param.should eql "Harvard UP".parameterize
|
114
|
-
end
|
115
|
-
|
116
|
-
it "does not update slug if slugged fields have not changed" do
|
117
|
-
former_slug = @publisher.to_param
|
118
|
-
@publisher.update_attributes(:year => 2001)
|
119
|
-
@publisher.to_param.should eql former_slug
|
120
|
-
end
|
121
|
-
|
122
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
123
|
-
former_slug = @publisher.to_param
|
124
|
-
@publisher.update_attributes(:name => "oup")
|
125
|
-
@publisher.to_param.should eql former_slug
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
|
-
context "composite fields" do
|
131
|
-
before(:each) do
|
132
|
-
@author = Author.create(:first_name => "Gilles", :last_name => "Deleuze")
|
133
|
-
end
|
134
|
-
|
135
|
-
it "generates slug" do
|
136
|
-
@author.to_param.should eql("Gilles Deleuze".parameterize)
|
137
|
-
end
|
138
|
-
|
139
|
-
it "updates slug" do
|
140
|
-
@author.update_attributes(:first_name => "Félix", :last_name => "Guattari")
|
141
|
-
@author.to_param.should eql "Félix Guattari".parameterize
|
142
|
-
end
|
143
|
-
|
144
|
-
it "generates a unique slug" do
|
145
|
-
similar_author = Author.create(:first_name => @author.first_name,
|
146
|
-
:last_name => @author.last_name)
|
147
|
-
similar_author.to_param.should_not eql @author.to_param
|
148
|
-
end
|
149
|
-
|
150
|
-
it "appends a counter when slug is not unique" do
|
151
|
-
similar_author = Author.create(:first_name => @author.first_name,
|
152
|
-
:last_name => @author.last_name)
|
153
|
-
similar_author.to_param.should match /\d$/
|
154
|
-
end
|
155
|
-
|
156
|
-
it "does not append a counter when slug is unique" do
|
157
|
-
@author.to_param.should_not match /\d$/
|
158
|
-
end
|
159
|
-
|
160
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
161
|
-
former_slug = @author.to_param
|
162
|
-
@author.update_attributes(:first_name => "gilles", :last_name => "DELEUZE")
|
163
|
-
@author.to_param.should eql former_slug
|
164
|
-
end
|
165
|
-
|
166
|
-
it "finds by slug" do
|
167
|
-
Author.where(:slug => "gilles-deleuze").first.should eql @author
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
context "deeply embedded relationships" do
|
173
|
-
|
174
|
-
before(:each) do
|
175
|
-
@foo = Foo.create(:name => "foo")
|
176
|
-
@bar = @foo.bars.create(:name => "bar")
|
177
|
-
@baz = @bar.bazes.create(:name => "baz")
|
178
|
-
@baz = Foo.first.bars.first.bazes.first # Better to be paranoid and reload from db
|
179
|
-
end
|
180
|
-
|
181
|
-
it "generates slug" do
|
182
|
-
@baz.to_param.should eql(@baz.name.parameterize)
|
183
|
-
end
|
184
|
-
|
185
|
-
it "updates slug" do
|
186
|
-
@baz.update_attributes(:name => "lorem")
|
187
|
-
@baz.to_param.should eql "lorem".parameterize
|
188
|
-
end
|
189
|
-
|
190
|
-
it "generates a unique slug" do
|
191
|
-
similar_baz = @bar.bazes.create(:name => @baz.name)
|
192
|
-
similar_baz.to_param.should_not eql @baz.to_param
|
193
|
-
end
|
194
|
-
|
195
|
-
it "appends a counter when slug is not unique" do
|
196
|
-
similar_baz = @bar.bazes.create(:name => @baz.name)
|
197
|
-
similar_baz.to_param.should match /\d$/
|
198
|
-
end
|
199
|
-
|
200
|
-
it "does not append a counter when slug is unique" do
|
201
|
-
@baz.to_param.should_not match /\d$/
|
202
|
-
end
|
203
|
-
|
204
|
-
it "does not update slug if slugged fields have not changed" do
|
205
|
-
former_slug = @baz.to_param
|
206
|
-
@baz.update_attributes(:other => "Lorem ipsum dolor sit amet")
|
207
|
-
@baz.to_param.should eql former_slug
|
208
|
-
end
|
209
|
-
|
210
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
211
|
-
former_slug = @baz.to_param
|
212
|
-
@baz.update_attributes(:name => "BAZ")
|
213
|
-
@baz.to_param.should eql former_slug
|
214
|
-
end
|
215
|
-
|
216
|
-
it "finds by slug" do
|
217
|
-
@bar.bazes.where(:slug => @baz.to_param).first.should eql @baz
|
218
|
-
end
|
219
|
-
|
220
|
-
end
|
221
|
-
|
222
|
-
context ":as option" do
|
223
|
-
|
224
|
-
before(:each) do
|
225
|
-
@person = Person.create(:name => "John Doe")
|
226
|
-
end
|
227
|
-
|
228
|
-
it "should set the slug field name" do
|
229
|
-
@person.respond_to?(:permalink).should be_true
|
230
|
-
@person.send(:permalink).should eql "john-doe"
|
231
|
-
end
|
232
|
-
|
233
|
-
it "generates slug" do
|
234
|
-
@person.to_param.should eql(@person.name.parameterize)
|
235
|
-
end
|
236
|
-
|
237
|
-
it "updates slug" do
|
238
|
-
@person.update_attributes(:name => "Jane Doe")
|
239
|
-
@person.to_param.should eql "Jane Doe".parameterize
|
240
|
-
end
|
241
|
-
|
242
|
-
it "generates a unique slug" do
|
243
|
-
similar_person = Person.create(:name => @person.name)
|
244
|
-
similar_person.to_param.should_not eql @person.to_param
|
245
|
-
end
|
246
|
-
|
247
|
-
it "appends a counter when slug is not unique" do
|
248
|
-
similar_person = Person.create(:name => @person.name)
|
249
|
-
similar_person.to_param.should match /\d$/
|
250
|
-
end
|
251
|
-
|
252
|
-
it "does not append a counter when slug is unique" do
|
253
|
-
@person.to_param.should_not match /\d$/
|
254
|
-
end
|
255
|
-
|
256
|
-
it "does not update slug if slugged fields have not changed" do
|
257
|
-
former_slug = @person.to_param
|
258
|
-
@person.update_attributes(:age => 31)
|
259
|
-
@person.to_param.should eql former_slug
|
260
|
-
end
|
261
|
-
|
262
|
-
it "does not update slug if slugged fields have changed but generated slug is the same" do
|
263
|
-
former_slug = @person.to_param
|
264
|
-
@person.update_attributes(:name => "JOHN DOE")
|
265
|
-
@person.to_param.should eql former_slug
|
266
|
-
end
|
267
|
-
|
268
|
-
it "finds by slug" do
|
269
|
-
Person.where(:permalink => @person.to_param).first.should eql @person
|
270
|
-
end
|
271
|
-
|
272
|
-
end
|
273
|
-
|
274
|
-
context "#find_" do
|
275
|
-
|
276
|
-
before(:each) do
|
277
|
-
@foo = Foo.create(:name => "foo")
|
278
|
-
@bar = @foo.bars.create(:name => "bar")
|
279
|
-
@baz = @bar.bazes.create(:name => "baz")
|
280
|
-
end
|
281
|
-
|
282
|
-
it "finds duplicate slug of a root document" do
|
283
|
-
@foo.send(:find_, @foo.to_param).count.should eql 1
|
284
|
-
end
|
285
|
-
|
286
|
-
it "finds duplicate slug of an embedded document" do
|
287
|
-
@bar.send(:find_, @bar.to_param).count.should eql 1
|
288
|
-
end
|
289
|
-
|
290
|
-
it "finds duplicate slug of a deeply-embedded document" do
|
291
|
-
@baz.send(:find_, @baz.to_param).count.should eql 1
|
292
|
-
end
|
293
|
-
|
294
|
-
end
|
295
|
-
|
296
|
-
context ":scoped option" do
|
297
|
-
before(:each) do
|
298
|
-
@foo = Foo.create(:name => "foo")
|
299
|
-
@sar = @foo.sars.create(:name => "sar")
|
300
|
-
@foo2 = Foo.create(:name => "foo")
|
301
|
-
end
|
302
|
-
|
303
|
-
it "generates a unique slug inside the same parent object" do
|
304
|
-
similar_sar = @foo.sars.create(:name => @sar.name)
|
305
|
-
similar_sar.to_param.should_not eql @sar.to_param
|
306
|
-
end
|
307
|
-
|
308
|
-
it "generates the same slug in different parent object" do
|
309
|
-
other_sar = @foo2.sars.create(:name => @sar.name)
|
310
|
-
other_sar.to_param.should eql @sar.to_param
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
end
|