slug 0.5.7 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
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 'test/unit'
3
- require 'shoulda'
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
- require 'active_support'
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
- silence_stream(STDOUT) do
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: 0.5.7
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
- date: 2010-06-25 00:00:00 -04:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: activerecord
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
- version_requirement:
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: "0"
24
- version:
25
- - !ruby/object:Gem::Dependency
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
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: "0"
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.rdoc
44
- files:
45
- - .gitignore
89
+ - README.md
90
+ files:
46
91
  - LICENSE
47
- - README.rdoc
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
- - --charset=UTF-8
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: "0"
72
- version:
73
- required_rubygems_version: !ruby/object:Gem::Requirement
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: "0"
78
- version:
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
79
118
  requirements: []
80
-
81
119
  rubyforge_project:
82
- rubygems_version: 1.3.5
120
+ rubygems_version: 2.7.6
83
121
  signing_key:
84
- specification_version: 3
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
@@ -1,3 +0,0 @@
1
- *.sw?
2
- .DS_Store
3
- coverage
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
@@ -1,4 +0,0 @@
1
- ---
2
- :patch: 7
3
- :major: 0
4
- :minor: 5
@@ -1,14 +0,0 @@
1
- module Slug
2
- # ASCII approximations for accented characters.
3
- ASCII_APPROXIMATIONS = {
4
- 198 => "AE",
5
- 208 => "D",
6
- 216 => "O",
7
- 222 => "Th",
8
- 223 => "ss",
9
- 230 => "ae",
10
- 240 => "d",
11
- 248 => "o",
12
- 254 => "th"
13
- }.freeze
14
- end