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,108 +1,104 @@
1
1
  require "friendly_id/slugged"
2
2
 
3
3
  module FriendlyId
4
-
5
- =begin
6
-
7
- ## Unique Slugs by Scope
8
-
9
- The {FriendlyId::Scoped} module allows FriendlyId to generate unique slugs
10
- within a scope.
11
-
12
- This allows, for example, two restaurants in different cities to have the slug
13
- `joes-diner`:
14
-
15
- class Restaurant < ActiveRecord::Base
16
- extend FriendlyId
17
- belongs_to :city
18
- friendly_id :name, :use => :scoped, :scope => :city
19
- end
20
-
21
- class City < ActiveRecord::Base
22
- extend FriendlyId
23
- has_many :restaurants
24
- friendly_id :name, :use => :slugged
25
- end
26
-
27
- City.friendly.find("seattle").restaurants.friendly.find("joes-diner")
28
- City.friendly.find("chicago").restaurants.friendly.find("joes-diner")
29
-
30
- Without :scoped in this case, one of the restaurants would have the slug
31
- `joes-diner` and the other would have `joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5`.
32
-
33
- The value for the `:scope` option can be the name of a `belongs_to` relation, or
34
- a column.
35
-
36
- Additionally, the `:scope` option can receive an array of scope values:
37
-
38
- class Cuisine < ActiveRecord::Base
39
- extend FriendlyId
40
- has_many :restaurants
41
- friendly_id :name, :use => :slugged
42
- end
43
-
44
- class City < ActiveRecord::Base
45
- extend FriendlyId
46
- has_many :restaurants
47
- friendly_id :name, :use => :slugged
48
- end
49
-
50
- class Restaurant < ActiveRecord::Base
51
- extend FriendlyId
52
- belongs_to :city
53
- friendly_id :name, :use => :scoped, :scope => [:city, :cuisine]
54
- end
55
-
56
- All supplied values will be used to determine scope.
57
-
58
- ### Finding Records by Friendly ID
59
-
60
- If you are using scopes your friendly ids may not be unique, so a simple find
61
- like:
62
-
63
- Restaurant.friendly.find("joes-diner")
64
-
65
- may return the wrong record. In these cases it's best to query through the
66
- relation:
67
-
68
- @city.restaurants.friendly.find("joes-diner")
69
-
70
- Alternatively, you could pass the scope value as a query parameter:
71
-
72
- Restaurant.where(:city_id => @city.id).friendly.find("joes-diner")
73
-
74
-
75
- ### Finding All Records That Match a Scoped ID
76
-
77
- Query the slug column directly:
78
-
79
- Restaurant.where(:slug => "joes-diner")
80
-
81
- ### Routes for Scoped Models
82
-
83
- Recall that FriendlyId is a database-centric library, and does not set up any
84
- routes for scoped models. You must do this yourself in your application. Here's
85
- an example of one way to set this up:
86
-
87
- # in routes.rb
88
- resources :cities do
89
- resources :restaurants
90
- end
91
-
92
- # in views
93
- <%= link_to 'Show', [@city, @restaurant] %>
94
-
95
- # in controllers
96
- @city = City.friendly.find(params[:city_id])
97
- @restaurant = @city.restaurants.friendly.find(params[:id])
98
-
99
- # URLs:
100
- http://example.org/cities/seattle/restaurants/joes-diner
101
- http://example.org/cities/chicago/restaurants/joes-diner
102
-
103
- =end
4
+ #
5
+ ## Unique Slugs by Scope
6
+ #
7
+ # The {FriendlyId::Scoped} module allows FriendlyId to generate unique slugs
8
+ # within a scope.
9
+ #
10
+ # This allows, for example, two restaurants in different cities to have the slug
11
+ # `joes-diner`:
12
+ #
13
+ # class Restaurant < ActiveRecord::Base
14
+ # extend FriendlyId
15
+ # belongs_to :city
16
+ # friendly_id :name, :use => :scoped, :scope => :city
17
+ # end
18
+ #
19
+ # class City < ActiveRecord::Base
20
+ # extend FriendlyId
21
+ # has_many :restaurants
22
+ # friendly_id :name, :use => :slugged
23
+ # end
24
+ #
25
+ # City.friendly.find("seattle").restaurants.friendly.find("joes-diner")
26
+ # City.friendly.find("chicago").restaurants.friendly.find("joes-diner")
27
+ #
28
+ # Without :scoped in this case, one of the restaurants would have the slug
29
+ # `joes-diner` and the other would have `joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5`.
30
+ #
31
+ # The value for the `:scope` option can be the name of a `belongs_to` relation, or
32
+ # a column.
33
+ #
34
+ # Additionally, the `:scope` option can receive an array of scope values:
35
+ #
36
+ # class Cuisine < ActiveRecord::Base
37
+ # extend FriendlyId
38
+ # has_many :restaurants
39
+ # friendly_id :name, :use => :slugged
40
+ # end
41
+ #
42
+ # class City < ActiveRecord::Base
43
+ # extend FriendlyId
44
+ # has_many :restaurants
45
+ # friendly_id :name, :use => :slugged
46
+ # end
47
+ #
48
+ # class Restaurant < ActiveRecord::Base
49
+ # extend FriendlyId
50
+ # belongs_to :city
51
+ # friendly_id :name, :use => :scoped, :scope => [:city, :cuisine]
52
+ # end
53
+ #
54
+ # All supplied values will be used to determine scope.
55
+ #
56
+ ### Finding Records by Friendly ID
57
+ #
58
+ # If you are using scopes your friendly ids may not be unique, so a simple find
59
+ # like:
60
+ #
61
+ # Restaurant.friendly.find("joes-diner")
62
+ #
63
+ # may return the wrong record. In these cases it's best to query through the
64
+ # relation:
65
+ #
66
+ # @city.restaurants.friendly.find("joes-diner")
67
+ #
68
+ # Alternatively, you could pass the scope value as a query parameter:
69
+ #
70
+ # Restaurant.where(:city_id => @city.id).friendly.find("joes-diner")
71
+ #
72
+ #
73
+ ### Finding All Records That Match a Scoped ID
74
+ #
75
+ # Query the slug column directly:
76
+ #
77
+ # Restaurant.where(:slug => "joes-diner")
78
+ #
79
+ ### Routes for Scoped Models
80
+ #
81
+ # Recall that FriendlyId is a database-centric library, and does not set up any
82
+ # routes for scoped models. You must do this yourself in your application. Here's
83
+ # an example of one way to set this up:
84
+ #
85
+ # # in routes.rb
86
+ # resources :cities do
87
+ # resources :restaurants
88
+ # end
89
+ #
90
+ # # in views
91
+ # <%= link_to 'Show', [@city, @restaurant] %>
92
+ #
93
+ # # in controllers
94
+ # @city = City.friendly.find(params[:city_id])
95
+ # @restaurant = @city.restaurants.friendly.find(params[:id])
96
+ #
97
+ # # URLs:
98
+ # http://example.org/cities/seattle/restaurants/joes-diner
99
+ # http://example.org/cities/chicago/restaurants/joes-diner
100
+ #
104
101
  module Scoped
