friendly_id 5.4.0 → 5.5.1

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 (65) hide show
  1. checksums.yaml +4 -4
  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 +1 -1
  6. data/.github/workflows/test.yml +38 -36
  7. data/.yardopts +2 -0
  8. data/Changelog.md +19 -0
  9. data/Gemfile +9 -13
  10. data/README.md +31 -8
  11. data/Rakefile +24 -27
  12. data/bench.rb +30 -27
  13. data/certs/parndt.pem +25 -23
  14. data/friendly_id.gemspec +26 -29
  15. data/gemfiles/Gemfile.rails-5.2.rb +11 -16
  16. data/gemfiles/Gemfile.rails-6.0.rb +11 -16
  17. data/gemfiles/Gemfile.rails-6.1.rb +22 -0
  18. data/gemfiles/Gemfile.rails-7.0.rb +22 -0
  19. data/guide.rb +13 -6
  20. data/lib/friendly_id/base.rb +59 -60
  21. data/lib/friendly_id/candidates.rb +9 -11
  22. data/lib/friendly_id/configuration.rb +6 -7
  23. data/lib/friendly_id/finder_methods.rb +63 -15
  24. data/lib/friendly_id/finders.rb +66 -66
  25. data/lib/friendly_id/history.rb +62 -63
  26. data/lib/friendly_id/initializer.rb +4 -4
  27. data/lib/friendly_id/migration.rb +6 -6
  28. data/lib/friendly_id/object_utils.rb +2 -2
  29. data/lib/friendly_id/reserved.rb +30 -32
  30. data/lib/friendly_id/scoped.rb +99 -102
  31. data/lib/friendly_id/sequentially_slugged/calculator.rb +69 -0
  32. data/lib/friendly_id/sequentially_slugged.rb +17 -64
  33. data/lib/friendly_id/simple_i18n.rb +78 -69
  34. data/lib/friendly_id/slug.rb +1 -2
  35. data/lib/friendly_id/slug_generator.rb +1 -3
  36. data/lib/friendly_id/slugged.rb +238 -239
  37. data/lib/friendly_id/version.rb +1 -1
  38. data/lib/friendly_id.rb +47 -49
  39. data/lib/generators/friendly_id_generator.rb +9 -9
  40. data/test/base_test.rb +10 -13
  41. data/test/benchmarks/finders.rb +28 -26
  42. data/test/benchmarks/object_utils.rb +13 -13
  43. data/test/candidates_test.rb +17 -18
  44. data/test/configuration_test.rb +7 -11
  45. data/test/core_test.rb +1 -2
  46. data/test/databases.yml +4 -3
  47. data/test/finders_test.rb +36 -13
  48. data/test/generator_test.rb +16 -26
  49. data/test/helper.rb +31 -24
  50. data/test/history_test.rb +70 -74
  51. data/test/numeric_slug_test.rb +4 -4
  52. data/test/object_utils_test.rb +0 -2
  53. data/test/reserved_test.rb +9 -11
  54. data/test/schema.rb +5 -4
  55. data/test/scoped_test.rb +18 -20
  56. data/test/sequentially_slugged_test.rb +65 -50
  57. data/test/shared.rb +15 -16
  58. data/test/simple_i18n_test.rb +22 -12
  59. data/test/slugged_test.rb +125 -113
  60. data/test/sti_test.rb +19 -21
  61. data.tar.gz.sig +0 -0
  62. metadata +38 -34
  63. metadata.gz.sig +0 -0
  64. data/gemfiles/Gemfile.rails-5.0.rb +0 -28
  65. data/gemfiles/Gemfile.rails-5.1.rb +0 -27
@@ -1,73 +1,73 @@
1
1
  module FriendlyId
