friendly_id 5.2.4 → 5.5.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.
Files changed (68) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.github/FUNDING.yml +1 -0
  4. data/.github/dependabot.yml +6 -0
  5. data/.github/stale.yml +17 -0
  6. data/.github/workflows/test.yml +58 -0
  7. data/Changelog.md +41 -0
  8. data/Gemfile +10 -11
  9. data/README.md +42 -15
  10. data/Rakefile +24 -27
  11. data/bench.rb +30 -27
  12. data/certs/parndt.pem +27 -0
  13. data/friendly_id.gemspec +28 -27
  14. data/gemfiles/Gemfile.rails-5.2.rb +11 -16
  15. data/gemfiles/Gemfile.rails-6.0.rb +22 -0
  16. data/gemfiles/Gemfile.rails-6.1.rb +22 -0
  17. data/gemfiles/Gemfile.rails-7.0.rb +22 -0
  18. data/guide.rb +5 -5
  19. data/lib/friendly_id/base.rb +61 -68
  20. data/lib/friendly_id/candidates.rb +9 -11
  21. data/lib/friendly_id/configuration.rb +8 -8
  22. data/lib/friendly_id/finder_methods.rb +72 -13
  23. data/lib/friendly_id/finders.rb +64 -67
  24. data/lib/friendly_id/history.rb +72 -66
  25. data/lib/friendly_id/initializer.rb +5 -5
  26. data/lib/friendly_id/migration.rb +10 -11
  27. data/lib/friendly_id/object_utils.rb +2 -2
  28. data/lib/friendly_id/reserved.rb +28 -32
  29. data/lib/friendly_id/scoped.rb +105 -103
  30. data/lib/friendly_id/sequentially_slugged/calculator.rb +69 -0
  31. data/lib/friendly_id/sequentially_slugged.rb +21 -58
  32. data/lib/friendly_id/simple_i18n.rb +75 -69
  33. data/lib/friendly_id/slug.rb +1 -2
  34. data/lib/friendly_id/slug_generator.rb +1 -3
  35. data/lib/friendly_id/slugged.rb +236 -239
  36. data/lib/friendly_id/version.rb +1 -1
  37. data/lib/friendly_id.rb +41 -45
  38. data/lib/generators/friendly_id_generator.rb +9 -9
  39. data/test/base_test.rb +10 -13
  40. data/test/benchmarks/finders.rb +28 -26
  41. data/test/benchmarks/object_utils.rb +13 -13
  42. data/test/candidates_test.rb +17 -18
  43. data/test/configuration_test.rb +7 -11
  44. data/test/core_test.rb +1 -2
  45. data/test/databases.yml +7 -4
  46. data/test/finders_test.rb +52 -5
  47. data/test/generator_test.rb +16 -26
  48. data/test/helper.rb +33 -20
  49. data/test/history_test.rb +116 -72
  50. data/test/numeric_slug_test.rb +31 -0
  51. data/test/object_utils_test.rb +0 -2
  52. data/test/reserved_test.rb +9 -11
  53. data/test/schema.rb +5 -4
  54. data/test/scoped_test.rb +26 -15
  55. data/test/sequentially_slugged_test.rb +107 -33
  56. data/test/shared.rb +17 -18
  57. data/test/simple_i18n_test.rb +23 -13
  58. data/test/slugged_test.rb +254 -78
  59. data/test/sti_test.rb +19 -21
  60. data.tar.gz.sig +0 -0
  61. metadata +49 -19
  62. metadata.gz.sig +1 -0
  63. data/.travis.yml +0 -57
  64. data/gemfiles/Gemfile.rails-4.0.rb +0 -30
  65. data/gemfiles/Gemfile.rails-4.1.rb +0 -29
  66. data/gemfiles/Gemfile.rails-4.2.rb +0 -28
  67. data/gemfiles/Gemfile.rails-5.0.rb +0 -28
  68. data/gemfiles/Gemfile.rails-5.1.rb +0 -27
