friendly_id 5.2.2 → 5.4.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 (48) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.github/stale.yml +17 -0
  5. data/.github/workflows/test.yml +60 -0
  6. data/Changelog.md +39 -1
  7. data/Gemfile +3 -0
  8. data/README.md +54 -164
  9. data/Rakefile +2 -2
  10. data/UPGRADING.md +115 -0
  11. data/certs/parndt.pem +25 -0
  12. data/friendly_id.gemspec +9 -5
  13. data/gemfiles/Gemfile.rails-5.0.rb +2 -2
  14. data/gemfiles/{Gemfile.rails-4.2.rb → Gemfile.rails-5.1.rb} +4 -5
  15. data/gemfiles/{Gemfile.rails-4.1.rb → Gemfile.rails-5.2.rb} +5 -7
  16. data/gemfiles/{Gemfile.rails-4.0.rb → Gemfile.rails-6.0.rb} +5 -8
  17. data/lib/friendly_id/base.rb +4 -8
  18. data/lib/friendly_id/candidates.rb +0 -2
  19. data/lib/friendly_id/configuration.rb +3 -2
  20. data/lib/friendly_id/finder_methods.rb +18 -7
  21. data/lib/friendly_id/finders.rb +1 -1
  22. data/lib/friendly_id/history.rb +21 -12
  23. data/lib/friendly_id/initializer.rb +11 -0
  24. data/lib/friendly_id/migration.rb +9 -3
  25. data/lib/friendly_id/object_utils.rb +9 -2
  26. data/lib/friendly_id/reserved.rb +1 -0
  27. data/lib/friendly_id/scoped.rb +9 -2
  28. data/lib/friendly_id/sequentially_slugged.rb +12 -2
  29. data/lib/friendly_id/slug.rb +4 -0
  30. data/lib/friendly_id/slug_generator.rb +6 -1
  31. data/lib/friendly_id/slugged.rb +3 -3
  32. data/lib/friendly_id/version.rb +1 -1
  33. data/test/databases.yml +6 -4
  34. data/test/finders_test.rb +24 -0
  35. data/test/helper.rb +13 -3
  36. data/test/history_test.rb +86 -7
  37. data/test/numeric_slug_test.rb +31 -0
  38. data/test/object_utils_test.rb +5 -3
  39. data/test/reserved_test.rb +10 -0
  40. data/test/schema.rb +19 -2
  41. data/test/scoped_test.rb +13 -0
  42. data/test/sequentially_slugged_test.rb +59 -0
  43. data/test/shared.rb +4 -4
  44. data/test/simple_i18n_test.rb +2 -2
  45. data/test/slugged_test.rb +168 -4
  46. metadata +48 -19
  47. metadata.gz.sig +0 -0
  48. data/.travis.yml +0 -40