2
- =begin
3
- ## Performing Finds with FriendlyId
4
-
5
- FriendlyId offers enhanced finders which will search for your record by
6
- friendly id, and fall back to the numeric id if necessary. This makes it easy
7
- to add FriendlyId to an existing application with minimal code modification.
8
-
9
- By default, these methods are available only on the `friendly` scope:
10
-
11
- Restaurant.friendly.find('plaza-diner') #=> works
12
- Restaurant.friendly.find(23) #=> also works
13
- Restaurant.find(23) #=> still works
14
- Restaurant.find('plaza-diner') #=> will not work
15
-
16
- ### Restoring FriendlyId 4.0-style finders
17
-
18
- Prior to version 5.0, FriendlyId overrode the default finder methods to perform
19
- friendly finds all the time. This required modifying parts of Rails that did
20
- not have a public API, which was harder to maintain and at times caused
21
- compatiblity problems. In 5.0 we decided to change the library's defaults and add
22
- the friendly finder methods only to the `friendly` scope in order to boost
23
- compatiblity. However, you can still opt-in to original functionality very
24
- easily by using the `:finders` addon:
25
-
26
- class Restaurant < ActiveRecord::Base
27
- extend FriendlyId
28
-
29
- scope :active, -> {where(:active => true)}
30
-
31
- friendly_id :name, :use => [:slugged, :finders]
32
- end
33
-
34
- Restaurant.friendly.find('plaza-diner') #=> works
35
- Restaurant.find('plaza-diner') #=> now also works
36
- Restaurant.active.find('plaza-diner') #=> now also works
37
-
38
- ### Updating your application to use FriendlyId's finders
39
-
40
- Unless you've chosen to use the `:finders` addon, be sure to modify the finders
41
- in your controllers to use the `friendly` scope. For example:
42
-
43
- # before
44
- def set_restaurant
45
- @restaurant = Restaurant.find(params[:id])
46
- end
47
-
48
- # after
49
- def set_restaurant
50
- @restaurant = Restaurant.friendly.find(params[:id])
51
- end
52
-
53
- #### Active Admin
54
-
55
- Unless you use the `:finders` addon, you should modify your admin controllers
56
- for models that use FriendlyId with something similar to the following:
57
-
58
- controller do
59
- def find_resource
60
- scoped_collection.friendly.find(params[:id])
61
- end
62
- end
63
-
64
- =end
2
+ # @guide begin
3
+ #
4
+ # ## Performing Finds with FriendlyId
5
+ #
6
+ # FriendlyId offers enhanced finders which will search for your record by
7
+ # friendly id, and fall back to the numeric id if necessary. This makes it easy
8
+ # to add FriendlyId to an existing application with minimal code modification.
9
+ #
10
+ # By default, these methods are available only on the `friendly` scope:
11
+ #
12
+ # Restaurant.friendly.find('plaza-diner') #=> works
13
+ # Restaurant.friendly.find(23) #=> also works
14
+ # Restaurant.find(23) #=> still works
15
+ # Restaurant.find('plaza-diner') #=> will not work
16
+ #
17
+ # ### Restoring FriendlyId 4.0-style finders
18
+ #
19
+ # Prior to version 5.0, FriendlyId overrode the default finder methods to perform
20
+ # friendly finds all the time. This required modifying parts of Rails that did
21
+ # not have a public API, which was harder to maintain and at times caused
22
+ # compatiblity problems. In 5.0 we decided to change the library's defaults and add
23
+ # the friendly finder methods only to the `friendly` scope in order to boost
24
+ # compatiblity. However, you can still opt-in to original functionality very
25
+ # easily by using the `:finders` addon:
26
+ #
27
+ # class Restaurant < ActiveRecord::Base
28
+ # extend FriendlyId
29
+ #
30
+ # scope :active, -> {where(:active => true)}
31
+ #
32
+ # friendly_id :name, :use => [:slugged, :finders]
33
+ # end
34
+ #
35
+ # Restaurant.friendly.find('plaza-diner') #=> works
36
+ # Restaurant.find('plaza-diner') #=> now also works
37
+ # Restaurant.active.find('plaza-diner') #=> now also works
38
+ #
39
+ # ### Updating your application to use FriendlyId's finders
40
+ #
41
+ # Unless you've chosen to use the `:finders` addon, be sure to modify the finders
42
+ # in your controllers to use the `friendly` scope. For example:
43
+ #
44
+ # # before
45
+ # def set_restaurant
46
+ # @restaurant = Restaurant.find(params[:id])
47
+ # end
48
+ #
49
+ # # after
50
+ # def set_restaurant
51
+ # @restaurant = Restaurant.friendly.find(params[:id])
52
+ # end
53
+ #
54
+ # #### Active Admin
55
+ #
56
+ # Unless you use the `:finders` addon, you should modify your admin controllers
57
+ # for models that use FriendlyId with something similar to the following:
58
+ #
59
+ # controller do
60
+ # def find_resource
61
+ # scoped_collection.friendly.find(params[:id])
62
+ # end
63
+ # end
64
+ #
65
+ # @guide end
65
66
  module Finders