data/test/helper.rb CHANGED
@@ -1,21 +1,21 @@
1
1
  require "bundler/setup"
2
2
 
3
- if ENV['COVERALLS'] || ENV['COVERAGE']
4
- require 'simplecov'
5
- if ENV['COVERALLS']
6
- require 'coveralls'
3
+ if ENV["COVERALLS"] || ENV["COVERAGE"]
4
+ require "simplecov"
5
+ if ENV["COVERALLS"]
6
+ require "coveralls"
7
7
  SimpleCov.formatter = Coveralls::SimpleCov::Formatter
8
8
  end
9
9
  SimpleCov.start do
10
- add_filter 'test'
11
- add_filter 'friendly_id/migration'
10
+ add_filter "test"
11
+ add_filter "friendly_id/migration"
12
12
  end
13
13
  end
14
14
 
15
15
  begin
16
- require 'minitest'
16
+ require "minitest"
17
17
  rescue LoadError
18
- require 'minitest/unit'
18
+ require "minitest/unit"
19
19
  end
20
20
 
21
21
  begin
@@ -24,9 +24,10 @@ rescue NameError
24
24
  TestCaseClass = MiniTest::Unit::TestCase
25
25
  end
26
26
 
27
- require "mocha/setup"
27
+ require "mocha/minitest"
28
28
  require "active_record"
29
- require 'active_support/core_ext/time/conversions'
29
+ require "active_support/core_ext/time/conversions"
30
+ require "erb"
30
31
 
31
32
  I18n.enforce_available_locales = false
32
33
 
@@ -38,29 +39,32 @@ if ENV["LOG"]
38
39
  ActiveRecord::Base.logger = Logger.new($stdout)
39
40
  end
40
41
 
41
- if ActiveSupport::VERSION::STRING >= '4.2'
42
+ if ActiveSupport::VERSION::STRING >= "4.2"
42
43
  ActiveSupport.test_order = :random
43
44
  end
44
45
 
45
46
  module FriendlyId
46
47
  module Test
47
-
48
48
  def self.included(base)
49
49
  if Minitest.respond_to?(:autorun)
50
50
  Minitest.autorun
51
51
  else
52
- require 'minitest/autorun'
52
+ require "minitest/autorun"
53
53
  end
54
54
  rescue LoadError
55
55
  end
56
56
 
57
57
  def transaction
58
- ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
58
+ ActiveRecord::Base.transaction do
59
+ yield
60
+
61
+ raise ActiveRecord::Rollback
62
+ end
59
63
  end
60
64
 
61
65
  def with_instance_of(*args)
62
66
  model_class = args.shift
63
- args[0] ||= {:name => "a b c"}
67
+ args[0] ||= {name: "a b c"}
64
68
  transaction { yield model_class.create!(*args) }
65
69
  end
66
70
 
@@ -69,8 +73,11 @@ module FriendlyId
69
73
 
70
74
  def connect
71
75
  version = ActiveRecord::VERSION::STRING
72
- driver = FriendlyId::Test::Database.driver
73
- engine = RUBY_ENGINE rescue "ruby"
76
+ engine = begin
77
+ RUBY_ENGINE
78
+ rescue
79
+ "ruby"
80
+ end
74
81
 
75
82
  ActiveRecord::Base.establish_connection config[driver]
76
83
  message = "Using #{engine} #{RUBY_VERSION} AR #{version} with #{driver}"
@@ -86,11 +93,17 @@ module FriendlyId
86
93
  end
87
94
 
88
95
  def config
89
- @config ||= YAML::load(File.open(File.expand_path("../databases.yml", __FILE__)))
96
+ @config ||= YAML.safe_load(
97
+ ERB.new(
98
+ File.read(File.expand_path("../databases.yml", __FILE__))
99
+ ).result
100
+ )
90
101
  end
91
102
 
92
103
  def driver
