slug 0.5.7 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +127 -0
- data/Rakefile +1 -29
- data/lib/slug/slug.rb +51 -77
- data/lib/slug.rb +4 -4
- data/slug.gemspec +38 -41
- data/test/models.rb +16 -4
- data/test/schema.rb +20 -9
- data/test/slug_test.rb +321 -0
- data/test/test_helper.rb +6 -11
- metadata +91 -57
- data/.gitignore +0 -3
- data/README.rdoc +0 -74
- data/VERSION.yml +0 -4
- data/lib/slug/ascii_approximations.rb +0 -14
- data/test/test_slug.rb +0 -246
data/test/slug_test.rb
ADDED
@@ -0,0 +1,321 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
describe Slug do
|
5
|
+
before do
|
6
|
+
Article.delete_all
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'slug' do
|
10
|
+
it "bases slug on specified source column" do
|
11
|
+
article = Article.create!(:headline => 'Test Headline')
|
12
|
+
assert_equal 'test-headline', article.slug
|
13
|
+
end
|
14
|
+
|
15
|
+
it "bases slug on specified source column, even if it is defined as a method rather than database attribute" do
|
16
|
+
article = Event.create!(:title => 'Test Event', :location => 'Portland')
|
17
|
+
assert_equal 'test-event-portland', article.slug
|
18
|
+
end
|
19
|
+
|
20
|
+
it "bases to_param on slug" do
|
21
|
+
article = Article.create!(:headline => 'Test Headline')
|
22
|
+
assert_equal(article.slug, article.to_param)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "does not impact lookup of model with no slug column" do
|
26
|
+
orphan = Orphan.create!(:name => 'Oliver')
|
27
|
+
query = orphan.to_param
|
28
|
+
assert_equal(orphan.id.to_s, query)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "slug column" do
|
32
|
+
it "saves slug to 'slug' column by default" do
|
33
|
+
article = Article.create!(:headline => 'Test Headline')
|
34
|
+
assert_equal 'test-headline', article.slug
|
35
|
+
end
|
36
|
+
|
37
|
+
it "saves slug to :column specified in options" do
|
38
|
+
Person.delete_all
|
39
|
+
person = Person.create!(:name => 'Test Person')
|
40
|
+
assert_equal 'test-person', person.web_slug
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "column validations" do
|
46
|
+
it "raises ArgumentError if an invalid source column is passed" do
|
47
|
+
Company.slug(:invalid_source_column)
|
48
|
+
assert_raises(ArgumentError) { Company.create! }
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises an ArgumentError if an invalid slug column is passed" do
|
52
|
+
Company.slug(:name, :column => :bad_slug_column)
|
53
|
+
assert_raises(ArgumentError) { Company.create! }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'generates a generic slug' do
|
58
|
+
before do
|
59
|
+
Generation.delete_all
|
60
|
+
end
|
61
|
+
|
62
|
+
it "if source column is empty" do
|
63
|
+
generation = Generation.create!
|
64
|
+
assert_equal 'generation', generation.slug
|
65
|
+
end
|
66
|
+
|
67
|
+
it "if normalization makes source value empty" do
|
68
|
+
generation = Generation.create!(:title => '$$$')
|
69
|
+
assert_equal 'generation', generation.slug
|
70
|
+
end
|
71
|
+
|
72
|
+
it "if source value contains no Latin characters" do
|
73
|
+
generation = Generation.create!(:title => 'ローマ字がない')
|
74
|
+
assert_equal 'generation', generation.slug
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'validation' do
|
79
|
+
it "sets validation error if source column is empty" do
|
80
|
+
article = Article.create
|
81
|
+
assert !article.valid?
|
82
|
+
assert article.errors[:slug]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "sets validation error if normalization makes source value empty" do
|
86
|
+
article = Article.create(:headline => '$$$')
|
87
|
+
assert !article.valid?
|
88
|
+
assert article.errors[:slug]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "validates slug format on save" do
|
92
|
+
article = Article.create!(:headline => 'Test Headline')
|
93
|
+
article.slug = 'A BAD $LUG.'
|
94
|
+
|
95
|
+
assert !article.valid?
|
96
|
+
assert article.errors[:slug].present?
|
97
|
+
end
|
98
|
+
|
99
|
+
it "validates uniqueness of slug by default" do
|
100
|
+
Article.create!(:headline => 'Test Headline')
|
101
|
+
article2 = Article.create!(:headline => 'Test Headline')
|
102
|
+
article2.slug = 'test-headline'
|
103
|
+
|
104
|
+
assert !article2.valid?
|
105
|
+
assert article2.errors[:slug].present?
|
106
|
+
end
|
107
|
+
|
108
|
+
it "uses validate_uniqueness_if proc to decide whether uniqueness validation applies" do
|
109
|
+
Post.create!(:headline => 'Test Headline')
|
110
|
+
article2 = Post.new
|
111
|
+
article2.slug = 'test-headline'
|
112
|
+
|
113
|
+
assert article2.valid?
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "doesn't overwrite slug value on create if it was already specified" do
|
118
|
+
a = Article.create!(:headline => 'Test Headline', :slug => 'slug1')
|
119
|
+
assert_equal 'slug1', a.slug
|
120
|
+
end
|
121
|
+
|
122
|
+
it "doesn't update the slug even if the source column changes" do
|
123
|
+
article = Article.create!(:headline => 'Test Headline')
|
124
|
+
article.update_attributes!(:headline => 'New Headline')
|
125
|
+
assert_equal 'test-headline', article.slug
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "resetting a slug" do
|
129
|
+
before do
|
130
|
+
@article = Article.create(:headline => 'test headline')
|
131
|
+
@original_slug = @article.slug
|
132
|
+
end
|
133
|
+
|
134
|
+
it "maintains the same slug if slug column hasn't changed" do
|
135
|
+
@article.reset_slug
|
136
|
+
assert_equal @original_slug, @article.slug
|
137
|
+
end
|
138
|
+
|
139
|
+
it "changes slug if slug column has updated" do
|
140
|
+
@article.headline = "donkey"
|
141
|
+
@article.reset_slug
|
142
|
+
refute_equal(@original_slug, @article.slug)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "maintains sequence" do
|
146
|
+
@existing_article = Article.create!(:headline => 'world cup')
|
147
|
+
@article.headline = "world cup"
|
148
|
+
@article.reset_slug
|
149
|
+
assert_equal 'world-cup-1', @article.slug
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "slug normalization" do
|
154
|
+
before do
|
155
|
+
@article = Article.new
|
156
|
+
end
|
157
|
+
|
158
|
+
it "lowercases strings" do
|
159
|
+
@article.headline = 'AbC'
|
160
|
+
@article.save!
|
161
|
+
assert_equal "abc", @article.slug
|
162
|
+
end
|
163
|
+
|
164
|
+
it "replaces whitespace with dashes" do
|
165
|
+
@article.headline = 'a b'
|
166
|
+
@article.save!
|
167
|
+
assert_equal 'a-b', @article.slug
|
168
|
+
end
|
169
|
+
|
170
|
+
it "replaces 2spaces with 1dash" do
|
171
|
+
@article.headline = 'a b'
|
172
|
+
@article.save!
|
173
|
+
assert_equal 'a-b', @article.slug
|
174
|
+
end
|
175
|
+
|
176
|
+
it "removes punctuation" do
|
177
|
+
@article.headline = 'abc!@#$%^&*•¶§∞¢££¡¿()><?""\':;][]\.,/'
|
178
|
+
@article.save!
|
179
|
+
assert_match 'abc', @article.slug
|
180
|
+
end
|
181
|
+
|
182
|
+
it "strips trailing space" do
|
183
|
+
@article.headline = 'ab '
|
184
|
+
@article.save!
|
185
|
+
assert_equal 'ab', @article.slug
|
186
|
+
end
|
187
|
+
|
188
|
+
it "strips leading space" do
|
189
|
+
@article.headline = ' ab'
|
190
|
+
@article.save!
|
191
|
+
assert_equal 'ab', @article.slug
|
192
|
+
end
|
193
|
+
|
194
|
+
it "strips trailing dashes" do
|
195
|
+
@article.headline = 'ab-'
|
196
|
+
@article.save!
|
197
|
+
assert_match 'ab', @article.slug
|
198
|
+
end
|
199
|
+
|
200
|
+
it "strips leading dashes" do
|
201
|
+
@article.headline = '-ab'
|
202
|
+
@article.save!
|
203
|
+
assert_match 'ab', @article.slug
|
204
|
+
end
|
205
|
+
|
206
|
+
it "remove double-dashes" do
|
207
|
+
@article.headline = 'a--b--c'
|
208
|
+
@article.save!
|
209
|
+
assert_match 'a-b-c', @article.slug
|
210
|
+
end
|
211
|
+
|
212
|
+
it "doesn't modify valid slug strings" do
|
213
|
+
@article.headline = 'a-b-c-d'
|
214
|
+
@article.save!
|
215
|
+
assert_match 'a-b-c-d', @article.slug
|
216
|
+
end
|
217
|
+
|
218
|
+
it "doesn't insert dashes for periods in acronyms, regardless of where they appear in string" do
|
219
|
+
@article.headline = "N.Y.P.D. vs. N.S.A. vs. F.B.I."
|
220
|
+
@article.save!
|
221
|
+
assert_match 'nypd-vs-nsa-vs-fbi', @article.slug
|
222
|
+
end
|
223
|
+
|
224
|
+
it "doesn't insert dashes for apostrophes" do
|
225
|
+
@article.headline = "Thomas Jefferson's Papers"
|
226
|
+
@article.save!
|
227
|
+
assert_match 'thomas-jeffersons-papers', @article.slug
|
228
|
+
end
|
229
|
+
|
230
|
+
it "preserves numbers in slug" do
|
231
|
+
@article.headline = "2010 Election"
|
232
|
+
@article.save!
|
233
|
+
assert_match '2010-election', @article.slug
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "diacritics handling" do
|
238
|
+
before do
|
239
|
+
@article = Article.new
|
240
|
+
end
|
241
|
+
|
242
|
+
it "strips diacritics" do
|
243
|
+
@article.headline = "açaí"
|
244
|
+
@article.save!
|
245
|
+
assert_equal "acai", @article.slug
|
246
|
+
end
|
247
|
+
|
248
|
+
it "strips diacritics correctly " do
|
249
|
+
@article.headline = "ÀÁÂÃÄÅÆÇÈÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
|
250
|
+
@article.save!
|
251
|
+
expected = "aaaaaaaeceeeiiiidnoooooouuuuythssaaaaaaaeceeeeiiiidnoooooouuuuythy".split(//)
|
252
|
+
output = @article.slug.split(//)
|
253
|
+
output.each_index do |i|
|
254
|
+
assert_equal expected[i], output[i]
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe "sequence handling" do
|
260
|
+
it "doesn't add a sequence if saving first instance of slug" do
|
261
|
+
article = Article.create!(:headline => 'Test Headline')
|
262
|
+
assert_equal 'test-headline', article.slug
|
263
|
+
end
|
264
|
+
|
265
|
+
it "assigns a -1 suffix to the second instance of the slug" do
|
266
|
+
Article.create!(:headline => 'Test Headline')
|
267
|
+
article_2 = Article.create!(:headline => 'Test Headline')
|
268
|
+
assert_equal 'test-headline-1', article_2.slug
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'assigns a -2 suffix to the third instance of the slug containing numbers' do
|
272
|
+
2.times { |i| Article.create! :headline => '11111' }
|
273
|
+
article_3 = Article.create! :headline => '11111'
|
274
|
+
assert_equal '11111-2', article_3.slug
|
275
|
+
end
|
276
|
+
|
277
|
+
it "assigns a -12 suffix to the thirteenth instance of the slug" do
|
278
|
+
12.times { |i| Article.create!(:headline => 'Test Headline') }
|
279
|
+
article_13 = Article.create!(:headline => 'Test Headline')
|
280
|
+
assert_equal 'test-headline-12', article_13.slug
|
281
|
+
|
282
|
+
12.times { |i| Article.create!(:headline => 'latest from lybia') }
|
283
|
+
article_13 = Article.create!(:headline => 'latest from lybia')
|
284
|
+
assert_equal 'latest-from-lybia-12', article_13.slug
|
285
|
+
end
|
286
|
+
|
287
|
+
it "ignores partial matches when calculating sequence" do
|
288
|
+
article_1 = Article.create!(:headline => 'Test Headline')
|
289
|
+
assert_equal 'test-headline', article_1.slug
|
290
|
+
article_2 = Article.create!(:headline => 'Test')
|
291
|
+
assert_equal 'test', article_2.slug
|
292
|
+
article_3 = Article.create!(:headline => 'Test')
|
293
|
+
assert_equal 'test-1', article_3.slug
|
294
|
+
article_4 = Article.create!(:headline => 'Test')
|
295
|
+
assert_equal 'test-2', article_4.slug
|
296
|
+
end
|
297
|
+
|
298
|
+
it "knows about single table inheritance" do
|
299
|
+
article = Article.create!(:headline => 'Test Headline')
|
300
|
+
story = Storyline.create!(:headline => article.headline)
|
301
|
+
assert_equal 'test-headline-1', story.slug
|
302
|
+
end
|
303
|
+
|
304
|
+
it "correctly slugs when a slug is a substring of another" do
|
305
|
+
rap_metal = Article.create!(:headline => 'Rap Metal')
|
306
|
+
assert_equal 'rap-metal', rap_metal.slug
|
307
|
+
|
308
|
+
rap = Article.create!(:headline => 'Rap')
|
309
|
+
assert_equal('rap', rap.slug)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "applies sequence logic correctly when the slug is a substring of another" do
|
313
|
+
rap_metal = Article.create!(:headline => 'Rap Metal')
|
314
|
+
assert_equal 'rap-metal', rap_metal.slug
|
315
|
+
|
316
|
+
Article.create!(:headline => 'Rap')
|
317
|
+
second_rap = Article.create!(:headline => 'Rap')
|
318
|
+
assert_equal('rap-1', second_rap.slug)
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,26 +1,21 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require 'mocha'
|
5
|
-
|
6
|
-
class Test::Unit::TestCase
|
7
|
-
end
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/reporters'
|
8
4
|
|
9
5
|
# You can use "rake test AR_VERSION=2.0.5" to test against 2.0.5, for example.
|
10
6
|
# The default is to use the latest installed ActiveRecord.
|
11
7
|
if ENV["AR_VERSION"]
|
12
8
|
gem 'activerecord', "#{ENV["AR_VERSION"]}"
|
13
|
-
gem 'activesupport', "#{ENV["AR_VERSION"]}"
|
14
9
|
end
|
15
10
|
require 'active_record'
|
16
|
-
|
11
|
+
|
12
|
+
# color test output
|
13
|
+
Minitest::Reporters.use! [Minitest::Reporters::DefaultReporter.new(:color => true)]
|
17
14
|
|
18
15
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
19
16
|
require 'slug'
|
20
17
|
|
21
18
|
ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
|
22
|
-
|
23
|
-
load(File.dirname(__FILE__) + "/schema.rb")
|
24
|
-
end
|
19
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
25
20
|
|
26
21
|
require 'models'
|
metadata
CHANGED
@@ -1,90 +1,124 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: slug
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 4.1.1
|
5
5
|
platform: ruby
|
6
|
-
authors:
|
6
|
+
authors:
|
7
7
|
- Ben Koski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
date: 2018-11-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
17
20
|
type: :runtime
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
20
|
-
requirements:
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
21
52
|
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version:
|
24
|
-
|
25
|
-
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activerecord
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.0.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 3.0.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
26
70
|
name: activesupport
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.0.0
|
27
76
|
type: :runtime
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
version:
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.0.0
|
35
83
|
description: Simple, straightforward slugs for your ActiveRecord models.
|
36
84
|
email: ben.koski@gmail.com
|
37
85
|
executables: []
|
38
|
-
|
39
86
|
extensions: []
|
40
|
-
|
41
|
-
extra_rdoc_files:
|
87
|
+
extra_rdoc_files:
|
42
88
|
- LICENSE
|
43
|
-
- README.
|
44
|
-
files:
|
45
|
-
- .gitignore
|
89
|
+
- README.md
|
90
|
+
files:
|
46
91
|
- LICENSE
|
47
|
-
- README.
|
92
|
+
- README.md
|
48
93
|
- Rakefile
|
49
|
-
- VERSION.yml
|
50
94
|
- lib/slug.rb
|
51
|
-
- lib/slug/ascii_approximations.rb
|
52
95
|
- lib/slug/slug.rb
|
53
96
|
- slug.gemspec
|
54
97
|
- test/models.rb
|
55
98
|
- test/schema.rb
|
99
|
+
- test/slug_test.rb
|
56
100
|
- test/test_helper.rb
|
57
|
-
- test/test_slug.rb
|
58
|
-
has_rdoc: true
|
59
101
|
homepage: http://github.com/bkoski/slug
|
60
102
|
licenses: []
|
61
|
-
|
103
|
+
metadata: {}
|
62
104
|
post_install_message:
|
63
|
-
rdoc_options:
|
64
|
-
|
65
|
-
require_paths:
|
105
|
+
rdoc_options: []
|
106
|
+
require_paths:
|
66
107
|
- lib
|
67
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
requirements:
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
69
110
|
- - ">="
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
version:
|
72
|
-
|
73
|
-
|
74
|
-
requirements:
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
75
115
|
- - ">="
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version:
|
78
|
-
version:
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
79
118
|
requirements: []
|
80
|
-
|
81
119
|
rubyforge_project:
|
82
|
-
rubygems_version:
|
120
|
+
rubygems_version: 2.7.6
|
83
121
|
signing_key:
|
84
|
-
specification_version:
|
122
|
+
specification_version: 4
|
85
123
|
summary: Simple, straightforward slugs for your ActiveRecord models.
|
86
|
-
test_files:
|
87
|
-
- test/models.rb
|
88
|
-
- test/schema.rb
|
89
|
-
- test/test_helper.rb
|
90
|
-
- test/test_slug.rb
|
124
|
+
test_files: []
|
data/.gitignore
DELETED
data/README.rdoc
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
= slug
|
2
|
-
|
3
|
-
Slug provides simple, straightforward slugging for your ActiveRecord models.
|
4
|
-
|
5
|
-
Slug is based on code from Norman Clarke's fantastic {friendly_id}[http://friendly-id.rubyforge.org] project and Nick Zadrozny's {friendly_identifier}[http://code.google.com/p/friendly-identifier/].
|
6
|
-
|
7
|
-
Here's what's different:
|
8
|
-
|
9
|
-
* Unlike friendly_id, slugs are stored directly in your model's table. friendly_id stores its data in a separate sluggable table, which enables cool things like slug versioning--but forces yet another join when trying to do complex find_by_slugs.
|
10
|
-
* Like friendly_id, diacritics (accented characters) are stripped from slug strings.
|
11
|
-
* Most importantly, there are just two options: source column (for example, headline) and an optional slug column (if for some reason you don't want your slugs stored in a column called slug)
|
12
|
-
|
13
|
-
== INSTALLATION
|
14
|
-
|
15
|
-
sudo gem install slug --source http://gemcutter.org
|
16
|
-
|
17
|
-
then add
|
18
|
-
|
19
|
-
config.gem 'slug', :source => 'http://gemcutter.org'
|
20
|
-
|
21
|
-
to your rails project.
|
22
|
-
|
23
|
-
== USAGE
|
24
|
-
|
25
|
-
It's up to you to set up the appropriate column in your model. By default, Slug saves the slug to a column called 'slug', so in most cases you'll just want to add
|
26
|
-
|
27
|
-
t.string :slug
|
28
|
-
|
29
|
-
to your create migration.
|
30
|
-
|
31
|
-
I'd also suggest adding a unique index on the slug field in your migration
|
32
|
-
|
33
|
-
add_index :model_name, :slug, :unique => true
|
34
|
-
|
35
|
-
Though Slug uses validates_uniqueness_of to ensue the uniqueness of your slug, two concurrent INSERTs could conceivably try to set the same slug. Unlikely, but it's worth adding a unique index as a backstop.
|
36
|
-
|
37
|
-
Once your table is migrated, just add
|
38
|
-
|
39
|
-
slug :source_field
|
40
|
-
|
41
|
-
to your ActiveRecord model. <tt>:source_field</tt> is the column you'd like to base the slug on--for example, it might be <tt>:headline</tt>.
|
42
|
-
|
43
|
-
If you want to save the slug in a database column that isn't called <tt>slug</tt>, just pass the <tt>:column</tt> option. For example
|
44
|
-
|
45
|
-
slug :headline, :column => :web_slug
|
46
|
-
|
47
|
-
would generate a slug based on <tt>headline</tt> and save it to <tt>web_slug</tt>.
|
48
|
-
|
49
|
-
The source column isn't limited to just database attributes--you can specify any instance method. This is handy if you need special formatting on your source column before it's slugged, or if you want to base the slug on several attributes.
|
50
|
-
|
51
|
-
For example:
|
52
|
-
|
53
|
-
class Article < ActiveRecord::Base
|
54
|
-
slug :title_for_slug
|
55
|
-
|
56
|
-
def title_for_slug
|
57
|
-
"#{headline}-#{publication_date.year}-#{publication_date.month}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
would use headline-pub year-pub month as the slug source.
|
63
|
-
|
64
|
-
From here, you can work with your slug as if it were a normal column--<tt>find_by_slug</tt> and named scopes will work as they do for any other column.
|
65
|
-
|
66
|
-
A few notes:
|
67
|
-
* Slug validates presence and uniqueness of the slug column. If you pass something that isn't sluggable as the source (for example, say you set the headline to '---'), a validation error will be set.
|
68
|
-
* Slug doesn't update the slug if the source column changes. If you really need to regenerate the slug, call <tt>@model.set_slug</tt> before save.
|
69
|
-
* If a slug already exists, Slug will automatically append a '-n' suffix to your slug to make it unique. The second instance of a slug is '-1'.
|
70
|
-
* If you don't like the slug formatting or the accented character stripping doesn't work for you, it's easy to override Slug's formatting functions. Check the source for details.
|
71
|
-
|
72
|
-
== AUTHOR
|
73
|
-
|
74
|
-
Ben Koski, ben.koski@gmail.com
|
data/VERSION.yml
DELETED