@@ -0,0 +1,115 @@
1
+ ## Articles
2
+
3
+ * [Migrating an ad-hoc URL slug system to FriendlyId](http://olivierlacan.com/posts/migrating-an-ad-hoc-url-slug-system-to-friendly-id/)
4
+ * [Pretty URLs with FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid)
5
+
6
+ ## Docs
7
+
8
+ The most current docs from the master branch can always be found
9
+ [here](http://norman.github.io/friendly_id).
10
+
11
+ Docs for older versions are also available:
12
+
13
+ * [5.0](http://norman.github.io/friendly_id/5.0/)
14
+ * [4.0](http://norman.github.io/friendly_id/4.0/)
15
+ * [3.3](http://norman.github.io/friendly_id/3.3/)
16
+ * [2.3](http://norman.github.io/friendly_id/2.3/)
17
+
18
+ ## What Changed in Version 5.1
19
+
20
+ 5.1 is a bugfix release, but bumps the minor version because some applications may be dependent
21
+ on the previously buggy behavior. The changes include:
22
+
23
+ * Blank strings can no longer be used as slugs.
24
+ * When the first slug candidate is rejected because it is reserved, additional candidates will
25
+ now be considered before marking the record as invalid.
26
+ * The `:finders` module is now compatible with Rails 4.2.
27
+
28
+ ## What Changed in Version 5.0
29
+
30
+ As of version 5.0, FriendlyId uses [semantic versioning](http://semver.org/). Therefore, as you might
31
+ infer from the version number, 5.0 introduces changes incompatible with 4.0.
32
+
33
+ The most important changes are:
34
+
35
+ * Finders are no longer overridden by default. If you want to do friendly finds,
36
+ you must do `Model.friendly.find` rather than `Model.find`. You can however
37
+ restore FriendlyId 4-style finders by using the `:finders` addon:
38
+
39
+ ```ruby
40
+ friendly_id :foo, use: :slugged # you must do MyClass.friendly.find('bar')
41
+ # or...
42
+ friendly_id :foo, use: [:slugged, :finders] # you can now do MyClass.find('bar')
43
+ ```
44
+ * A new "candidates" functionality which makes it easy to set up a list of
45
+ alternate slugs that can be used to uniquely distinguish records, rather than
46
+ appending a sequence. For example:
47
+
48
+ ```ruby
49
+ class Restaurant < ActiveRecord::Base
50
+ extend FriendlyId
51
+ friendly_id :slug_candidates, use: :slugged
52
+
53
+ # Try building a slug based on the following fields in
54
+ # increasing order of specificity.
55
+ def slug_candidates
56
+ [
57
+ :name,
58
+ [:name, :city],
59
+ [:name, :street, :city],
60
+ [:name, :street_number, :street, :city]
61
+ ]
62
+ end
63
+ end
64
+ ```
65
+ * Now that candidates have been added, FriendlyId no longer uses a numeric
66
+ sequence to differentiate conflicting slug, but rather a UUID (e.g. something
67
+ like `2bc08962-b3dd-4f29-b2e6-244710c86106`). This makes the
68
+ codebase simpler and more reliable when running concurrently, at the expense
69
+ of uglier ids being generated when there are conflicts.
70
+ * The default sequence separator has been changed from two dashes to one dash.
71
+ * Slugs are no longer regenerated when a record is saved. If you want to regenerate
72
+ a slug, you must explicitly set the slug column to nil:
73
+
74
+ ```ruby
75
+ restaurant.friendly_id # joes-diner
76
+ restaurant.name = "The Plaza Diner"
77
+ restaurant.save!
78
+ restaurant.friendly_id # joes-diner
79
+ restaurant.slug = nil
80
+ restaurant.save!
81
+ restaurant.friendly_id # the-plaza-diner
82
+ ```
83
+
84
+ You can restore some of the old behavior by overriding the
85
+ `should_generate_new_friendly_id?` method.
86
+ * The `friendly_id` Rails generator now generates an initializer showing you
87
+ how to do some common global configuration.
88
+ * The Globalize plugin has moved to a [separate gem](https://github.com/norman/friendly_id-globalize) (currently in alpha).
89
+ * The `:reserved` module no longer includes any default reserved words.
90
+ Previously it blocked "edit" and "new" everywhere. The default word list has
91
+ been moved to `config/initializers/friendly_id.rb` and now includes many more
92
+ words.
93
+ * The `:history` and `:scoped` addons can now be used together.
94
+ * Since it now requires Rails 4, FriendlyId also now requires Ruby 1.9.3 or
95
+ higher.
96
+
97
+ ## Upgrading from FriendlyId 4.0
98
+
99
+ Run `rails generate friendly_id --skip-migration` and edit the initializer
100
+ generated in `config/initializers/friendly_id.rb`. This file contains notes
101
+ describing how to restore (or not) some of the defaults from FriendlyId 4.0.
102
+
103
+ If you want to use the `:history` and `:scoped` addons together, you must add a
104
+ `:scope` column to your friendly_id_slugs table and replace the unique index on
105
+ `:slug` and `:sluggable_type` with a unique index on those two columns, plus
106
+ the new `:scope` column.
107
+
108
+ A migration like this should be sufficient:
109
+
110
+ ```ruby
111
+ add_column :friendly_id_slugs, :scope, :string
112
+ remove_index :friendly_id_slugs, [:slug, :sluggable_type]
113
+ add_index :friendly_id_slugs, [:slug, :sluggable_type]
114
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], unique: true
115
+ ```
@@ -0,0 +1,25 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEMjCCApqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhnZW1z
3
+ L0RDPXAvREM9YXJuZHQvREM9aW8wHhcNMjAwNTEwMjIxOTQ2WhcNMjEwNTEwMjIx
4
+ OTQ2WjAjMSEwHwYDVQQDDBhnZW1zL0RDPXAvREM9YXJuZHQvREM9aW8wggGiMA0G
5
+ CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDT+JzHYPGMYJt9ct2DCsbIymn1uJJp
6
+ HnDkQESfsGe40jTC90oF2iVbVOkaacNc1N3CSWUZvZjuygUuS86P6/kpBILGdO2+
7
+ bkXXKtfGC2YGGx9TdNLpCb4925vQHvdFeKXGpQDZdDw1SNC6zraZou47CvOE1cl2
8
+ Bp+1QMZuGRZ4+5CzOEWDWurjqce3O1jUEbyBB7z5H0h/YEaxfXipxhL1Dhi0sgkH
9
+ qP/e6SxzifdifdZCksJFQ06a1ji9hJY6eM23qbv/aaluVHAZSVBAQBS7rYniLo+N
10
+ G4vpFhoubQO5u8UluUtCaPUpI/qOvVcSaZn3ZkzlMwC8b1RwAeXBQmtFE2wnrv2i
11
+ ovTwoN7rHchwhgaHbkuFh4Wr92wGbrWL7J+X8rWKk1f8RF4kvtNE/NA6YrkxTpVh
12
+ QMyDmekt7rTxvcq2NneLGroWIUVCx/JID+Jw492LKQ6Sl1/P2TRzdEDtqZAZL0gt
13
+ xlWeMUfGG2D/gLnhs5qnaFaWQwGTmBnTgHcCAwEAAaNxMG8wCQYDVR0TBAIwADAL
14
+ BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEqtAyQVxPgKsrgoTQ1YmaIu/fmvMBoGA1Ud
15
+ EQQTMBGBD2dlbXNAcC5hcm5kdC5pbzAaBgNVHRIEEzARgQ9nZW1zQHAuYXJuZHQu
16
+ aW8wDQYJKoZIhvcNAQELBQADggGBALu2HM50B8xqlAXkCwavJDvWWtV9pG1igFUg
17
+ friZRWprUQ5nTaNmAd8p8qbJQwaIK2gt+DfYWfB9LtKnQTfbhLRBbmJ7zYw8LjKY
18
+ PwCs4RWjDAiuyCO3ppfsz+1bsMUXPLgWlaUkXsUy3nr2NruEFTO9zu3wGYQQ93Tt
19
+ vYSHOnP35UB4QjsjNrOO7FBaQfy6O909PP+GnVcJ62s9c26voJz63RSolwY7Jydw
20
+ XUlG68jjJKSoDHRzVTmNB7sX8rs8P2kvYkpIUXPHyls3mWBWjBWbdEYWESZrxI2x
21
+ dS7jY3AnfqhvsWra2pSREb2IDqPnJrHVOejnEI/zuuufUxLwDx3AC6SMdsyWkZ7V
22
+ 9OmLt2rg75Sct6h2220lO5ySqYtqAXuOMBDGv5L0zLalx1g8LACA7uILTKVWh8B8
23
+ Hsej0MQ3drCB1eA4c9OXdCUQJnY2aLTq3uNvTbZvoTgWK55eq3KLBJ4zzoKZ4tBX
24
+ /HIFI/fEwYlI1Ji3oikUrHkc4rWgaQ==
25
+ -----END CERTIFICATE-----
@@ -8,20 +8,19 @@ Gem::Specification.new do |s|
8
8
  s.email = ["norman@njclarke.com", "p@arndt.io"]
9
9
  s.homepage = "https://github.com/norman/friendly_id"
10
10
  s.summary = "A comprehensive slugging and pretty-URL plugin."
11
- s.rubyforge_project = "friendly_id"
12
11
  s.files = `git ls-files`.split("\n")
13
12
  s.test_files = `git ls-files -- {test}/*`.split("\n")
14
13
  s.require_paths = ["lib"]
15
14
  s.license = 'MIT'
16
15
 
17
- s.required_ruby_version = '>= 1.9.3'
16
+ s.required_ruby_version = '>= 2.1.0'
18
17
 
19
18
  s.add_dependency 'activerecord', '>= 4.0.0'
20
19
 
21
20
  s.add_development_dependency 'coveralls'
22
- s.add_development_dependency 'railties', '~> 4.0'
23
- s.add_development_dependency 'minitest', '~> 5.3.5'
24
- s.add_development_dependency 'mocha', '~> 1.1.0'
21
+ s.add_development_dependency 'railties', '>= 4.0'
22
+ s.add_development_dependency 'minitest', '~> 5.3'
23
+ s.add_development_dependency 'mocha', '~> 1.1'
25
24
  s.add_development_dependency 'yard'
26
25
  s.add_development_dependency 'i18n'
27
26
  s.add_development_dependency 'ffaker'
@@ -32,4 +31,9 @@ FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
32
31
  Active Record. It lets you create pretty URLs and work with human-friendly
33
32
  strings as if they were numeric ids.
34
33
  EOM
34
+
35
+ s.cert_chain = [File.expand_path('certs/parndt.pem', __dir__)]
36
+ if $PROGRAM_NAME =~ /gem\z/ && ARGV.include?('build') && ARGV.include?(__FILE__)
37
+ s.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
38
+ end
35
39
  end
@@ -9,8 +9,8 @@ gem 'i18n', '~> 0.7.0'
9
9
  # Database Configuration
10
10
  group :development, :test do
11
11
  platforms :jruby do
12
- gem 'activerecord-jdbcmysql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter.git', branch: 'rails-5'
13
- gem 'activerecord-jdbcpostgresql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter.git', branch: 'rails-5'
12
+ gem 'activerecord-jdbcmysql-adapter', '~> 50.1'
13
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 50.1'
14
14
  gem 'kramdown'
15
15
  end
16
16
 
@@ -2,15 +2,14 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec path: '../'
4
4
 
5
- gem 'activerecord', '~> 4.2.1'
6
- gem 'railties', '~> 4.2.1'
7
- gem 'i18n', '~> 0.7.0'
5
+ gem 'activerecord', '~> 5.1.0'
6
+ gem 'railties', '~> 5.1.0'
8
7
 
9
8
  # Database Configuration
10
9
  group :development, :test do
11
10
  platforms :jruby do
12
- gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
13
- gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
11
+ gem 'activerecord-jdbcmysql-adapter', '~> 50.1'
12
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 50.1'
14
13
  gem 'kramdown'
15
14
  end
16
15
 
@@ -2,21 +2,20 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec path: '../'
4
4
 
5
- gem 'activerecord', '~> 4.1.9'
6
- gem 'railties', '~> 4.1.9'
5
+ gem 'activerecord', '~> 5.2.0'
6
+ gem 'railties', '~> 5.2.0'
7
7
 
8
8
  # Database Configuration
9
9
  group :development, :test do
10
10
  platforms :jruby do
11
- gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.14'
12
- gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
13
- gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
11
+ gem 'activerecord-jdbcmysql-adapter', '~> 51.1'
12
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 51.1'
14
13
  gem 'kramdown'
15
14
  end
16
15
 
17
16
  platforms :ruby, :rbx do
18
17
  gem 'sqlite3'
19
- gem 'mysql2', '~> 0.3.13'
18
+ gem 'mysql2'
20
19
  gem 'pg'
21
20
  gem 'redcarpet'
22
21
  end
@@ -24,6 +23,5 @@ group :development, :test do
24
23
  platforms :rbx do
25
24
  gem 'rubysl', '~> 2.0'
26
25
  gem 'rubinius-developer_tools'
27
- gem 'json'
28
26
  end
29
27
  end
@@ -2,22 +2,20 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec path: '../'
4
4
 
5
- gem 'activerecord', '~> 4.0.13'
6
- gem 'railties', '~> 4.0.13'
7
- gem 'minitest', '~> 4.5.0'
5
+ gem 'activerecord', '~> 6.0.0'
6
+ gem 'railties', '~> 6.0.0'
8
7
 
9
8
  # Database Configuration
10
9
  group :development, :test do
11
10
  platforms :jruby do
12
- gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.14'
13
- gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
14
- gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
11
+ gem 'activerecord-jdbcmysql-adapter', '~> 51.1'
12
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 51.1'
15
13
  gem 'kramdown'
16
14
  end
17
15
 
18
16
  platforms :ruby, :rbx do
19
17
  gem 'sqlite3'
20
- gem 'mysql2', '~> 0.3.10'
18
+ gem 'mysql2'
21
19
  gem 'pg'
22
20
  gem 'redcarpet'
23
21
  end
@@ -25,6 +23,5 @@ group :development, :test do
25
23
  platforms :rbx do
26
24
  gem 'rubysl', '~> 2.0'
27
25
  gem 'rubinius-developer_tools'
28
- gem 'json'
29
26
  end
30
27
  end
@@ -159,8 +159,9 @@ often better and easier to use {FriendlyId::Slugged slugs}.
159
159
  #
160
160
  # @option options [Symbol,Module] :use The addon or name of an addon to use.
161
161
  # By default, FriendlyId provides {FriendlyId::Slugged :slugged},
162
- # {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
163
- # {FriendlyId::Scoped :scoped}, and {FriendlyId::SimpleI18n :simple_i18n}.
162
+ # {FriendlyId::Reserved :finders}, {FriendlyId::History :history},
163
+ # {FriendlyId::Reserved :reserved}, {FriendlyId::Scoped :scoped}, and
164
+ # {FriendlyId::SimpleI18n :simple_i18n}.
164
165
  #
165
166
  # @option options [Array] :reserved_words Available when using `:reserved`,
166
167
  # which is loaded by default. Sets an array of words banned for use as
@@ -261,12 +262,7 @@ often better and easier to use {FriendlyId::Slugged slugs}.
261
262
  # Either the friendly_id, or the numeric id cast to a string.
262
263
  def to_param
263
264
  if friendly_id_config.routes == :friendly
264
- if attribute_changed?(friendly_id_config.query_field)
265
- diff = changes[friendly_id_config.query_field]
266
- diff.first || diff.second
267
- else
268
- friendly_id.presence.to_param || super
269
- end
265
+ friendly_id.presence.to_param || super
270
266
  else
271
267
  super
272
268
  end
@@ -63,8 +63,6 @@ module FriendlyId
63
63
  slug.present?
64
64
  end
65
65
 
66
- private
67
-
68
66
  def reserved?(slug)
69
67
  config = @object.friendly_id_config
70
68
  return false unless config.uses? ::FriendlyId::Reserved
@@ -25,6 +25,7 @@ module FriendlyId
25
25
  attr_accessor :routes
26
26
 
27
27
  def initialize(model_class, values = nil)
28
+ @base = nil
28
29
  @model_class = model_class
29
30
  @defaults = {}
30
31
  @modules = []
@@ -47,8 +48,8 @@ module FriendlyId
47
48
  #
48
49
  # @param [#to_s,Module] modules Arguments should be Modules, or symbols or
49
50
  # strings that correspond with the name of an addon to use with FriendlyId.
50
- # By default FriendlyId provides `:slugged`, `:history`, `:simple_i18n`,
51
- # and `:scoped`.
51
+ # By default FriendlyId provides `:slugged`, `:finders`, `:history`,
52
+ # `:reserved`, `:simple_i18n`, and `:scoped`.
52
53
  def use(*modules)
53
54
  modules.to_a.flatten.compact.map do |object|
54
55
  mod = get_module(object)
@@ -9,7 +9,7 @@ module FriendlyId
9
9
  # id matching '123' and then fall back to looking for a record with the
10
10
  # numeric id '123'.
11
11
  #
12
- # Since FriendlyId 5.0, if the id is a numeric string like '123-foo' it
12
+ # Since FriendlyId 5.0, if the id is a nonnumeric string like '123-foo' it
13
13
  # will *only* search by friendly id and not fall back to the regular find
14
14
  # method.
15
15
  #
@@ -20,20 +20,22 @@ module FriendlyId
20
20
  return super if args.count != 1 || id.unfriendly_id?
21
21
  first_by_friendly_id(id).tap {|result| return result unless result.nil?}
22
22
  return super if potential_primary_key?(id)
23
- raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
23
+ raise_not_found_exception id
24
+
24
25
  end
25
26
 
26
27
  # Returns true if a record with the given id exists.
27
28
  def exists?(conditions = :none)
28
- return super unless conditions.friendly_id?
29
- exists_by_friendly_id?(conditions)
29
+ return super if conditions.unfriendly_id?
30
+ return true if exists_by_friendly_id?(conditions)
31
+ super
30
32
  end
31
33
 
32
34
  # Finds exclusively by the friendly id, completely bypassing original
33
35
  # `find`.
34
36
  # @raise ActiveRecord::RecordNotFound
35
37
  def find_by_friendly_id(id)
36
- first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
38
+ first_by_friendly_id(id) or raise raise_not_found_exception(id)
37
39
  end
38
40
 
39
41
  def exists_by_friendly_id?(id)
@@ -50,14 +52,23 @@ module FriendlyId
50
52
  when :integer
51
53
  Integer(id, 10) rescue false
52
54
  when :uuid
53
- id.match /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/
55
+ id.match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
54
56
  else
55
57
  true
56
58
  end
57
59
  end
58
60
 
59
61
  def first_by_friendly_id(id)
60
- find_by(friendly_id_config.query_field => id)
62
+ find_by(friendly_id_config.query_field => id.downcase)
63
+ end
64
+
65
+ def raise_not_found_exception(id)
66
+ message = "can't find record with friendly id: #{id.inspect}"
67
+ if ActiveRecord.version < Gem::Version.create('5.0') then
68
+ raise ActiveRecord::RecordNotFound.new(message)
69
+ else
70
+ raise ActiveRecord::RecordNotFound.new(message, name, friendly_id_config.query_field, id)
71
+ end
61
72
  end
62
73
 
63
74
  end
@@ -76,7 +76,7 @@ for models that use FriendlyId with something similar to the following:
76
76
  def self.setup(model_class)
77
77
  model_class.instance_eval do
78
78
  relation.class.send(:include, friendly_id_config.finder_methods)
79
- if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) || ActiveRecord::VERSION::MAJOR == 5
79
+ if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 2) || ActiveRecord::VERSION::MAJOR >= 5
80
80
  model_class.send(:extend, friendly_id_config.finder_methods)
81
81
  end
82
82
  end
@@ -44,9 +44,9 @@ method.
44
44
  @post = Post.friendly.find params[:id]
45
45
 
46
46
  # If an old id or a numeric id was used to find the record, then
47
- # the request path will not match the post_path, and we should do
48
- # a 301 redirect that uses the current friendly id.
49
- if request.path != post_path(@post)
47
+ # the request slug will not match the current slug, and we should do
48
+ # a 301 redirect to the new path
49
+ if params[:id] != @post.slug
50
50
  return redirect_to @post, :status => :moved_permanently
51
51
  end
52
52
  end
@@ -72,7 +72,7 @@ method.
72
72
  # Configures the model instance to use the History add-on.
73
73
  def self.included(model_class)
74
74
  model_class.class_eval do
75
- has_many :slugs, -> {order("#{Slug.quoted_table_name}.id DESC")}, {
75
+ has_many :slugs, -> {order(id: :desc)}, **{
76
76
  :as => :sluggable,
77
77
  :dependent => @friendly_id_config.dependent_value,
78
78
  :class_name => Slug.to_s
@@ -86,14 +86,13 @@ method.
86
86
  include ::FriendlyId::FinderMethods
87
87
 
88
88
  def exists_by_friendly_id?(id)
89
- where(arel_table[friendly_id_config.query_field].eq(id)).exists? || joins(:slugs).where(slug_history_clause(id)).exists?
89
+ super || joins(:slugs).where(slug_history_clause(id)).exists?
90
90
  end
91
91
 
92
92
  private
93
93
 
94
94
  def first_by_friendly_id(id)
95
- matching_record = where(friendly_id_config.query_field => id).first
96
- matching_record || slug_table_record(id)
95
+ super || slug_table_record(id)
97
96
  end
98
97
 
99
98
  def slug_table_record(id)
@@ -111,9 +110,10 @@ method.
111
110
  # to be conflicts. This will allow a record to revert to a previously
112
111
  # used slug.
113
112
  def scope_for_slug_generator
114
- relation = super
115
- return relation if new_record?
116
- relation = relation.joins(:slugs).merge(Slug.where('sluggable_id <> ?', id))
113
+ relation = super.joins(:slugs)
114
+ unless new_record?
115
+ relation = relation.merge(Slug.where('sluggable_id <> ?', id))
116
+ end
117
117
  if friendly_id_config.uses?(:scoped)
118
118
  relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
119
119
  end
@@ -122,17 +122,26 @@ method.
122
122
 
123
123
  def create_slug
124
124
  return unless friendly_id
125
- return if slugs.first.try(:slug) == friendly_id
125
+ return if history_is_up_to_date?
126
126
  # Allow reversion back to a previously used slug
127
127
  relation = slugs.where(:slug => friendly_id)
128
128
  if friendly_id_config.uses?(:scoped)
129
129
  relation = relation.where(:scope => serialized_scope)
130
130
  end
131
- relation.delete_all
131
+ relation.destroy_all unless relation.empty?
132
132
  slugs.create! do |record|
133
133
  record.slug = friendly_id
134
134
  record.scope = serialized_scope if friendly_id_config.uses?(:scoped)
135
135
  end
136
136
  end
137
+
138
+ def history_is_up_to_date?
139
+ latest_history = slugs.first
140
+ check = latest_history.try(:slug) == friendly_id
141
+ if friendly_id_config.uses?(:scoped)
142
+ check = check && latest_history.scope == serialized_scope
143
+ end
144
+ check
145
+ end
137
146
  end
138
147
  end