93
- (ENV["DB"] or "sqlite3").downcase
104
+ db_driver = ENV.fetch("DB", "sqlite3").downcase
105
+ db_driver = "postgres" if %w[postgresql pg].include?(db_driver)
106
+ db_driver
94
107
  end
95
108
 
96
109
  def in_memory?
@@ -109,4 +122,4 @@ end
109
122
  require "schema"
110
123
  require "shared"
111
124
  FriendlyId::Test::Database.connect
112
- at_exit {ActiveRecord::Base.connection.disconnect!}
125
+ at_exit { ActiveRecord::Base.connection.disconnect! }
data/test/history_test.rb CHANGED
@@ -1,13 +1,12 @@
1
1
  require "helper"
2
2
 
3
3
  class HistoryTest < TestCaseClass
4
-
5
4
  include FriendlyId::Test
6
5
  include FriendlyId::Test::Shared::Core
7
6
 
8
7
  class Manual < ActiveRecord::Base
9
8
  extend FriendlyId
10
- friendly_id :name, :use => [:slugged, :history]
9
+ friendly_id :name, use: [:slugged, :history]
11
10
  end
12
11
 
13
12
  def model_class
@@ -15,7 +14,7 @@ class HistoryTest < TestCaseClass
15
14
  end
16
15
 
17
16
  test "should insert record in slugs table on create" do
18
- with_instance_of(model_class) {|record| assert record.slugs.any?}
17
+ with_instance_of(model_class) { |record| assert record.slugs.any? }
19
18
  end
20
19
 
21
20
  test "should not create new slug record if friendly_id is not changed" do
@@ -52,8 +51,9 @@ class HistoryTest < TestCaseClass
52
51
 
53
52
  test "should create slug records on each change" do
54
53
  transaction do
55
- record = model_class.create! :name => "hello"
54
+ model_class.create! name: "hello"
56
55
  assert_equal 1, FriendlyId::Slug.count
56
+
57
57
  record = model_class.friendly.find("hello")
58
58
  record.name = "hello again"
59
59
  record.slug = nil
@@ -65,8 +65,7 @@ class HistoryTest < TestCaseClass
65
65
  test "should not be read only when found by slug" do
66
66
  with_instance_of(model_class) do |record|
67
67
  refute model_class.friendly.find(record.friendly_id).readonly?
68
- assert record.update_attribute :name, 'foo'
69
- assert record.update_attributes name: 'foo'
68
+ assert record.update name: "foo"
70
69
  end
71
70
  end
72
71
 
@@ -81,38 +80,60 @@ class HistoryTest < TestCaseClass
81
80
 
82
81
  test "should handle renames" do
83
82
  with_instance_of(model_class) do |record|
84
- record.name = 'x'
83
+ record.name = "x"
85
84
  record.slug = nil
86
85
  assert record.save
87
- record.name = 'y'
86
+ record.name = "y"
88
87
  record.slug = nil
89
88
  assert record.save
90
- record.name = 'x'
89
+ record.name = "x"
90
+ record.slug = nil
91
+ assert record.save
92
+ end
93
+ end
94
+
95
+ test "should maintain history even if current slug is not the most recent one" do
96
+ with_instance_of(model_class) do |record|
97
+ record.name = "current"
98
+ assert record.save
99
+
100
+ # this feels like a hack. only thing i can get to work with the HistoryTestWithSti
101
+ # test cases. (Editorialist vs Journalist.)
102
+ sluggable_type = FriendlyId::Slug.first.sluggable_type
103
+ # create several slugs for record
104
+ # current slug does not have max id
105
+ FriendlyId::Slug.delete_all
106
+ FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: "current")
107
+ FriendlyId::Slug.create(sluggable_type: sluggable_type, sluggable_id: record.id, slug: "outdated")
108
+
109
+ record.reload
91
110
  record.slug = nil
92
111
  assert record.save
112
+
113
+ assert_equal 2, FriendlyId::Slug.count
93
114
  end
