friendly_id 5.2.4 → 5.5.0

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