66
-
67
67
  module ClassMethods
68
68
  if (ActiveRecord::VERSION::MAJOR == 4) && (ActiveRecord::VERSION::MINOR == 0)
69
69
  def relation_delegate_class(klass)
70
- relation_class_name = :"#{klass.to_s.gsub('::', '_')}_#{self.to_s.gsub('::', '_')}"
70
+ relation_class_name = :"#{klass.to_s.gsub("::", "_")}_#{to_s.gsub("::", "_")}"
71
71
  klass.const_get(relation_class_name)
72
72
  end
73
73
  end
@@ -82,7 +82,7 @@ for models that use FriendlyId with something similar to the following:
82
82
  end
83
83
 
84
84
  # Support for friendly finds on associations for Rails 4.0.1 and above.
85
- if ::ActiveRecord.const_defined?('AssociationRelation')
85
+ if ::ActiveRecord.const_defined?("AssociationRelation")
86
86
  model_class.extend(ClassMethods)
87
87
  association_relation_delegate_class = model_class.relation_delegate_class(::ActiveRecord::AssociationRelation)
88
88
  association_relation_delegate_class.send(:include, model_class.friendly_id_config.finder_methods)
@@ -1,59 +1,58 @@
1
1
  module FriendlyId
2
-
3
- =begin
4
-
5
- ## History: Avoiding 404's When Slugs Change
6
-
7
- FriendlyId's {FriendlyId::History History} module adds the ability to store a
8
- log of a model's slugs, so that when its friendly id changes, it's still
9
- possible to perform finds by the old id.
10
-
11
- The primary use case for this is avoiding broken URLs.
12
-
13
- ### Setup
14
-
15
- In order to use this module, you must add a table to your database schema to
16
- store the slug records. FriendlyId provides a generator for this purpose:
17
-
18
- rails generate friendly_id
19
- rake db:migrate
20
-
21
- This will add a table named `friendly_id_slugs`, used by the {FriendlyId::Slug}
22
- model.
23
-
24
- ### Considerations
25
-
26
- Because recording slug history requires creating additional database records,
27
- this module has an impact on the performance of the associated model's `create`
28
- method.
29
-
30
- ### Example
31
-
32
- class Post < ActiveRecord::Base
33
- extend FriendlyId
34
- friendly_id :title, :use => :history
35
- end
36
-
37
- class PostsController < ApplicationController
38
-
39
- before_filter :find_post
40
-
41
- ...
42
-
43
- def find_post
44
- @post = Post.friendly.find params[:id]
45
-
46
- # If an old id or a numeric id was used to find the record, then
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
- return redirect_to @post, :status => :moved_permanently
51
- end
52
- end
53
- end
54
- =end
2
+ # @guide begin
3
+ #
4
+ # ## History: Avoiding 404's When Slugs Change
5
+ #
6
+ # FriendlyId's {FriendlyId::History History} module adds the ability to store a
7
+ # log of a model's slugs, so that when its friendly id changes, it's still
8
+ # possible to perform finds by the old id.
9
+ #
10
+ # The primary use case for this is avoiding broken URLs.
11
+ #
12
+ # ### Setup
13
+ #
14
+ # In order to use this module, you must add a table to your database schema to
15
+ # store the slug records. FriendlyId provides a generator for this purpose:
16
+ #
17
+ # rails generate friendly_id
18
+ # rake db:migrate
19
+ #
20
+ # This will add a table named `friendly_id_slugs`, used by the {FriendlyId::Slug}
21
+ # model.
22
+ #
23
+ # ### Considerations
24
+ #
25
+ # Because recording slug history requires creating additional database records,
26
+ # this module has an impact on the performance of the associated model's `create`
27
+ # method.
28
+ #
29
+ # ### Example
30
+ #
31
+ # class Post < ActiveRecord::Base
32
+ # extend FriendlyId
33
+ # friendly_id :title, :use => :history
34
+ # end
35
+ #
36
+ # class PostsController < ApplicationController
37
+ #
38
+ # before_filter :find_post
39
+ #
40
+ # ...
41
+ #
42
+ # def find_post
43
+ # @post = Post.friendly.find params[:id]
44
+ #
45
+ # # If an old id or a numeric id was used to find the record, then
46
+ # # the request slug will not match the current slug, and we should do
47
+ # # a 301 redirect to the new path
48
+ # if params[:id] != @post.slug
49
+ # return redirect_to @post, :status => :moved_permanently
50
+ # end
51
+ # end
52
+ # end
53
+ #
54
+ # @guide end
55
55
  module History