105
-
106
102
  # FriendlyId::Config.use will invoke this method when present, to allow
107
103
  # loading dependent modules prior to overriding them when necessary.
108
104
  def self.setup(model_class)
@@ -122,7 +118,10 @@ an example of one way to set this up:
122
118
  end
123
119
 
124
120
  def scope_for_slug_generator
125
- relation = self.class.unscoped.friendly
121
+ if friendly_id_config.uses?(:History)
122
+ return super
123
+ end
124
+ relation = self.class.base_class.unscoped.friendly
126
125
  friendly_id_config.scope_columns.each do |column|
127
126
  relation = relation.where(column => send(column))
128
127
  end
@@ -136,10 +135,13 @@ an example of one way to set this up:
136
135
  end
137
136
  private :slug_generator
138
137
 
138
+ def should_generate_new_friendly_id?
139
+ (changed & friendly_id_config.scope_columns).any? || super
140
+ end
141
+
139
142
  # This module adds the `:scope` configuration option to
140
143
  # {FriendlyId::Configuration FriendlyId::Configuration}.
141
144
  module Configuration
142
-
143
145
  # Gets the scope value.
144
146
  #
145
147
  # When setting this value, the argument should be a symbol referencing a
@@ -0,0 +1,69 @@
1
+ module FriendlyId
2
+ module SequentiallySlugged
3
+ class Calculator
4
+ attr_accessor :scope, :slug, :slug_column, :sequence_separator
5
+
6
+ def initialize(scope, slug, slug_column, sequence_separator, base_class)
7
+ @scope = scope
8
+ @slug = slug
9
+ table_name = scope.connection.quote_table_name(base_class.arel_table.name)
10
+ @slug_column = "#{table_name}.#{scope.connection.quote_column_name(slug_column)}"
11
+ @sequence_separator = sequence_separator
12
+ end
13
+
14
+ def next_slug
15
+ slug + sequence_separator + next_sequence_number.to_s
16
+ end
17
+
18
+ private
19
+
20
+ def conflict_query
21
+ base = "#{slug_column} = ? OR #{slug_column} LIKE ?"
22
+ # Awful hack for SQLite3, which does not pick up '\' as the escape character
23
+ # without this.
24
+ base << " ESCAPE '\\'" if /sqlite/i.match?(scope.connection.adapter_name)
25
+ base
26
+ end
27
+
28
+ def next_sequence_number
29
+ last_sequence_number ? last_sequence_number + 1 : 2
30
+ end
31
+
32
+ def last_sequence_number
33
+ # Reject slug_conflicts that doesn't come from the first_candidate
34
+ # Map all sequence numbers and take the maximum
35
+ slug_conflicts
36
+ .reject { |slug_conflict| !regexp.match(slug_conflict) }
37
+ .map { |slug_conflict| regexp.match(slug_conflict)[1].to_i }
38
+ .max
39
+ end
40
+
41
+ # Return the unnumbered (shortest) slug first, followed by the numbered ones
42
+ # in ascending order.
43
+ def ordering_query
44
+ "#{sql_length}(#{slug_column}) ASC, #{slug_column} ASC"
45
+ end
46
+
47
+ def regexp
48
+ /#{slug}#{sequence_separator}(\d+)\z/
49
+ end
50
+
51
+ def sequential_slug_matcher
52
+ # Underscores (matching a single character) and percent signs (matching
53
+ # any number of characters) need to be escaped. While this looks like
54
+ # an excessive number of backslashes, it is correct.
55
+ "#{slug}#{sequence_separator}".gsub(/[_%]/, '\\\\\&') + "%"
56
+ end
57
+
58
+ def slug_conflicts
59
+ scope
60
+ .where(conflict_query, slug, sequential_slug_matcher)
61
+ .order(Arel.sql(ordering_query)).pluck(Arel.sql(slug_column))
62
+ end
63
+
64
+ def sql_length
65
+ /sqlserver/i.match?(scope.connection.adapter_name) ? "LEN" : "LENGTH"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,5 @@
1
+ require_relative "sequentially_slugged/calculator"
2
+
1
3
  module FriendlyId
