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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/stale.yml +17 -0
- data/.github/workflows/test.yml +58 -0
- data/Changelog.md +41 -0
- data/Gemfile +10 -11
- data/README.md +42 -15
- data/Rakefile +24 -27
- data/bench.rb +30 -27
- data/certs/parndt.pem +27 -0
- data/friendly_id.gemspec +28 -27
- data/gemfiles/Gemfile.rails-5.2.rb +11 -16
- data/gemfiles/Gemfile.rails-6.0.rb +22 -0
- data/gemfiles/Gemfile.rails-6.1.rb +22 -0
- data/gemfiles/Gemfile.rails-7.0.rb +22 -0
- data/guide.rb +5 -5
- data/lib/friendly_id/base.rb +61 -68
- data/lib/friendly_id/candidates.rb +9 -11
- data/lib/friendly_id/configuration.rb +8 -8
- data/lib/friendly_id/finder_methods.rb +72 -13
- data/lib/friendly_id/finders.rb +64 -67
- data/lib/friendly_id/history.rb +72 -66
- data/lib/friendly_id/initializer.rb +5 -5
- data/lib/friendly_id/migration.rb +10 -11
- data/lib/friendly_id/object_utils.rb +2 -2
- data/lib/friendly_id/reserved.rb +28 -32
- data/lib/friendly_id/scoped.rb +105 -103
- data/lib/friendly_id/sequentially_slugged/calculator.rb +69 -0
- data/lib/friendly_id/sequentially_slugged.rb +21 -58
- data/lib/friendly_id/simple_i18n.rb +75 -69
- data/lib/friendly_id/slug.rb +1 -2
- data/lib/friendly_id/slug_generator.rb +1 -3
- data/lib/friendly_id/slugged.rb +236 -239
- data/lib/friendly_id/version.rb +1 -1
- data/lib/friendly_id.rb +41 -45
- data/lib/generators/friendly_id_generator.rb +9 -9
- data/test/base_test.rb +10 -13
- data/test/benchmarks/finders.rb +28 -26
- data/test/benchmarks/object_utils.rb +13 -13
- data/test/candidates_test.rb +17 -18
- data/test/configuration_test.rb +7 -11
- data/test/core_test.rb +1 -2
- data/test/databases.yml +7 -4
- data/test/finders_test.rb +52 -5
- data/test/generator_test.rb +16 -26
- data/test/helper.rb +33 -20
- data/test/history_test.rb +116 -72
- data/test/numeric_slug_test.rb +31 -0
- data/test/object_utils_test.rb +0 -2
- data/test/reserved_test.rb +9 -11
- data/test/schema.rb +5 -4
- data/test/scoped_test.rb +26 -15
- data/test/sequentially_slugged_test.rb +107 -33
- data/test/shared.rb +17 -18
- data/test/simple_i18n_test.rb +23 -13
- data/test/slugged_test.rb +254 -78
- data/test/sti_test.rb +19 -21
- data.tar.gz.sig +0 -0
- metadata +49 -19
- metadata.gz.sig +1 -0
- data/.travis.yml +0 -57
- data/gemfiles/Gemfile.rails-4.0.rb +0 -30
- data/gemfiles/Gemfile.rails-4.1.rb +0 -29
- data/gemfiles/Gemfile.rails-4.2.rb +0 -28
- data/gemfiles/Gemfile.rails-5.0.rb +0 -28
- data/gemfiles/Gemfile.rails-5.1.rb +0 -27
data/lib/friendly_id/scoped.rb
CHANGED
@@ -1,108 +1,104 @@
|
|
1
1
|
require "friendly_id/slugged"
|
2
2
|
|
3
3
|
module FriendlyId
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
`
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
### Finds
|
36
|
-
|
37
|
-
Finds will take into consideration the current locale:
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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}_#{
|
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
|
data/lib/friendly_id/slug.rb
CHANGED
@@ -3,7 +3,7 @@ module FriendlyId
|
|
3
3
|
#
|
4
4
|
# @see FriendlyId::History
|
5
5
|
class Slug < ActiveRecord::Base
|
6
|
-
belongs_to :sluggable, :
|
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
|