94
115
  end
95
116
 
96
117
  test "should not create new slugs that match old slugs" do
97
118
  transaction do
98
- first_record = model_class.create! :name => "foo"
119
+ first_record = model_class.create! name: "foo"
99
120
  first_record.name = "bar"
100
121
  first_record.save!
101
- second_record = model_class.create! :name => "foo"
122
+ second_record = model_class.create! name: "foo"
102
123
  assert second_record.slug != "foo"
103
124
  assert_match(/foo-.+/, second_record.slug)
104
125
  end
105
126
  end
106
127
 
107
- test 'should not fail when updating historic slugs' do
128
+ test "should not fail when updating historic slugs" do
108
129
  transaction do
109
- first_record = model_class.create! :name => "foo"
110
- second_record = model_class.create! :name => 'another'
130
+ first_record = model_class.create! name: "foo"
131
+ second_record = model_class.create! name: "another"
111
132
 
112
- second_record.update_attributes :name => 'foo', :slug => nil
133
+ second_record.update name: "foo", slug: nil
113
134
  assert_match(/foo-.*/, second_record.slug)
114
135
 
115
- first_record.update_attributes :name => 'another', :slug => nil
136
+ first_record.update name: "another", slug: nil
116
137
  assert_match(/another-.*/, first_record.slug)
117
138
  end
118
139
  end
@@ -123,25 +144,23 @@ class HistoryTest < TestCaseClass
123
144
  second_record = model_class.create! name: "bar"
124
145
 
125
146
  first_record.update! slug: "not_foo"
126
- second_record.update! slug: "foo" #now both records have used foo; second_record most recently
147
+ second_record.update! slug: "foo" # now both records have used foo; second_record most recently
127
148
  second_record.update! slug: "not_bar"
128
149
 
129
150
  assert_equal model_class.friendly.find("foo"), second_record
130
151
  end
131
152
  end
132
153
 
133
- test 'should name table according to prefix and suffix' do
154
+ test "should name table according to prefix and suffix" do
134
155
  transaction do
135
- begin
136
- prefix = "prefix_"
137
- without_prefix = FriendlyId::Slug.table_name
138
- ActiveRecord::Base.table_name_prefix = prefix
139
- FriendlyId::Slug.reset_table_name
140
- assert_equal prefix + without_prefix, FriendlyId::Slug.table_name
141
- ensure
142
- ActiveRecord::Base.table_name_prefix = ""
143
- FriendlyId::Slug.table_name = without_prefix
144
- end
156
+ prefix = "prefix_"
157
+ without_prefix = FriendlyId::Slug.table_name
158
+ ActiveRecord::Base.table_name_prefix = prefix
159
+ FriendlyId::Slug.reset_table_name
160
+ assert_equal prefix + without_prefix, FriendlyId::Slug.table_name
161
+ ensure
162
+ ActiveRecord::Base.table_name_prefix = ""
163
+ FriendlyId::Slug.table_name = without_prefix
145
164
  end
146
165
  end
147
166
  end
@@ -149,7 +168,7 @@ end
149
168
  class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
150
169
  class Manual < ActiveRecord::Base
151
170
  extend FriendlyId
152
- friendly_id :name, :use => [:slugged, :history]
171
+ friendly_id :name, use: [:slugged, :history]
153
172
 
154
173
  def should_generate_new_friendly_id?
155
174
  slug.blank? or name_changed?
@@ -160,62 +179,61 @@ class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
160
179
  Manual
161
180
  end
162
181
 
163
- test 'should allow reversion back to a previously used slug' do
164
- with_instance_of(model_class, name: 'foo') do |record|
165
- record.name = 'bar'
182
+ test "should allow reversion back to a previously used slug" do
183
+ with_instance_of(model_class, name: "foo") do |record|
184
+ record.name = "bar"
166
185
  record.save!
