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
@@ -1,27 +1,22 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- gemspec path: '../'
3
+ gemspec path: "../"
4
4
 
5
- gem 'activerecord', '~> 5.2.0'
6
- gem 'railties', '~> 5.2.0'
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-jdbcmysql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
12
- gem 'activerecord-jdbcpostgresql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
13
- gem 'kramdown'
11
+ gem "activerecord-jdbcmysql-adapter", "~> 51.1"
12
+ gem "activerecord-jdbcpostgresql-adapter", "~> 51.1"
13
+ gem "kramdown"
14
14
  end
15
15
 
16
16
  platforms :ruby, :rbx do
17
- gem 'sqlite3'
18
- gem 'mysql2'
19
- gem 'pg'
20
- gem 'redcarpet'
21
- end
22
-
23
- platforms :rbx do
24
- gem 'rubysl', '~> 2.0'
25
- gem 'rubinius-developer_tools'
17
+ gem "sqlite3"
18
+ gem "mysql2"
19
+ gem "pg"
20
+ gem "redcarpet"
26
21
  end
27
22
  end
@@ -0,0 +1,22 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../"
4
+
5
+ gem "activerecord", "~> 6.0.0"
6
+ gem "railties", "~> 6.0.0"
7
+
8
+ # Database Configuration
9
+ group :development, :test do
10
+ platforms :jruby do
11
+ gem "activerecord-jdbcmysql-adapter", "~> 51.1"
12
+ gem "activerecord-jdbcpostgresql-adapter", "~> 51.1"
13
+ gem "kramdown"
14
+ end
15
+
16
+ platforms :ruby, :rbx do
17
+ gem "sqlite3"
18
+ gem "mysql2"
19
+ gem "pg"
20
+ gem "redcarpet"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../"
4
+
5
+ gem "activerecord", "~> 6.1.4"
6
+ gem "railties", "~> 6.1.4"
7
+
8
+ # Database Configuration
9
+ group :development, :test do
10
+ platforms :jruby do
11
+ gem "activerecord-jdbcmysql-adapter", "~> 61.0"
12
+ gem "activerecord-jdbcpostgresql-adapter", "~> 61.0"
13
+ gem "kramdown"
14
+ end
15
+
16
+ platforms :ruby, :rbx do
17
+ gem "sqlite3"
18
+ gem "mysql2"
19
+ gem "pg"
20
+ gem "redcarpet"
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: "../"
4
+
5
+ gem "activerecord", "~> 7.0.0"
6
+ gem "railties", "~> 7.0.0"
7
+
8
+ # Database Configuration
9
+ group :development, :test do
10
+ platforms :jruby do
11
+ gem "activerecord-jdbcmysql-adapter", "~> 61.0"
12
+ gem "activerecord-jdbcpostgresql-adapter", "~> 61.0"
13
+ gem "kramdown"
14
+ end
15
+
16
+ platforms :ruby, :rbx do
17
+ gem "sqlite3"
18
+ gem "mysql2"
19
+ gem "pg"
20
+ gem "redcarpet"
21
+ end
22
+ end
data/guide.rb CHANGED
@@ -3,14 +3,14 @@
3
3
  # This script generates the Guide.md file included in the Yard docs.
4
4
 
5
5
  def comments_from path
6
- path = File.expand_path("../lib/friendly_id/#{path}", __FILE__)
6
+ path = File.expand_path("../lib/friendly_id/#{path}", __FILE__)
7
7
  match = File.read(path).match(/\n=begin(.*)\n=end/m)[1].to_s
8
- match.split("\n").reject {|x| x =~ /^@/}.join("\n").strip
8
+ match.split("\n").reject { |x| x =~ /^@/ }.join("\n").strip
9
9
  end
10
10
 
11
- File.open(File.expand_path('../Guide.md', __FILE__), 'w:utf-8') do |guide|
12
- ['../friendly_id.rb', 'base.rb', 'finders.rb', 'slugged.rb', 'history.rb',
13
- 'scoped.rb', 'simple_i18n.rb', 'reserved.rb'].each do |file|
11
+ File.open(File.expand_path("../Guide.md", __FILE__), "w:utf-8") do |guide|
12
+ ["../friendly_id.rb", "base.rb", "finders.rb", "slugged.rb", "history.rb",
13
+ "scoped.rb", "simple_i18n.rb", "reserved.rb"].each do |file|
14
14
  guide.write comments_from file