2
4
  module SequentiallySlugged
3
5
  def self.setup(model_class)
@@ -7,70 +9,31 @@ module FriendlyId
7
9
  def resolve_friendly_id_conflict(candidate_slugs)
8
10
  candidate = candidate_slugs.first
9
11
  return if candidate.nil?
10
- SequentialSlugCalculator.new(scope_for_slug_generator,
11
- candidate,
12
- friendly_id_config.slug_column,
13
- friendly_id_config.sequence_separator,
14
- self.class.base_class).next_slug
15
- end
16
-
17
- class SequentialSlugCalculator
18
- attr_accessor :scope, :slug, :slug_column, :sequence_separator
19
-
20
- def initialize(scope, slug, slug_column, sequence_separator, base_class)
21
- @scope = scope
22
- @slug = slug
23
- table_name = scope.connection.quote_table_name(base_class.arel_table.name)
24
- @slug_column = "#{table_name}.#{scope.connection.quote_column_name(slug_column)}"
25
- @sequence_separator = sequence_separator
26
- end
27
12
 
28
- def next_slug
29
- slug + sequence_separator + next_sequence_number.to_s
30
- end
13
+ Calculator.new(
14
+ scope_for_slug_generator,
15
+ candidate,
16
+ slug_column,
17
+ friendly_id_config.sequence_separator,
18
+ slug_base_class
19
+ ).next_slug
20
+ end
31
21
 
32
22
  private
33
23
 