167
- assert_equal 'bar', record.friendly_id
168
- record.name = 'foo'
186
+ assert_equal "bar", record.friendly_id
187
+ record.name = "foo"
169
188
  record.save!
170
- assert_equal 'foo', record.friendly_id
189
+ assert_equal "foo", record.friendly_id
171
190
  end
172
191
  end
173
192
  end
174
193
 
175
194
  class DependentDestroyTest < TestCaseClass
176
-
177
195
  include FriendlyId::Test
178
196
 
179
197
  class FalseManual < ActiveRecord::Base
180
- self.table_name = 'manuals'
198
+ self.table_name = "manuals"
181
199
 
182
200
  extend FriendlyId
183
- friendly_id :name, :use => :history, :dependent => false
201
+ friendly_id :name, use: :history, dependent: false
184
202
  end
185
203
 
186
204
  class DefaultManual < ActiveRecord::Base
187
- self.table_name = 'manuals'
205
+ self.table_name = "manuals"
188
206
 
189
207
  extend FriendlyId
190
- friendly_id :name, :use => :history
208
+ friendly_id :name, use: :history
191
209
  end
192
210
 
193
- test 'should allow disabling of dependent destroy' do
211
+ test "should allow disabling of dependent destroy" do
194
212
  transaction do
195
- assert FriendlyId::Slug.find_by_slug('foo').nil?
196
- l = FalseManual.create! :name => 'foo'
197
- assert FriendlyId::Slug.find_by_slug('foo').present?
213
+ assert FriendlyId::Slug.find_by_slug("foo").nil?
214
+ l = FalseManual.create! name: "foo"
215
+ assert FriendlyId::Slug.find_by_slug("foo").present?
198
216
  l.destroy
199
- assert FriendlyId::Slug.find_by_slug('foo').present?
217
+ assert FriendlyId::Slug.find_by_slug("foo").present?
200
218
  end
201
219
  end
202
220
 
203
- test 'should dependently destroy by default' do
221
+ test "should dependently destroy by default" do
204
222
  transaction do
205
- assert FriendlyId::Slug.find_by_slug('baz').nil?
206
- l = DefaultManual.create! :name => 'baz'
207
- assert FriendlyId::Slug.find_by_slug('baz').present?
223
+ assert FriendlyId::Slug.find_by_slug("baz").nil?
224
+ l = DefaultManual.create! name: "baz"
225
+ assert FriendlyId::Slug.find_by_slug("baz").present?
208
226
  l.destroy
209
- assert FriendlyId::Slug.find_by_slug('baz').nil?
227
+ assert FriendlyId::Slug.find_by_slug("baz").nil?
210
228
  end
211
229
  end
212
230
  end
213
231
 
214
- if ActiveRecord::VERSION::STRING >= '5.0'
232
+ if ActiveRecord::VERSION::STRING >= "5.0"
215
233
  class HistoryTestWithParanoidDeletes < HistoryTest
216
234
  class ParanoidRecord < ActiveRecord::Base
217
235
  extend FriendlyId
218
- friendly_id :name, :use => :history, :dependent => false
236
+ friendly_id :name, use: :history, dependent: false
219
237
 
220
238
  default_scope { where(deleted_at: nil) }
221
239
  end
@@ -224,19 +242,19 @@ if ActiveRecord::VERSION::STRING >= '5.0'
224
242
  ParanoidRecord
225
243
  end
226
244
 
227
- test 'slug should have a sluggable even when soft deleted by a library' do
245
+ test "slug should have a sluggable even when soft deleted by a library" do
228
246
  transaction do
229
- assert FriendlyId::Slug.find_by_slug('paranoid').nil?
230
- record = model_class.create(name: 'paranoid')
231
- assert FriendlyId::Slug.find_by_slug('paranoid').present?
247
+ assert FriendlyId::Slug.find_by_slug("paranoid").nil?
248
+ record = model_class.create(name: "paranoid")
249
+ assert FriendlyId::Slug.find_by_slug("paranoid").present?
232
250
 