15
15
  guide.write "\n"
16
16
  end
@@ -1,62 +1,59 @@
1
1
  module FriendlyId
2
- =begin
3
-
4
- ## Setting Up FriendlyId in Your Model
5
-
6
- To use FriendlyId in your ActiveRecord models, you must first either extend or
7
- include the FriendlyId module (it makes no difference), then invoke the
8
- {FriendlyId::Base#friendly_id friendly_id} method to configure your desired
9
- options:
10
-
11
- class Foo < ActiveRecord::Base
12
- include FriendlyId
13
- friendly_id :bar, :use => [:slugged, :simple_i18n]
14
- end
15
-
16
- The most important option is `:use`, which you use to tell FriendlyId which
17
- addons it should use. See the documentation for {FriendlyId::Base#friendly_id} for a list of all
18
- available addons, or skim through the rest of the docs to get a high-level
19
- overview.
20
-
21
- *A note about single table inheritance (STI): you must extend FriendlyId in
22
- all classes that participate in STI, both your parent classes and their
23
- children.*
24
-
25
- ### The Default Setup: Simple Models
26
-
27
- The simplest way to use FriendlyId is with a model that has a uniquely indexed
28
- column with no spaces or special characters, and that is seldom or never
29
- updated. The most common example of this is a user name:
30
-
31
- class User < ActiveRecord::Base
32
- extend FriendlyId
33
- friendly_id :login
34
- validates_format_of :login, :with => /\A[a-z0-9]+\z/i
35
- end
36
-
37
- @user = User.friendly.find "joe" # the old User.find(1) still works, too
38
- @user.to_param # returns "joe"
39
- redirect_to @user # the URL will be /users/joe
40
-
41
- In this case, FriendlyId assumes you want to use the column as-is; it will never
42
- modify the value of the column, and your application should ensure that the
43
- value is unique and admissible in a URL:
44
-
45
- class City < ActiveRecord::Base
46
- extend FriendlyId
47
- friendly_id :name
48
- end
49
-
50
- @city.friendly.find "Viña del Mar"
51
- redirect_to @city # the URL will be /cities/Viña%20del%20Mar
52
-
53
- Writing the code to process an arbitrary string into a good identifier for use
54
- in a URL can be repetitive and surprisingly tricky, so for this reason it's
55
- often better and easier to use {FriendlyId::Slugged slugs}.
56
-
57
- =end
2
+ #
3
+ ## Setting Up FriendlyId in Your Model
4
+ #
5
+ # To use FriendlyId in your ActiveRecord models, you must first either extend or
6
+ # include the FriendlyId module (it makes no difference), then invoke the
7
+ # {FriendlyId::Base#friendly_id friendly_id} method to configure your desired
8
+ # options:
9
+ #
10
+ # class Foo < ActiveRecord::Base
11
+ # include FriendlyId
12
+ # friendly_id :bar, :use => [:slugged, :simple_i18n]
13
+ # end
14
+ #
15
+ # The most important option is `:use`, which you use to tell FriendlyId which
16
+ # addons it should use. See the documentation for {FriendlyId::Base#friendly_id} for a list of all
17
+ # available addons, or skim through the rest of the docs to get a high-level
18
+ # overview.
19
+ #
20
+ # *A note about single table inheritance (STI): you must extend FriendlyId in
21
+ # all classes that participate in STI, both your parent classes and their
22
+ # children.*
23
+ #
24
+ ### The Default Setup: Simple Models
25
+ #
26
+ # The simplest way to use FriendlyId is with a model that has a uniquely indexed
27
+ # column with no spaces or special characters, and that is seldom or never
28
+ # updated. The most common example of this is a user name:
29
+ #
30
+ # class User < ActiveRecord::Base
31
+ # extend FriendlyId
32
+ # friendly_id :login
33
+ # validates_format_of :login, :with => /\A[a-z0-9]+\z/i
34
+ # end
35
+ #
36
+ # @user = User.friendly.find "joe" # the old User.find(1) still works, too
37
+ # @user.to_param # returns "joe"
38
+ # redirect_to @user # the URL will be /users/joe
39
+ #
40
+ # In this case, FriendlyId assumes you want to use the column as-is; it will never
41
+ # modify the value of the column, and your application should ensure that the
42
+ # value is unique and admissible in a URL:
43
+ #
44
+ # class City < ActiveRecord::Base
45
+ # extend FriendlyId
46
+ # friendly_id :name
47
+ # end
48
+ #
49
+ # @city.friendly.find "Viña del Mar"
50
+ # redirect_to @city # the URL will be /cities/Viña%20del%20Mar
51
+ #
52
+ # Writing the code to process an arbitrary string into a good identifier for use
53
+ # in a URL can be repetitive and surprisingly tricky, so for this reason it's
54
+ # often better and easier to use {FriendlyId::Slugged slugs}.
55
+ #
58
56
  module Base
59
-
60
57
  # Configure FriendlyId's behavior in a model.
61
58
  #
62
59
  # class Post < ActiveRecord::Base
@@ -159,8 +156,9 @@ often better and easier to use {FriendlyId::Slugged slugs}.
159
156
  #
160
157
  # @option options [Symbol,Module] :use The addon or name of an addon to use.
161
158
  # By default, FriendlyId provides {FriendlyId::Slugged :slugged},
162
- # {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
163
- # {FriendlyId::Scoped :scoped}, and {FriendlyId::SimpleI18n :simple_i18n}.
159
+ # {FriendlyId::Reserved :finders}, {FriendlyId::History :history},
160
+ # {FriendlyId::Reserved :reserved}, {FriendlyId::Scoped :scoped}, and
161
+ # {FriendlyId::SimpleI18n :simple_i18n}.
164
162
  #
165
163
  # @option options [Array] :reserved_words Available when using `:reserved`,
166
164
  # which is loaded by default. Sets an array of words banned for use as
@@ -204,10 +202,10 @@ often better and easier to use {FriendlyId::Slugged slugs}.
204
202
  #
205
203
  # @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
206
204
  def friendly_id(base = nil, options = {}, &block)
207
- yield friendly_id_config if block_given?
205
+ yield friendly_id_config if block
208
206
  friendly_id_config.dependent = options.delete :dependent
209
207
  friendly_id_config.use options.delete :use
210
- friendly_id_config.send :set, base ? options.merge(:base => base) : options
208
+ friendly_id_config.send :set, base ? options.merge(base: base) : options
211
209
  include Model
212
210
  end
213
211
 
@@ -261,12 +259,7 @@ often better and easier to use {FriendlyId::Slugged slugs}.
261
259
  # Either the friendly_id, or the numeric id cast to a string.
262
260
  def to_param
263
261
  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
262
+ friendly_id.presence.to_param || super
270
263
  else
271
264
  super
272
265
  end
@@ -274,7 +267,7 @@ often better and easier to use {FriendlyId::Slugged slugs}.
274
267
 
275
268
  # Clears slug on duplicate records when calling `dup`.
276
269
  def dup
277
- super.tap { |duplicate| duplicate.slug = nil if duplicate.respond_to?('slug=') }
270
+ super.tap { |duplicate| duplicate.slug = nil if duplicate.respond_to?("slug=") }
278
271
  end
279
272
  end
280
273
  end
@@ -1,11 +1,9 @@
1
- require 'securerandom'
1
+ require "securerandom"
2
2
 
3
3
  module FriendlyId
4
-
5
4
  # This class provides the slug candidate functionality.
6
5
  # @see FriendlyId::Slugged
7
6
  class Candidates
8
-
9
7
  include Enumerable
10
8
 
11
9
  def initialize(object, *array)
@@ -14,8 +12,8 @@ module FriendlyId
14
12
  end
15
13
 
16
14
  def each(*args, &block)
17
- return candidates unless block_given?
18
- candidates.each{ |candidate| yield candidate }
15
+ return candidates unless block
16
+ candidates.each { |candidate| yield candidate }
19
17
  end
20
18
 
21
19
  private
@@ -29,13 +27,13 @@ module FriendlyId
29
27
 
30
28
  def normalize(candidates)
31
29
  candidates.map do |candidate|
32
- @object.normalize_friendly_id(candidate.map(&:call).join(' '))
33
- end.select {|x| wanted?(x)}
30
+ @object.normalize_friendly_id(candidate.map(&:call).join(" "))
31
+ end.select { |x| wanted?(x) }
34
32
  end
35
33
 
36
34
  def filter(candidates)
37
- unless candidates.all? {|x| reserved?(x)}
38
- candidates.reject! {|x| reserved?(x)}
35
+ unless candidates.all? { |x| reserved?(x) }
36
+ candidates.reject! { |x| reserved?(x) }
39
37
  end
40
38
  candidates
41
39
  end
@@ -44,7 +42,7 @@ module FriendlyId
44
42
  array.map do |candidate|
45
43
  case candidate
46
44
  when String
47
- [->{candidate}]
45
+ [-> { candidate }]
48
46
  when Array
49
47
  to_candidate_array(object, candidate).flatten
50
48
  when Symbol
@@ -53,7 +51,7 @@ module FriendlyId
53
51
  if candidate.respond_to?(:call)
54
52
  [candidate]
55
53
  else
56
- [->{candidate.to_s}]
54
+ [-> { candidate.to_s }]
57
55
  end
58
56
  end
59
57
  end
@@ -2,7 +2,6 @@ module FriendlyId
2
2
  # The configuration parameters passed to {Base#friendly_id} will be stored in
3
3
  # this object.
4
4
  class Configuration
5
-
6
5
  attr_writer :base
7
6
 
8
7
  # The default configuration options.
@@ -25,9 +24,10 @@ module FriendlyId
25
24
  attr_accessor :routes
26
25
 
27
26
  def initialize(model_class, values = nil)
28
- @model_class = model_class
29
- @defaults = {}
30
- @modules = []
27
+ @base = nil
28
+ @model_class = model_class
29
+ @defaults = {}
30
+ @modules = []
31
31
  @finder_methods = FriendlyId::FinderMethods
32
32
  self.routes = :friendly
33
33
  set values
@@ -47,8 +47,8 @@ module FriendlyId
47
47
  #
48
48
  # @param [#to_s,Module] modules Arguments should be Modules, or symbols or
49
49
  # 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`.
50
+ # By default FriendlyId provides `:slugged`, `:finders`, `:history`,
51
+ # `:reserved`, `:simple_i18n`, and `:scoped`.
52
52
  def use(*modules)
53
53
  modules.to_a.flatten.compact.map do |object|
54
54
  mod = get_module(object)
@@ -101,11 +101,11 @@ module FriendlyId
101
101
  private
102
102
 
103
103
  def get_module(object)
104
- Module === object ? object : FriendlyId.const_get(object.to_s.titleize.camelize.gsub(/\s+/, ''))
104
+ Module === object ? object : FriendlyId.const_get(object.to_s.titleize.camelize.gsub(/\s+/, ""))
105
105
  end
106
106
 
107
107
  def set(values)
108
- values and values.each {|name, value| self.send "#{name}=", value}
108
+ values&.each { |name, value| send "#{name}=", value }
109
109
  end
110
110
  end
111
111
  end
@@ -1,7 +1,5 @@
1
1
  module FriendlyId
2
-
3
2
  module FinderMethods
4
-
5
3
  # Finds a record using the given id.
6
4
  #
7
5
  # If the id is "unfriendly", it will call the original find method.
@@ -9,35 +7,51 @@ module FriendlyId
9
7
  # id matching '123' and then fall back to looking for a record with the
10
8
  # numeric id '123'.
11
9
  #
10
+ # @param [Boolean] allow_nil (default: false)
11
+ # Use allow_nil: true if you'd like the finder to return nil instead of
12
+ # raising ActivRecord::RecordNotFound
13
+ #
14
+ # ### Example
15
+ #
16
+ # MyModel.friendly.find("bad-slug")
17
+ # #=> raise ActiveRecord::RecordNotFound
18
+ #
19
+ # MyModel.friendly.find("bad-slug", allow_nil: true)
20
+ # #=> nil
21
+ #
12
22
  # Since FriendlyId 5.0, if the id is a nonnumeric string like '123-foo' it
13
23
  # will *only* search by friendly id and not fall back to the regular find
14
24
  # method.
15
25
  #
16
26
  # If you want to search only by the friendly id, use {#find_by_friendly_id}.
17
27
  # @raise ActiveRecord::RecordNotFound
18
- def find(*args)
28
+ def find(*args, allow_nil: false)
19
29
  id = args.first
20
- return super if args.count != 1 || id.unfriendly_id?
21
- first_by_friendly_id(id).tap {|result| return result unless result.nil?}
22
- return super if potential_primary_key?(id)
23
- raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
30
+ return super(*args) if args.count != 1 || id.unfriendly_id?
31
+ first_by_friendly_id(id).tap { |result| return result unless result.nil? }
32
+ return super(*args) if potential_primary_key?(id)
33
+
34
+ raise_not_found_exception(id) unless allow_nil
35
+ rescue ActiveRecord::RecordNotFound => exception
36
+ raise exception unless allow_nil
24
37
  end
25
38
 
26
39
  # Returns true if a record with the given id exists.
27
40
  def exists?(conditions = :none)
28
- return super unless conditions.friendly_id?
29
- exists_by_friendly_id?(conditions)
41
+ return super if conditions.unfriendly_id?
42
+ return true if exists_by_friendly_id?(conditions)
43
+ super
30
44
  end
31
45
 
32
46
  # Finds exclusively by the friendly id, completely bypassing original
33
47
  # `find`.
34
48
  # @raise ActiveRecord::RecordNotFound
35
49
  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}"
50
+ first_by_friendly_id(id) or raise_not_found_exception(id)
37
51
  end
38
52
 
39
53
  def exists_by_friendly_id?(id)
40
- where(friendly_id_config.query_field => id).exists?
54
+ where(friendly_id_config.query_field => parse_friendly_id(id)).exists?
41
55
  end
42
56
 
43
57
  private
@@ -48,7 +62,11 @@ module FriendlyId
48
62
  key_type = key_type.type if key_type.respond_to?(:type)
49
63
  case key_type
50
64
  when :integer
51
- Integer(id, 10) rescue false
65
+ begin
66
+ Integer(id, 10)
67
+ rescue
68
+ false
69
+ end
52
70
  when :uuid
53
71
  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
72
  else
@@ -57,8 +75,49 @@ module FriendlyId
57
75
  end
58
76
 
59
77
  def first_by_friendly_id(id)
60
- find_by(friendly_id_config.query_field => id)
78
+ find_by(friendly_id_config.query_field => parse_friendly_id(id))
61
79
  end
62
80
 
81
+ # Parse the given value to make it suitable for use as a slug according to
82
+ # your application's rules.
83
+ #
84
+ # This method is not intended to be invoked directly; FriendlyId uses it
85
+ # internally to process a slug into string to use as a finder.
86
+ #
87
+ # However, if FriendlyId's default slug parsing doesn't suit your needs,
88
+ # you can override this method in your model class to control exactly how
89
+ # slugs are generated.
90
+ #
91
+ # ### Example
92
+ #
93
+ # class Person < ActiveRecord::Base
94
+ # extend FriendlyId
95
+ # friendly_id :name_and_location
96
+ #
97
+ # def name_and_location
98
+ # "#{name} from #{location}"
99
+ # end
100
+ #
101
+ # # Use default slug, but lower case
102
+ # # If `id` is "Jane-Doe" or "JANE-DOE", this finds data by "jane-doe"
103
+ # def parse_friendly_id(slug)
104
+ # super.downcase
105
+ # end
106
+ # end
107
+ #
108
+ # @param [#to_s] value The slug to be parsed.
109
+ # @return The parsed slug, which is not modified by default.
110
+ def parse_friendly_id(value)
111
+ value
112
+ end
113
+
114
+ def raise_not_found_exception(id)
115
+ message = "can't find record with friendly id: #{id.inspect}"
116
+ if ActiveRecord.version < Gem::Version.create("5.0")
117
+ raise ActiveRecord::RecordNotFound.new(message)
118
+ else
119
+ raise ActiveRecord::RecordNotFound.new(message, name, friendly_id_config.query_field, id)
120
+ end
121
+ end
63
122
  end
64
123
  end