34
- def next_sequence_number
35
- last_sequence_number ? last_sequence_number + 1 : 2
36
- end
37
-
38
- def last_sequence_number
39
- regexp = /#{slug}#{sequence_separator}(\d+)\z/
40
- # Reject slug_conflicts that doesn't come from the first_candidate
41
- # Map all sequence numbers and take the maximum
42
- slug_conflicts.reject{ |slug_conflict| !regexp.match(slug_conflict) }.map do |slug_conflict|
43
- regexp.match(slug_conflict)[1].to_i
44
- end.max
45
- end
46
-
47
- def slug_conflicts
48
- scope.
49
- where(conflict_query, slug, sequential_slug_matcher).
50
- order(Arel.sql(ordering_query)).pluck(Arel.sql(slug_column))
51
- end
52
-
53
- def conflict_query
54
- base = "#{slug_column} = ? OR #{slug_column} LIKE ?"
55
- # Awful hack for SQLite3, which does not pick up '\' as the escape character
56
- # without this.
57
- base << " ESCAPE '\\'" if scope.connection.adapter_name =~ /sqlite/i
58
- base
59
- end
60
-
61
- def sequential_slug_matcher
62
- # Underscores (matching a single character) and percent signs (matching
63
- # any number of characters) need to be escaped. While this looks like
64
- # an excessive number of backslashes, it is correct.
65
- "#{slug}#{sequence_separator}".gsub(/[_%]/, '\\\\\&') + '%'
24
+ def slug_base_class
25
+ if friendly_id_config.uses?(:history)
26
+ Slug
27
+ else
28
+ self.class.base_class
66
29
  end
30
+ end
67
31
 
68
- # Return the unnumbered (shortest) slug first, followed by the numbered ones
69
- # in ascending order.
70
- def ordering_query
71
- length_command = "LENGTH"
72
- length_command = "LEN" if scope.connection.adapter_name =~ /sqlserver/i
73
- "#{length_command}(#{slug_column}) ASC, #{slug_column} ASC"
32
+ def slug_column
33
+ if friendly_id_config.uses?(:history)
34
+ :slug
35
+ else
36
+ friendly_id_config.slug_column
74
37
  end
75
38
  end
76
39
  end
@@ -1,75 +1,75 @@
1
1
  require "i18n"
2
2
 
3
3
  module FriendlyId