233
- record.update_attribute(:deleted_at, Time.now)
251
+ record.update deleted_at: Time.now
234
252
 
235
- orphan_slug = FriendlyId::Slug.find_by_slug('paranoid')
236
- assert orphan_slug.present?, 'Orphaned slug should exist'
253
+ orphan_slug = FriendlyId::Slug.find_by_slug("paranoid")
254
+ assert orphan_slug.present?, "Orphaned slug should exist"
237
255
 
238
256
  assert orphan_slug.valid?, "Errors: #{orphan_slug.errors.full_messages}"
239
- assert orphan_slug.sluggable.present?, 'Orphaned slug should still find corresponding paranoid sluggable'
257
+ assert orphan_slug.sluggable.present?, "Orphaned slug should still find corresponding paranoid sluggable"
240
258
  end
241
259
  end
242
260
  end
@@ -245,7 +263,7 @@ end
245
263
  class HistoryTestWithSti < HistoryTest
246
264
  class Journalist < ActiveRecord::Base
247
265
  extend FriendlyId
248
- friendly_id :name, :use => [:slugged, :history]
266
+ friendly_id :name, use: [:slugged, :history]
249
267
  end
250
268
 
251
269
  class Editorialist < Journalist
@@ -259,16 +277,15 @@ end
259
277
  class HistoryTestWithFriendlyFinders < HistoryTest
260
278
  class Journalist < ActiveRecord::Base
261
279
  extend FriendlyId
262
- friendly_id :name, :use => [:slugged, :finders, :history]
280
+ friendly_id :name, use: [:slugged, :finders, :history]
263
281
  end
264
282
 
265
283
  class Restaurant < ActiveRecord::Base
266
284
  extend FriendlyId
267
285
  belongs_to :city
268
- friendly_id :name, :use => [:slugged, :history, :finders]
286
+ friendly_id :name, use: [:slugged, :history, :finders]
269
287
  end
270
288
 
271
-
272
289
  test "should be findable by old slugs" do
273
290
  [Journalist, Restaurant].each do |model_class|
274
291
  with_instance_of(model_class) do |record|
@@ -297,7 +314,7 @@ class HistoryTestWithFindersBeforeHistory < HistoryTest
297
314
 
298
315
  belongs_to :novelist
299
316
 
300
- friendly_id :name, :use => [:finders, :history]
317
+ friendly_id :name, use: [:finders, :history]
301
318
 
302
319
  def should_generate_new_friendly_id?
303
320
  slug.blank? || name_changed?
@@ -306,8 +323,8 @@ class HistoryTestWithFindersBeforeHistory < HistoryTest
306
323
 
307
324
  test "should be findable by old slug through has_many association" do
308
325
  transaction do
309
- novelist = Novelist.create!(:name => "Stephen King")
310
- novel = novelist.novels.create(:name => "Rita Hayworth and Shawshank Redemption")
326
+ novelist = Novelist.create!(name: "Stephen King")
327
+ novel = novelist.novels.create(name: "Rita Hayworth and Shawshank Redemption")
311
328
  slug = novel.slug
312
329
  novel.name = "Shawshank Redemption"
313
330
  novel.save!
@@ -324,7 +341,7 @@ end
324
341
  class Restaurant < ActiveRecord::Base
325
342
  extend FriendlyId
326
343
  belongs_to :city
327
- friendly_id :name, :use => [:scoped, :history], :scope => :city
344
+ friendly_id :name, use: [:scoped, :history], scope: :city
328
345
  end
329
346
 
330
347
  class ScopedHistoryTest < TestCaseClass
@@ -368,21 +385,48 @@ class ScopedHistoryTest < TestCaseClass
368
385
  record.slug = nil
369
386
  record.save!
370
387
 
371
- second_record = model_class.create! :city => city, :name => 'x'
388
+ second_record = model_class.create! city: city, name: "x"
372
389
  assert_match(/x-.+/, second_record.friendly_id)
