friendly_id 5.4.0 → 5.5.1

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