56
-
57
56
  module Configuration
58
57
  def dependent_value
59
58
  dependent.nil? ? :destroy : dependent
@@ -72,10 +71,10 @@ method.
72
71
  # Configures the model instance to use the History add-on.
73
72
  def self.included(model_class)
74
73
  model_class.class_eval do
75
- has_many :slugs, -> {order(id: :desc)}, **{
76
- :as => :sluggable,
77
- :dependent => @friendly_id_config.dependent_value,
78
- :class_name => Slug.to_s
74
+ has_many :slugs, -> { order(id: :desc) }, **{
75
+ as: :sluggable,
76
+ dependent: @friendly_id_config.dependent_value,
77
+ class_name: Slug.to_s
79
78
  }
80
79
 
81
80
  after_save :create_slug
@@ -96,7 +95,7 @@ method.
96
95
  end
97
96
 
98
97
  def slug_table_record(id)
99
- select(quoted_table_name + '.*').joins(:slugs).where(slug_history_clause(id)).order(Slug.arel_table[:id].desc).first
98
+ select(quoted_table_name + ".*").joins(:slugs).where(slug_history_clause(id)).order(Slug.arel_table[:id].desc).first
100
99
  end
101
100
 
102
101
  def slug_history_clause(id)
@@ -112,7 +111,7 @@ method.
112
111
  def scope_for_slug_generator
113
112
  relation = super.joins(:slugs)
114
113
  unless new_record?
115
- relation = relation.merge(Slug.where('sluggable_id <> ?', id))
114
+ relation = relation.merge(Slug.where("sluggable_id <> ?", id))
116
115
  end
117
116
  if friendly_id_config.uses?(:scoped)
118
117
  relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
@@ -124,9 +123,9 @@ method.
124
123
  return unless friendly_id
125
124
  return if history_is_up_to_date?
126
125
  # Allow reversion back to a previously used slug
127
- relation = slugs.where(:slug => friendly_id)
126
+ relation = slugs.where(slug: friendly_id)
128
127
  if friendly_id_config.uses?(:scoped)
129
- relation = relation.where(:scope => serialized_scope)
128
+ relation = relation.where(scope: serialized_scope)
130
129
  end
131
130
  relation.destroy_all unless relation.empty?
132
131
  slugs.create! do |record|
@@ -139,7 +138,7 @@ method.
139
138
  latest_history = slugs.first
140
139
  check = latest_history.try(:slug) == friendly_id
141
140
  if friendly_id_config.uses?(:scoped)
142
- check = check && latest_history.scope == serialized_scope
141
+ check &&= latest_history.scope == serialized_scope
143
142
  end
144
143
  check
145
144
  end
@@ -16,9 +16,9 @@ FriendlyId.defaults do |config|
16
16
  # undesirable to allow as slugs. Edit this list as needed for your app.
17
17
  config.use :reserved
18
18
 
19
- config.reserved_words = %w(new edit index session login logout users admin
20
- stylesheets assets javascripts images)
21
-
19
+ config.reserved_words = %w[new edit index session login logout users admin
20
+ stylesheets assets javascripts images]
21
+
22
22
  # This adds an option to treat reserved words as conflicts rather than exceptions.
23
23
  # When there is no good candidate, a UUID will be appended, matching the existing
24
24
  # conflict behavior.
@@ -37,7 +37,7 @@ FriendlyId.defaults do |config|
37
37
  # MyModel.find('foo')
38
38
  #
39
39
  # This is significantly more convenient but may not be appropriate for
40
- # all applications, so you must explicity opt-in to this behavior. You can
40
+ # all applications, so you must explicitly opt-in to this behavior. You can
41
41
  # always also configure it on a per-model basis if you prefer.
42
42
  #
43
43
  # Something else to consider is that using the :finders addon boosts
@@ -8,14 +8,14 @@ MIGRATION_CLASS =
8
8
  class CreateFriendlyIdSlugs < MIGRATION_CLASS
9
9
  def change
10
10
  create_table :friendly_id_slugs do |t|
11
- t.string :slug, :null => false
12
- t.integer :sluggable_id, :null => false
13
- t.string :sluggable_type, :limit => 50
14
- t.string :scope
11
+ t.string :slug, null: false
12
+ t.integer :sluggable_id, null: false
13
+ t.string :sluggable_type, limit: 50
14
+ t.string :scope
15
15
  t.datetime :created_at
16
16
  end
17
17
  add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
18
- add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 }
19
- add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
18
+ add_index :friendly_id_slugs, [:slug, :sluggable_type], length: {slug: 140, sluggable_type: 50}
19
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: {slug: 70, sluggable_type: 50, scope: 70}, unique: true
20
20
  end