373
390
 
374
- third_record = model_class.create! :city => city, :name => 'y'
391
+ third_record = model_class.create! city: city, name: "y"
375
392
  assert_match(/y-.+/, third_record.friendly_id)
376
393
  end
377
394
  end
378
395
  end
379
396
 
397
+ test "should record history when scope changes" do
398
+ transaction do
399
+ city1 = City.create!
400
+ city2 = City.create!
401
+ with_instance_of(Restaurant) do |record|
402
+ record.name = "x"
403
+ record.slug = nil
404
+
405
+ record.city = city1
406
+ record.save!
407
+ assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
408
+ assert_equal("x", record.slugs.reload.first.slug)
409
+
410
+ record.city = city2
411
+ record.save!
412
+ assert_equal("city_id:#{city2.id}", record.slugs.reload.first.scope)
413
+
414
+ record.name = "y"
415
+ record.slug = nil
416
+ record.city = city1
417
+ record.save!
418
+ assert_equal("city_id:#{city1.id}", record.slugs.reload.first.scope)
419
+ assert_equal("y", record.slugs.reload.first.slug)
420
+ end
421
+ end
422
+ end
423
+
380
424
  test "should allow equal slugs in different scopes" do
381
425
  transaction do
382
426
  city = City.create!
383
427
  second_city = City.create!
384
- record = model_class.create! :city => city, :name => 'x'
385
- second_record = model_class.create! :city => second_city, :name => 'x'
428
+ record = model_class.create! city: city, name: "x"
429
+ second_record = model_class.create! city: second_city, name: "x"
386
430
 
387
431
  assert_equal record.slug, second_record.slug
388
432
  end
@@ -0,0 +1,31 @@
1
+ require "helper"
2
+
3
+ class NumericSlugTest < TestCaseClass
4
+ include FriendlyId::Test
5
+ include FriendlyId::Test::Shared::Core
6
+
7
+ def model_class
8
+ Article
9
+ end
10
+
11
+ test "should generate numeric slugs" do
12
+ transaction do
13
+ record = model_class.create! name: "123"
14
+ assert_equal "123", record.slug
15
+ end
16
+ end
17
+
18
+ test "should find by numeric slug" do
19
+ transaction do
20
+ record = model_class.create! name: "123"
21
+ assert_equal model_class.friendly.find("123").id, record.id
22
+ end
23
+ end
24
+
25
+ test "should exist? by numeric slug" do
26
+ transaction do
27
+ model_class.create! name: "123"
28
+ assert model_class.friendly.exists?("123")
29
+ end
30
+ end
31
+ end
@@ -1,8 +1,6 @@
1
1
  require "helper"
2
2
 
3
-
4
3
  class ObjectUtilsTest < TestCaseClass
5
-
6
4
  include FriendlyId::Test
7
5
 
8
6
  test "strings with letters are friendly_ids" do
@@ -1,12 +1,11 @@
1
1
  require "helper"
2
2
 
3
3
  class ReservedTest < TestCaseClass
4
-
5
4
  include FriendlyId::Test
6
5
 
7
6
  class Journalist < ActiveRecord::Base
8
7
  extend FriendlyId
9
- friendly_id :slug_candidates, :use => [:slugged, :reserved], :reserved_words => %w(new edit)
8
+ friendly_id :slug_candidates, use: [:slugged, :reserved], reserved_words: %w[new edit]
10
9
 
11
10
  after_validation :move_friendly_id_error_to_name
12
11
 
@@ -24,9 +23,9 @@ class ReservedTest < TestCaseClass
24
23
  end
25
24
 
26
25
  test "should reserve words" do
27
- %w(new edit NEW Edit).each do |word|
26
+ %w[new edit NEW Edit].each do |word|
28
27
  transaction do
29
- assert_raises(ActiveRecord::RecordInvalid) {model_class.create! :name => word}
28
+ assert_raises(ActiveRecord::RecordInvalid) { model_class.create! name: word }
30
29
  end