4
-
5
- =begin
6
-
7
- ## Translating Slugs Using Simple I18n
8
-
9
- The {FriendlyId::SimpleI18n SimpleI18n} module adds very basic i18n support to
10
- FriendlyId.
11
-
12
- In order to use this module, your model must have a slug column for each locale.
13
- By default FriendlyId looks for columns named, for example, "slug_en",
14
- "slug_es", etc. The first part of the name can be configured by passing the
15
- `:slug_column` option if you choose. Note that the column for the default locale
16
- must also include the locale in its name.
17
-
18
- This module is most suitable to applications that need to support few locales.
19
- If you need to support two or more locales, you may wish to use the
20
- friendly_id_globalize gem instead.
21
-
22
- ### Example migration
23
-
24
- def self.up
25
- create_table :posts do |t|
26
- t.string :title
27
- t.string :slug_en
28
- t.string :slug_es
29
- t.text :body
30
- end
31
- add_index :posts, :slug_en
32
- add_index :posts, :slug_es
33
- end
34
-
35
- ### Finds
36
-
37
- Finds will take into consideration the current locale:
38
-
39
- I18n.locale = :es
40
- Post.friendly.find("la-guerra-de-las-galaxias")
41
- I18n.locale = :en
42
- Post.friendly.find("star-wars")
43
-
44
- To find a slug by an explicit locale, perform the find inside a block
45
- passed to I18n's `with_locale` method:
46
-
47
- I18n.with_locale(:es) do
48
- Post.friendly.find("la-guerra-de-las-galaxias")
49
- end
50
-
51
- ### Creating Records
52
-
53
- When new records are created, the slug is generated for the current locale only.
54
-
55
- ### Translating Slugs
56
-
57
- To translate an existing record's friendly_id, use
58
- {FriendlyId::SimpleI18n::Model#set_friendly_id}. This will ensure that the slug
59
- you add is properly escaped, transliterated and sequenced:
60
-
61
- post = Post.create :name => "Star Wars"
62
- post.set_friendly_id("La guerra de las galaxias", :es)
63
-
64
- If you don't pass in a locale argument, FriendlyId::SimpleI18n will just use the
65
- current locale:
66
-
67
- I18n.with_locale(:es) do
68
- post.set_friendly_id("La guerra de las galaxias")
69
- end
70
- =end
4
+ #
5
+ ## Translating Slugs Using Simple I18n
6
+ #
7
+ # The {FriendlyId::SimpleI18n SimpleI18n} module adds very basic i18n support to
8
+ # FriendlyId.
9
+ #
10
+ # In order to use this module, your model must have a slug column for each locale.
11
+ # By default FriendlyId looks for columns named, for example, "slug_en",
12
+ # "slug_es", "slug_pt_br", etc. The first part of the name can be configured by
13
+ # passing the `:slug_column` option if you choose. Note that the column for the
14
+ # default locale must also include the locale in its name.
15
+ #
16
+ # This module is most suitable to applications that need to support few locales.
17
+ # If you need to support two or more locales, you may wish to use the
18
+ # friendly_id_globalize gem instead.
19
+ #
20
+ ### Example migration
21
+ #
22
+ # def self.up
23
+ # create_table :posts do |t|
24
+ # t.string :title
25
+ # t.string :slug_en
26
+ # t.string :slug_es
27
+ # t.string :slug_pt_br
28
+ # t.text :body
29
+ # end
30
+ # add_index :posts, :slug_en
31
+ # add_index :posts, :slug_es
32
+ # add_index :posts, :slug_pt_br
33
+ # end
34
+ #
35
+ ### Finds
36
+ #
37
+ # Finds will take into consideration the current locale:
38
+ #
39
+ # I18n.locale = :es
40
+ # Post.friendly.find("la-guerra-de-las-galaxias")
41
+ # I18n.locale = :en
42
+ # Post.friendly.find("star-wars")
43
+ # I18n.locale = :"pt-BR"
44
+ # Post.friendly.find("guerra-das-estrelas")
45
+ #
46
+ # To find a slug by an explicit locale, perform the find inside a block
47
+ # passed to I18n's `with_locale` method:
48
+ #
49
+ # I18n.with_locale(:es) do
50
+ # Post.friendly.find("la-guerra-de-las-galaxias")
51
+ # end
52
+ #
53
+ ### Creating Records
54
+ #
55
+ # When new records are created, the slug is generated for the current locale only.
56
+ #
57
+ ### Translating Slugs
58
+ #
59
+ # To translate an existing record's friendly_id, use
60
+ # {FriendlyId::SimpleI18n::Model#set_friendly_id}. This will ensure that the slug
61
+ # you add is properly escaped, transliterated and sequenced:
62
+ #
63
+ # post = Post.create :name => "Star Wars"
64
+ # post.set_friendly_id("La guerra de las galaxias", :es)
65
+ #
66
+ # If you don't pass in a locale argument, FriendlyId::SimpleI18n will just use the
67
+ # current locale:
68
+ #
69
+ # I18n.with_locale(:es) do
70
+ # post.set_friendly_id("La guerra de las galaxias")
71
+ # end
71
72
  module SimpleI18n
72
-
73
73
  # FriendlyId::Config.use will invoke this method when present, to allow
74
74
  # loading dependent modules prior to overriding them when necessary.
75
75
  def self.setup(model_class)
@@ -98,7 +98,13 @@ current locale:
98
98
 
99
99
  module Configuration
100
100
  def slug_column
101
- "#{super}_#{I18n.locale}"
101
+ "#{super}_#{locale_suffix}"
102
+ end
103
+
104
+ private
105
+
106
+ def locale_suffix
107
+ I18n.locale.to_s.underscore
102
108
  end
103
109
  end
104
110
  end
@@ -3,7 +3,7 @@ module FriendlyId
3
3
  #
4
4
  # @see FriendlyId::History
5
5
  class Slug < ActiveRecord::Base
6
- belongs_to :sluggable, :polymorphic => true
6
+ belongs_to :sluggable, polymorphic: true
7
7
 
8
8
  def sluggable
9
9
  sluggable_type.constantize.unscoped { super }
@@ -12,6 +12,5 @@ module FriendlyId
12
12
  def to_param
13
13
  slug
14
14
  end
15
-
16
15
  end
17
16
  end
@@ -2,7 +2,6 @@ module FriendlyId
2
2
  # The default slug generator offers functionality to check slug candidates for
3
3
  # availability.
4
4
  class SlugGenerator
5
-
6
5
  def initialize(scope, config)
7
6
  @scope = scope
8
7
  @config = config
@@ -17,9 +16,8 @@ module FriendlyId
17
16
  end
18
17
 
19
18
  def generate(candidates)
20
- candidates.each {|c| return c if available?(c)}
19
+ candidates.each { |c| return c if available?(c) }
21
20
  nil
22
21
  end
23
-
24
22
  end
25
23
  end