friendly_id 5.2.2 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
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