21
21
  end
@@ -19,7 +19,6 @@ module FriendlyId
19
19
  # names that unambigously refer to the library of their origin, which should
20
20
  # be sufficient to avoid conflicts with other libraries.
21
21
  module ObjectUtils
22
-
23
22
  # True if the id is definitely friendly, false if definitely unfriendly,
24
23
  # else nil.
25
24
  #
@@ -45,7 +44,8 @@ module FriendlyId
45
44
  # True if the id is definitely unfriendly, false if definitely friendly,
46
45
  # else nil.
47
46
  def unfriendly_id?
48
- val = friendly_id? ; !val unless val.nil?
47
+ val = friendly_id?
48
+ !val unless val.nil?
49
49
  end
50
50
  end
51
51
 
@@ -1,42 +1,40 @@
1
1
  module FriendlyId
2
-
3
- =begin
4
-
5
- ## Reserved Words
6
-
7
- The {FriendlyId::Reserved Reserved} module adds the ability to exclude a list of
8
- words from use as FriendlyId slugs.
9
-
10
- With Ruby on Rails, FriendlyId's generator generates an initializer that
11
- reserves some words such as "new" and "edit" using {FriendlyId.defaults
12
- FriendlyId.defaults}.
13
-
14
- Note that the error messages for fields will appear on the field
15
- `:friendly_id`. If you are using Rails's scaffolded form errors display, then
16
- it will have no field to highlight. If you'd like to change this so that
17
- scaffolding works as expected, one way to accomplish this is to move the error
18
- message to a different field. For example:
19
-
20
- class Person < ActiveRecord::Base
21
- extend FriendlyId
22
- friendly_id :name, use: :slugged
23
-
24
- after_validation :move_friendly_id_error_to_name
25
-
26
- def move_friendly_id_error_to_name
27
- errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
28
- end
29
- end
30
-
31
- =end
2
+ # @guide begin
3
+ #
4
+ # ## Reserved Words
5
+ #
6
+ # The {FriendlyId::Reserved Reserved} module adds the ability to exclude a list of
7
+ # words from use as FriendlyId slugs.
8
+ #
9
+ # With Ruby on Rails, FriendlyId's generator generates an initializer that
10
+ # reserves some words such as "new" and "edit" using {FriendlyId.defaults
11
+ # FriendlyId.defaults}.
12
+ #
13
+ # Note that the error messages for fields will appear on the field
14
+ # `:friendly_id`. If you are using Rails's scaffolded form errors display, then
15
+ # it will have no field to highlight. If you'd like to change this so that
16
+ # scaffolding works as expected, one way to accomplish this is to move the error
17
+ # message to a different field. For example:
18
+ #
19
+ # class Person < ActiveRecord::Base
20
+ # extend FriendlyId
21
+ # friendly_id :name, use: :slugged
22
+ #
23
+ # after_validation :move_friendly_id_error_to_name
24
+ #
25
+ # def move_friendly_id_error_to_name
26
+ # errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
27
+ # end
28
+ # end
29
+ #
30
+ # @guide end
32
31
  module Reserved
33
-
34
32
  # When included, this module adds configuration options to the model class's
35
33
  # friendly_id_config.
36
34
  def self.included(model_class)
37
35
  model_class.class_eval do
38
36
  friendly_id_config.class.send :include, Reserved::Configuration
39
- validates_exclusion_of :friendly_id, :in => ->(_) {
37
+ validates_exclusion_of :friendly_id, in: ->(_) {
40
38
  friendly_id_config.reserved_words || []
41
39
  }
42
40
  end