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,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