mongoid_slug 0.6.4 → 0.7.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 +9 -41
- data/lib/mongoid/slug.rb +74 -55
- data/lib/mongoid/slug/version.rb +1 -1
- data/spec/models/article.rb +3 -1
- data/spec/models/caption.rb +10 -0
- data/spec/models/comic_book.rb +1 -2
- data/spec/mongoid/slug_spec.rb +35 -31
- metadata +5 -47
data/README.md
CHANGED
@@ -2,10 +2,9 @@ Mongoid Slug
|
|
2
2
|
============
|
3
3
|
|
4
4
|
Mongoid Slug generates a URL slug or permalink based on one or more
|
5
|
-
fields in a Mongoid model.
|
5
|
+
fields in a Mongoid model. It sits idly on top of [stringex](https://github.com/rsl/stringex) and works with non-Latin characters.
|
6
6
|
|
7
|
-
|
8
|
-
works with non-Latin characters.
|
7
|
+

|
9
8
|
|
10
9
|
Quick Start
|
11
10
|
-----------
|
@@ -14,7 +13,7 @@ Add mongoid_slug to your Gemfile:
|
|
14
13
|
|
15
14
|
gem 'mongoid_slug', :require => 'mongoid/slug'
|
16
15
|
|
17
|
-
Set up slugs
|
16
|
+
Set up some slugs:
|
18
17
|
|
19
18
|
class Book
|
20
19
|
include Mongoid::Document
|
@@ -37,36 +36,20 @@ Set up slugs in models like this:
|
|
37
36
|
slug :first, :last, :as => :name
|
38
37
|
end
|
39
38
|
|
40
|
-
|
41
|
-
------
|
42
|
-
|
43
|
-
In your controller, throw in some minimal magic:
|
39
|
+
In your controller, use available finders:
|
44
40
|
|
45
41
|
# GET /books/a-thousand-plateaus/authors/gilles-deleuze
|
46
42
|
author = Book.find_by_slug(params[:book_id]).
|
47
43
|
authors.
|
48
44
|
find_by_name(params[:id])
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
By default, slugs are not permanent:
|
54
|
-
|
55
|
-
>> book = Book.create(:title => "A Thousand Plateaus")
|
56
|
-
>> book.to_param
|
57
|
-
"a-thousand-plateaus"
|
58
|
-
>> book.title = "Anti Oedipus"
|
59
|
-
>> book.save
|
60
|
-
>> book.to_param
|
61
|
-
"anti-oedipus"
|
62
|
-
|
63
|
-
If you require permanent slugs, pass the `:permanent` option when
|
64
|
-
defining the slug.
|
46
|
+
[Read here](https://github.com/papercavalier/mongoid-slug/blob/master/lib/mongoid/slug.rb)
|
47
|
+
for all available options.
|
65
48
|
|
66
|
-
|
67
|
-
|
49
|
+
Scoping
|
50
|
+
-------
|
68
51
|
|
69
|
-
To scope
|
52
|
+
To scope a slug by a reference association, pass `:scope`:
|
70
53
|
|
71
54
|
class Company
|
72
55
|
include Mongoid::Document
|
@@ -88,18 +71,3 @@ Currently, if you have an irregular association name, you **must**
|
|
88
71
|
specify the `:inverse_of` option on the other side of the assocation.
|
89
72
|
|
90
73
|
Embedded objects are automatically scoped by their parent.
|
91
|
-
|
92
|
-
Indexes
|
93
|
-
-------
|
94
|
-
|
95
|
-
You may optionally pass an `:index` option to define an index on top-level
|
96
|
-
slugs.
|
97
|
-
|
98
|
-
class Book
|
99
|
-
field :title
|
100
|
-
slug :title, :index => true
|
101
|
-
end
|
102
|
-
|
103
|
-
Indexes on unscoped slugs will be unique.
|
104
|
-
|
105
|
-
This option has no effect if the object is embedded.
|
data/lib/mongoid/slug.rb
CHANGED
@@ -2,52 +2,82 @@ require 'stringex'
|
|
2
2
|
|
3
3
|
module Mongoid #:nodoc:
|
4
4
|
|
5
|
-
#
|
6
|
-
# model.
|
5
|
+
# The slug module helps you generate a URL slug or permalink based on one or
|
6
|
+
# more fields in a Mongoid model.
|
7
|
+
#
|
8
|
+
# class Person
|
9
|
+
# include Mongoid::Document
|
10
|
+
# include Mongoid::Slug
|
11
|
+
#
|
12
|
+
# field :name
|
13
|
+
# slug :name
|
14
|
+
# end
|
15
|
+
#
|
7
16
|
module Slug
|
8
17
|
extend ActiveSupport::Concern
|
9
18
|
|
10
19
|
included do
|
11
|
-
cattr_accessor :
|
20
|
+
cattr_accessor :slug_builder, :slugged_fields, :slug_name, :slug_scope
|
12
21
|
end
|
13
22
|
|
14
23
|
module ClassMethods
|
15
24
|
|
16
25
|
# Sets one ore more fields as source of slug.
|
17
26
|
#
|
18
|
-
#
|
19
|
-
#
|
27
|
+
# Takes a list of one or more fields to slug and an optional options
|
28
|
+
# hash.
|
20
29
|
#
|
21
|
-
#
|
30
|
+
# The options hash respects the following members:
|
22
31
|
#
|
23
|
-
#
|
24
|
-
|
32
|
+
# * `:as`, which specifies name of the field that stores the slug.
|
33
|
+
# Defaults to `slug`.
|
34
|
+
#
|
35
|
+
# * `:scope`, which specifies a reference association to scope the slug
|
36
|
+
# by. Embedded documents are by default scoped by their parent.
|
37
|
+
#
|
38
|
+
# * `:permanent`, which specifies whether the slug should be immutable
|
39
|
+
# once created. Defaults to `false`.
|
40
|
+
#
|
41
|
+
# * `:index`, which specifies whether an index should be defined for the
|
42
|
+
# slug. Defaults to `false` and has no effect if the document is em-
|
43
|
+
# bedded.
|
44
|
+
#
|
45
|
+
# Alternatively, this method can be given a block to build a custom slug
|
46
|
+
# out of the specified fields.
|
47
|
+
#
|
48
|
+
# The block takes a single argument, the document itself, and should
|
49
|
+
# return a string that will serve as the base of the slug.
|
50
|
+
#
|
51
|
+
# Here, for instance, we slug an array field.
|
52
|
+
#
|
53
|
+
# class Person
|
54
|
+
# include Mongoid::Document
|
55
|
+
# include Mongoid::Slug
|
56
|
+
#
|
57
|
+
# field :names, :type => Array
|
58
|
+
# slug :names do |doc|
|
59
|
+
# doc.names.join(' ')
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
def slug(*fields, &block)
|
25
63
|
options = fields.extract_options!
|
26
|
-
|
27
|
-
self.slug_name
|
28
|
-
self.slug_scope = options[:scope] || nil
|
29
|
-
|
30
|
-
class_eval <<-CODE
|
31
|
-
def slug_any?
|
32
|
-
#{!!options[:any]}
|
33
|
-
end
|
34
|
-
CODE
|
35
|
-
|
64
|
+
self.slug_scope = options[:scope]
|
65
|
+
self.slug_name = options[:as] || :slug
|
36
66
|
self.slugged_fields = fields
|
37
67
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
68
|
+
self.slug_builder =
|
69
|
+
if block_given?
|
70
|
+
block
|
71
|
+
else
|
72
|
+
lambda do |doc|
|
73
|
+
slugged_fields.map { |f| doc.send(f) }.join(',')
|
74
|
+
end
|
75
|
+
end
|
46
76
|
|
47
77
|
field slug_name
|
48
78
|
|
49
79
|
if options[:index]
|
50
|
-
index
|
80
|
+
index(slug_name, :unique => !slug_scope)
|
51
81
|
end
|
52
82
|
|
53
83
|
if options[:permanent]
|
@@ -56,9 +86,12 @@ module Mongoid #:nodoc:
|
|
56
86
|
before_save :generate_slug
|
57
87
|
end
|
58
88
|
|
89
|
+
# Build a finder based on the slug name.
|
90
|
+
#
|
91
|
+
# Defaults to `find_by_slug`.
|
59
92
|
instance_eval <<-CODE
|
60
93
|
def self.find_by_#{slug_name}(slug)
|
61
|
-
where(slug_name => slug).first
|
94
|
+
where(slug_name => slug).first
|
62
95
|
end
|
63
96
|
CODE
|
64
97
|
end
|
@@ -66,23 +99,21 @@ module Mongoid #:nodoc:
|
|
66
99
|
|
67
100
|
# Regenerates slug.
|
68
101
|
#
|
69
|
-
#
|
70
|
-
# collection.
|
102
|
+
# Should come in handy when generating slugs for an existing collection.
|
71
103
|
def slug!
|
72
|
-
|
73
|
-
save
|
104
|
+
generate_slug!
|
105
|
+
save
|
74
106
|
end
|
75
107
|
|
108
|
+
# Returns the slug.
|
76
109
|
def to_param
|
77
110
|
self.send(slug_name)
|
78
111
|
end
|
79
112
|
|
80
113
|
private
|
81
114
|
|
82
|
-
attr_reader :slug_counter
|
83
|
-
|
84
115
|
def build_slug
|
85
|
-
("#{
|
116
|
+
("#{slug_builder.call(self)} #{@slug_counter}").to_url
|
86
117
|
end
|
87
118
|
|
88
119
|
def find_unique_slug
|
@@ -95,34 +126,22 @@ module Mongoid #:nodoc:
|
|
95
126
|
end
|
96
127
|
end
|
97
128
|
|
98
|
-
def generate_slug!
|
99
|
-
self.send("#{slug_name}=", find_unique_slug)
|
100
|
-
end
|
101
|
-
|
102
129
|
def generate_slug
|
103
|
-
|
130
|
+
if new_record? || slugged_fields_changed?
|
131
|
+
generate_slug!
|
132
|
+
end
|
104
133
|
end
|
105
134
|
|
106
|
-
def
|
107
|
-
|
135
|
+
def generate_slug!
|
136
|
+
self.send("#{slug_name}=", find_unique_slug)
|
108
137
|
end
|
109
138
|
|
110
|
-
def
|
111
|
-
|
112
|
-
self.send(field)
|
113
|
-
end
|
114
|
-
|
115
|
-
if slug_any?
|
116
|
-
values.detect { |value| value.present? }
|
117
|
-
else
|
118
|
-
values.join(' ')
|
119
|
-
end
|
139
|
+
def increment_slug_counter
|
140
|
+
@slug_counter = (@slug_counter.to_i + 1).to_s
|
120
141
|
end
|
121
142
|
|
122
143
|
def slugged_fields_changed?
|
123
|
-
|
124
|
-
self.send("#{field}_changed?")
|
125
|
-
end
|
144
|
+
slugged_fields.any? { |f| self.send("#{f}_changed?") }
|
126
145
|
end
|
127
146
|
|
128
147
|
def unique_slug?(slug)
|
data/lib/mongoid/slug/version.rb
CHANGED
data/spec/models/article.rb
CHANGED
data/spec/models/comic_book.rb
CHANGED
data/spec/mongoid/slug_spec.rb
CHANGED
@@ -160,25 +160,6 @@ module Mongoid
|
|
160
160
|
|
161
161
|
end
|
162
162
|
|
163
|
-
context "when :any is passed as an argument" do
|
164
|
-
let!(:article) do
|
165
|
-
Article.create(
|
166
|
-
:brief => "This is the brief",
|
167
|
-
:title => "This is the title")
|
168
|
-
end
|
169
|
-
|
170
|
-
it "uses the first available field for the slug if any option is used" do
|
171
|
-
article.to_param.should eql 'this-is-the-title'
|
172
|
-
article.title = ""
|
173
|
-
article.save
|
174
|
-
article.to_param.should eql 'this-is-the-brief'
|
175
|
-
|
176
|
-
article.title = nil
|
177
|
-
article.save
|
178
|
-
article.to_param.should eql 'this-is-the-brief'
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
163
|
context "when :as is passed as an argument" do
|
183
164
|
let!(:person) do
|
184
165
|
Person.create(:name => "John Doe")
|
@@ -247,6 +228,34 @@ module Mongoid
|
|
247
228
|
end
|
248
229
|
end
|
249
230
|
|
231
|
+
context "when :slug is given a block" do
|
232
|
+
let(:caption) do
|
233
|
+
Caption.create(:identity => 'Edward Hopper (American, 1882-1967)',
|
234
|
+
:title => 'Soir Bleu, 1914',
|
235
|
+
:medium => 'Oil on Canvas')
|
236
|
+
end
|
237
|
+
|
238
|
+
it "generates a slug" do
|
239
|
+
caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
|
240
|
+
end
|
241
|
+
|
242
|
+
it "updates the slug" do
|
243
|
+
caption.title = 'Road in Maine, 1914'
|
244
|
+
caption.save
|
245
|
+
caption.to_param.should eql "edward-hopper-road-in-maine-1914"
|
246
|
+
end
|
247
|
+
|
248
|
+
it "does not change slug if slugged fields have changed but generated slug is identical" do
|
249
|
+
caption.identity = 'Edward Hopper'
|
250
|
+
caption.save
|
251
|
+
caption.to_param.should eql 'edward-hopper-soir-bleu-1914'
|
252
|
+
end
|
253
|
+
|
254
|
+
it "finds by slug" do
|
255
|
+
Caption.find_by_slug(caption.to_param).should eql caption
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
250
259
|
it "works with non-Latin characters" do
|
251
260
|
book.title = "Капитал"
|
252
261
|
book.save
|
@@ -265,16 +274,6 @@ module Mongoid
|
|
265
274
|
book.to_param.should eql 'paul-cezanne'
|
266
275
|
end
|
267
276
|
|
268
|
-
it "deprecates the :scoped option" do
|
269
|
-
ActiveSupport::Deprecation.should_receive(:warn)
|
270
|
-
class Oldie
|
271
|
-
include Mongoid::Document
|
272
|
-
include Mongoid::Slug
|
273
|
-
field :name
|
274
|
-
slug :name, :scoped => true
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
277
|
context "when :index is passed as an argument" do
|
279
278
|
before do
|
280
279
|
Book.collection.drop_indexes
|
@@ -299,8 +298,6 @@ module Mongoid
|
|
299
298
|
Book.index_information["slug_1"]["unique"].should be_true
|
300
299
|
end
|
301
300
|
end
|
302
|
-
|
303
|
-
it "has no effect in embedded objects"
|
304
301
|
end
|
305
302
|
|
306
303
|
context "when :index is not passed as an argument" do
|
@@ -343,5 +340,12 @@ module Mongoid
|
|
343
340
|
foo.reload.slug.should eql 'john'
|
344
341
|
end
|
345
342
|
end
|
343
|
+
|
344
|
+
describe ".find_by_slug" do
|
345
|
+
it "returns nil if no document is found" do
|
346
|
+
Book.create(:title => "A Thousand Plateaus")
|
347
|
+
Book.find_by_slug(:title => "Anti Oedipus").should be_nil
|
348
|
+
end
|
349
|
+
end
|
346
350
|
end
|
347
351
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: mongoid_slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.7.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paper Cavalier
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-03-31 00:00:00 +01:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
requirements:
|
22
22
|
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: 2.0.0
|
24
|
+
version: 2.0.0
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -35,50 +35,6 @@ dependencies:
|
|
35
35
|
version: 1.2.0
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
|
-
- !ruby/object:Gem::Dependency
|
39
|
-
name: bson_ext
|
40
|
-
prerelease: false
|
41
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
|
-
requirements:
|
44
|
-
- - ~>
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: 1.2.0
|
47
|
-
type: :development
|
48
|
-
version_requirements: *id003
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: database_cleaner
|
51
|
-
prerelease: false
|
52
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
-
none: false
|
54
|
-
requirements:
|
55
|
-
- - ~>
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: 0.6.0
|
58
|
-
type: :development
|
59
|
-
version_requirements: *id004
|
60
|
-
- !ruby/object:Gem::Dependency
|
61
|
-
name: rspec
|
62
|
-
prerelease: false
|
63
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
64
|
-
none: false
|
65
|
-
requirements:
|
66
|
-
- - ~>
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 2.4.0
|
69
|
-
type: :development
|
70
|
-
version_requirements: *id005
|
71
|
-
- !ruby/object:Gem::Dependency
|
72
|
-
name: ruby-debug19
|
73
|
-
prerelease: false
|
74
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
75
|
-
none: false
|
76
|
-
requirements:
|
77
|
-
- - ~>
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version: 0.11.0
|
80
|
-
type: :development
|
81
|
-
version_requirements: *id006
|
82
38
|
description: Mongoid Slug generates a URL slug or permalink based on one or more fields in a Mongoid model.
|
83
39
|
email:
|
84
40
|
- code@papercavalier.com
|
@@ -96,6 +52,7 @@ files:
|
|
96
52
|
- spec/models/article.rb
|
97
53
|
- spec/models/author.rb
|
98
54
|
- spec/models/book.rb
|
55
|
+
- spec/models/caption.rb
|
99
56
|
- spec/models/comic_book.rb
|
100
57
|
- spec/models/partner.rb
|
101
58
|
- spec/models/person.rb
|
@@ -135,6 +92,7 @@ test_files:
|
|
135
92
|
- spec/models/article.rb
|
136
93
|
- spec/models/author.rb
|
137
94
|
- spec/models/book.rb
|
95
|
+
- spec/models/caption.rb
|
138
96
|
- spec/models/comic_book.rb
|
139
97
|
- spec/models/partner.rb
|
140
98
|
- spec/models/person.rb
|