31
30
  end
32
31
  end
@@ -43,7 +42,7 @@ class ReservedTest < TestCaseClass
43
42
 
44
43
  test "should reject reserved candidates" do
45
44
  transaction do
46
- record = model_class.new(:name => 'new')
45
+ record = model_class.new(name: "new")
47
46
  def record.slug_candidates
48
47
  [:name, "foo"]
49
48
  end
@@ -54,22 +53,21 @@ class ReservedTest < TestCaseClass
54
53
 
55
54
  test "should be invalid if all candidates are reserved" do
56
55
  transaction do
57
- record = model_class.new(:name => 'new')
56
+ record = model_class.new(name: "new")
58
57
  def record.slug_candidates
59
58
  ["edit", "new"]
60
59
  end
61
- assert_raises(ActiveRecord::RecordInvalid) {record.save!}
60
+ assert_raises(ActiveRecord::RecordInvalid) { record.save! }
62
61
  end
63
62
  end
64
63
 
65
64
  test "should optionally treat reserved words as conflict" do
66
65
  klass = Class.new(model_class) do
67
- friendly_id :slug_candidates, :use => [:slugged, :reserved], :reserved_words => %w(new edit), :treat_reserved_as_conflict => true
66
+ friendly_id :slug_candidates, use: [:slugged, :reserved], reserved_words: %w[new edit], treat_reserved_as_conflict: true
68
67
  end
69
68
 
70
- with_instance_of(klass, name: 'new') do |record|
71
- assert_match(/new-([0-9a-z]+\-){4}[0-9a-z]+\z/, record.slug)
69
+ with_instance_of(klass, name: "new") do |record|
70
+ assert_match(/new-([0-9a-z]+-){4}[0-9a-z]+\z/, record.slug)
72
71
  end
73
72
  end
74
-
75
73
  end
data/test/schema.rb CHANGED
@@ -25,7 +25,7 @@ module FriendlyId
25
25
 
26
26
  tables.each do |table_name|
27
27
  create_table table_name do |t|
28
- t.string :name
28
+ t.string :name
29
29
  t.boolean :active
30
30
  end
31
31
  end
@@ -41,7 +41,7 @@ module FriendlyId
41
41
 
42
42
  slugged_tables.each do |table_name|
43
43
  add_column table_name, :slug, :string
44
- add_index table_name, :slug, :unique => true if 'novels' != table_name
44
+ add_index table_name, :slug, unique: true if table_name != "novels"
45
45
  end
46
46
 
47
47
  scoped_tables.each do |table_name|
@@ -57,7 +57,7 @@ module FriendlyId
57
57
  # This will be used to test scopes
58
58
  add_column :novels, :novelist_id, :integer
59
59
  add_column :novels, :publisher_id, :integer
60
- add_index :novels, [:slug, :publisher_id, :novelist_id], :unique => true
60
+ add_index :novels, [:slug, :publisher_id, :novelist_id], unique: true
61
61
 
62
62
  # This will be used to test column name quoting
63
63
  add_column :journalists, "strange name", :string
@@ -69,6 +69,7 @@ module FriendlyId
69
69
  add_column :journalists, "slug_en", :string
70
70
  add_column :journalists, "slug_es", :string
71
71
  add_column :journalists, "slug_de", :string
72
+ add_column :journalists, "slug_fr_ca", :string
72
73
 
73
74
  # This will be used to test relationships
74
75
  add_column :books, :author_id, :integer
@@ -77,7 +78,7 @@ module FriendlyId
77
78
  add_column :restaurants, :city_id, :integer
78
79
 
79
80
  # Used to test candidates
80
- add_column :cities, :code, :string, :limit => 3
81
+ add_column :cities, :code, :string, limit: 3
81
82
 
82
83
  # Used as a non-default slug_column
83
84
  add_column :authors, :subdomain, :string