friendly_id 4.1.0.beta.1 → 5.0.0.alpha.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.
- checksums.yaml +7 -0
- data/.travis.yml +7 -12
- data/Changelog.md +4 -0
- data/Gemfile +2 -2
- data/Guide.rdoc +34 -63
- data/README.md +116 -60
- data/Rakefile +5 -1
- data/WhatsNew.md +2 -94
- data/friendly_id.gemspec +10 -20
- data/gemfiles/Gemfile.rails-4.0.rb +19 -0
- data/lib/friendly_id.rb +11 -6
- data/lib/friendly_id/.gitattributes +1 -0
- data/lib/friendly_id/base.rb +6 -65
- data/lib/friendly_id/candidates.rb +41 -0
- data/lib/friendly_id/configuration.rb +10 -6
- data/lib/friendly_id/history.rb +22 -60
- data/lib/friendly_id/object_utils.rb +1 -1
- data/lib/friendly_id/scoped.rb +40 -34
- data/lib/friendly_id/scopes.rb +28 -0
- data/lib/friendly_id/simple_i18n.rb +13 -3
- data/lib/friendly_id/slug.rb +0 -1
- data/lib/friendly_id/slug_generator.rb +9 -68
- data/lib/friendly_id/slugged.rb +22 -34
- data/lib/friendly_id/version.rb +3 -0
- data/lib/generators/friendly_id_generator.rb +5 -4
- data/test/helper.rb +1 -1
- data/test/history_test.rb +63 -56
- data/test/object_utils_test.rb +0 -1
- data/test/schema.rb +1 -10
- data/test/shared.rb +27 -23
- data/test/simple_i18n_test.rb +3 -2
- data/test/slugged_test.rb +17 -48
- data/test/sti_test.rb +2 -2
- metadata +40 -103
- data/gemfiles/Gemfile.rails-3.0.rb +0 -21
- data/gemfiles/Gemfile.rails-3.1.rb +0 -22
- data/gemfiles/Gemfile.rails-3.2.rb +0 -22
- data/lib/friendly_id/finder_methods.rb +0 -35
- data/lib/friendly_id/globalize.rb +0 -115
- data/test/globalize_test.rb +0 -57
@@ -8,7 +8,7 @@ module FriendlyId
|
|
8
8
|
# be sufficient to avoid conflicts with other libraries.
|
9
9
|
module ObjectUtils
|
10
10
|
|
11
|
-
# True
|
11
|
+
# True if the id is definitely friendly, false if definitely unfriendly,
|
12
12
|
# else nil.
|
13
13
|
#
|
14
14
|
# An object is considired "definitely unfriendly" if its class is or
|
data/lib/friendly_id/scoped.rb
CHANGED
@@ -28,11 +28,33 @@ This allows, for example, two restaurants in different cities to have the slug
|
|
28
28
|
City.find("chicago").restaurants.find("joes-diner")
|
29
29
|
|
30
30
|
Without :scoped in this case, one of the restaurants would have the slug
|
31
|
-
+joes-diner+ and the other would have +joes-diner
|
31
|
+
+joes-diner+ and the other would have +joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5+.
|
32
32
|
|
33
33
|
The value for the +:scope+ option can be the name of a +belongs_to+ relation, or
|
34
34
|
a column.
|
35
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
|
+
|
36
58
|
=== Finding Records by Friendly ID
|
37
59
|
|
38
60
|
If you are using scopes your friendly ids may not be unique, so a simple find
|
@@ -81,14 +103,17 @@ an example of one way to set this up:
|
|
81
103
|
=end
|
82
104
|
module Scoped
|
83
105
|
|
106
|
+
# FriendlyId::Config.use will invoke this method when present, to allow
|
107
|
+
# loading dependent modules prior to overriding them when necessary.
|
108
|
+
def self.setup(model_class)
|
109
|
+
model_class.friendly_id_config.use :slugged
|
110
|
+
end
|
84
111
|
|
85
112
|
# Sets up behavior and configuration options for FriendlyId's scoped slugs
|
86
113
|
# feature.
|
87
114
|
def self.included(model_class)
|
88
|
-
model_class.
|
89
|
-
include Slugged unless self < Slugged
|
115
|
+
model_class.class_eval do
|
90
116
|
friendly_id_config.class.send :include, Configuration
|
91
|
-
friendly_id_config.slug_generator_class.send :include, SlugGenerator
|
92
117
|
end
|
93
118
|
end
|
94
119
|
|
@@ -96,6 +121,15 @@ an example of one way to set this up:
|
|
96
121
|
friendly_id_config.scope_columns.sort.map { |column| "#{column}:#{send(column)}" }.join(",")
|
97
122
|
end
|
98
123
|
|
124
|
+
def slug_generator
|
125
|
+
relation = self.class.unscoped.friendly
|
126
|
+
friendly_id_config.scope_columns.each do |column|
|
127
|
+
relation = relation.where(column => send(column))
|
128
|
+
end
|
129
|
+
friendly_id_config.slug_generator_class.new(relation)
|
130
|
+
end
|
131
|
+
private :slug_generator
|
132
|
+
|
99
133
|
# This module adds the +:scope+ configuration option to
|
100
134
|
# {FriendlyId::Configuration FriendlyId::Configuration}.
|
101
135
|
module Configuration
|
@@ -122,36 +156,8 @@ an example of one way to set this up:
|
|
122
156
|
|
123
157
|
private
|
124
158
|
|
125
|
-
|
126
|
-
|
127
|
-
model_class.reflections[scope].try(:primary_key_name)
|
128
|
-
end
|
129
|
-
else
|
130
|
-
def reflection_foreign_key(scope)
|
131
|
-
model_class.reflections[scope].try(:foreign_key)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# This module overrides {FriendlyId::SlugGenerator#conflict} to consider
|
137
|
-
# scope, to avoid adding sequences to slugs under different scopes.
|
138
|
-
module SlugGenerator
|
139
|
-
|
140
|
-
private
|
141
|
-
|
142
|
-
def conflict
|
143
|
-
if friendly_id_config.uses?(:history)
|
144
|
-
# When using the :history module +conflicts+ already returns only real conflicts, so there's no need to check
|
145
|
-
# for the scope columns again
|
146
|
-
conflicts.first
|
147
|
-
else
|
148
|
-
columns = friendly_id_config.scope_columns
|
149
|
-
matched = columns.inject(conflicts) do |memo, column|
|
150
|
-
memo.where(column => sluggable.send(column))
|
151
|
-
end
|
152
|
-
|
153
|
-
matched.first
|
154
|
-
end
|
159
|
+
def reflection_foreign_key(scope)
|
160
|
+
model_class.reflections[scope].try(:foreign_key)
|
155
161
|
end
|
156
162
|
end
|
157
163
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
module Scopes
|
3
|
+
|
4
|
+
def self.extended(model_class)
|
5
|
+
model_class.scope :friendly, ->{model_class.all} do
|
6
|
+
def find(*args)
|
7
|
+
id = args.first
|
8
|
+
return super if args.count != 1 || id.unfriendly_id?
|
9
|
+
find_by_friendly_id(id) or super
|
10
|
+
end
|
11
|
+
|
12
|
+
def exists?(conditions = :none)
|
13
|
+
return super if conditions.unfriendly_id? || conditions.unfriendly_id?.nil?
|
14
|
+
exists_by_friendly_id?(conditions)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_by_friendly_id(id)
|
20
|
+
where(friendly_id_config.query_field => id).first
|
21
|
+
end
|
22
|
+
|
23
|
+
def exists_by_friendly_id?(id)
|
24
|
+
where(friendly_id_config.query_field => id).exists?
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -17,7 +17,7 @@ must also include the locale in its name.
|
|
17
17
|
|
18
18
|
This module is most suitable to applications that need to support few locales.
|
19
19
|
If you need to support two or more locales, you may wish to use the
|
20
|
-
|
20
|
+
friendly_id_globalize gem instead.
|
21
21
|
|
22
22
|
=== Example migration
|
23
23
|
|
@@ -70,9 +70,14 @@ current locale:
|
|
70
70
|
=end
|
71
71
|
module SimpleI18n
|
72
72
|
|
73
|
+
# FriendlyId::Config.use will invoke this method when present, to allow
|
74
|
+
# loading dependent modules prior to overriding them when necessary.
|
75
|
+
def self.setup(model_class)
|
76
|
+
model_class.friendly_id_config.use :slugged
|
77
|
+
end
|
78
|
+
|
73
79
|
def self.included(model_class)
|
74
|
-
model_class.
|
75
|
-
friendly_id_config.use :slugged
|
80
|
+
model_class.class_eval do
|
76
81
|
friendly_id_config.class.send :include, Configuration
|
77
82
|
include Model
|
78
83
|
end
|
@@ -84,6 +89,11 @@ current locale:
|
|
84
89
|
set_slug(normalize_friendly_id(text))
|
85
90
|
end
|
86
91
|
end
|
92
|
+
|
93
|
+
def slug=(value)
|
94
|
+
super
|
95
|
+
write_attribute friendly_id_config.slug_column, value
|
96
|
+
end
|
87
97
|
end
|
88
98
|
|
89
99
|
module Configuration
|
data/lib/friendly_id/slug.rb
CHANGED
@@ -2,82 +2,23 @@ module FriendlyId
|
|
2
2
|
# The default slug generator offers functionality to check slug strings for
|
3
3
|
# uniqueness and, if necessary, appends a sequence to guarantee it.
|
4
4
|
class SlugGenerator
|
5
|
-
attr_reader :sluggable, :normalized
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
@sluggable = sluggable
|
10
|
-
@normalized = normalized
|
6
|
+
def initialize(scope)
|
7
|
+
@scope = scope
|
11
8
|
end
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
"#{normalized}#{separator}#{next_in_sequence}"
|
10
|
+
def available?(slug)
|
11
|
+
!@scope.exists?(slug)
|
16
12
|
end
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
conflict? ? self.next : normalized
|
14
|
+
def add(slug)
|
15
|
+
slug
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
last_in_sequence == 0 ? 2 : last_in_sequence.next
|
27
|
-
end
|
28
|
-
|
29
|
-
def last_in_sequence
|
30
|
-
@_last_in_sequence ||= extract_sequence_from_slug(conflict.to_param)
|
31
|
-
end
|
32
|
-
|
33
|
-
def extract_sequence_from_slug(slug)
|
34
|
-
slug.split("#{normalized}#{separator}").last.to_i
|
18
|
+
def generate(candidates)
|
19
|
+
candidates.each {|c| return add c if available?(c)}
|
20
|
+
nil
|
35
21
|
end
|
36
22
|
|
37
|
-
def column
|
38
|
-
sluggable.connection.quote_column_name friendly_id_config.slug_column
|
39
|
-
end
|
40
|
-
|
41
|
-
def conflict?
|
42
|
-
!! conflict
|
43
|
-
end
|
44
|
-
|
45
|
-
def conflict
|
46
|
-
unless defined? @conflict
|
47
|
-
@conflict = conflicts.first
|
48
|
-
end
|
49
|
-
@conflict
|
50
|
-
end
|
51
|
-
|
52
|
-
def conflicts
|
53
|
-
sluggable_class = friendly_id_config.model_class.base_class
|
54
|
-
|
55
|
-
pkey = sluggable_class.primary_key
|
56
|
-
value = sluggable.send pkey
|
57
|
-
base = "#{column} = ? OR #{column} LIKE ?"
|
58
|
-
# Awful hack for SQLite3, which does not pick up '\' as the escape character without this.
|
59
|
-
base << "ESCAPE '\\'" if sluggable.connection.adapter_name =~ /sqlite/i
|
60
|
-
scope = sluggable_class.unscoped.where(base, normalized, wildcard)
|
61
|
-
scope = scope.where("#{pkey} <> ?", value) unless sluggable.new_record?
|
62
|
-
|
63
|
-
length_command = "LENGTH"
|
64
|
-
length_command = "LEN" if sluggable.connection.adapter_name =~ /sqlserver/i
|
65
|
-
scope = scope.order("#{length_command}(#{column}) DESC, #{column} DESC")
|
66
|
-
end
|
67
|
-
|
68
|
-
def friendly_id_config
|
69
|
-
sluggable.friendly_id_config
|
70
|
-
end
|
71
|
-
|
72
|
-
def separator
|
73
|
-
friendly_id_config.sequence_separator
|
74
|
-
end
|
75
|
-
|
76
|
-
def wildcard
|
77
|
-
# Underscores (matching a single character) and percent signs (matching
|
78
|
-
# any number of characters) need to be escaped
|
79
|
-
# (While this seems like an excessive number of backslashes, it is correct)
|
80
|
-
"#{normalized}#{separator}".gsub(/[_%]/, '\\\\\&') + '%'
|
81
|
-
end
|
82
23
|
end
|
83
24
|
end
|
data/lib/friendly_id/slugged.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "friendly_id/slug_generator"
|
3
|
+
require "friendly_id/candidates"
|
3
4
|
|
4
5
|
module FriendlyId
|
5
6
|
=begin
|
@@ -82,11 +83,11 @@ FriendlyId will append a sequence to the generated slug to ensure uniqueness:
|
|
82
83
|
car2 = Car.create :title => "Peugot 206"
|
83
84
|
|
84
85
|
car.friendly_id #=> "peugot-206"
|
85
|
-
car2.friendly_id #=> "peugot-206
|
86
|
+
car2.friendly_id #=> "peugot-206-f9f3789a-daec-4156-af1d-fab81aa16ee5"
|
86
87
|
|
87
88
|
==== Sequence Separator - The Two Dashes
|
88
89
|
|
89
|
-
By default, FriendlyId uses
|
90
|
+
By default, FriendlyId uses a dash to separate the slug from a sequence.
|
90
91
|
|
91
92
|
You can change this with the {FriendlyId::Slugged::Configuration#sequence_separator
|
92
93
|
sequence_separator} configuration option.
|
@@ -185,11 +186,11 @@ second, in concurrent code, either in threads or multiple processes.
|
|
185
186
|
|
186
187
|
To solve the nested attributes issue, I recommend simply avoiding them when
|
187
188
|
creating more than one nested record for a model that uses FriendlyId. See {this
|
188
|
-
Github issue}[https://github.com/
|
189
|
+
Github issue}[https://github.com/FriendlyId/friendly_id/issues/185] for discussion.
|
189
190
|
|
190
191
|
To solve the concurrency issue, I recommend locking the model's table against
|
191
192
|
inserts while when saving the record. See {this Github
|
192
|
-
issue}[https://github.com/
|
193
|
+
issue}[https://github.com/FriendlyId/friendly_id/issues/180] for discussion.
|
193
194
|
|
194
195
|
=end
|
195
196
|
module Slugged
|
@@ -199,9 +200,9 @@ issue}[https://github.com/norman/friendly_id/issues/180] for discussion.
|
|
199
200
|
def self.included(model_class)
|
200
201
|
model_class.friendly_id_config.instance_eval do
|
201
202
|
self.class.send :include, Configuration
|
202
|
-
self.slug_generator_class ||=
|
203
|
+
self.slug_generator_class ||= SlugGenerator
|
203
204
|
defaults[:slug_column] ||= 'slug'
|
204
|
-
defaults[:sequence_separator] ||= '
|
205
|
+
defaults[:sequence_separator] ||= '-'
|
205
206
|
end
|
206
207
|
model_class.before_validation :set_slug
|
207
208
|
end
|
@@ -250,34 +251,28 @@ issue}[https://github.com/norman/friendly_id/issues/180] for discussion.
|
|
250
251
|
# You can override this method in your model if, for example, you only want
|
251
252
|
# slugs to be generated once, and then never updated.
|
252
253
|
def should_generate_new_friendly_id?
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
return false if base.nil? && slug_value.nil?
|
259
|
-
# Otherwise, if this is a new record, we're definitely going to try to
|
260
|
-
# create a new slug.
|
261
|
-
return true if new_record?
|
262
|
-
slug_base = normalize_friendly_id(base)
|
263
|
-
separator = Regexp.escape friendly_id_config.sequence_separator
|
264
|
-
# If the slug base (with and without sequence) is different from either the current
|
265
|
-
# friendly id or the slug value, then we'll generate a new friendly_id.
|
266
|
-
compare = (current_friendly_id || slug_value)
|
267
|
-
slug_base != compare && slug_base != compare.try(:sub, /#{separator}[\d]*\z/, '')
|
254
|
+
send(friendly_id_config.slug_column).nil? && !send(friendly_id_config.base).nil?
|
255
|
+
end
|
256
|
+
|
257
|
+
def resolve_friendly_id_conflict(candidates)
|
258
|
+
candidates.first + friendly_id_config.sequence_separator + SecureRandom.uuid
|
268
259
|
end
|
269
260
|
|
270
261
|
# Sets the slug.
|
271
|
-
# FIXME: This method sucks and the logic is pretty dubious.
|
272
262
|
def set_slug(normalized_slug = nil)
|
273
|
-
if
|
274
|
-
normalized_slug
|
275
|
-
|
276
|
-
send "#{friendly_id_config.slug_column}=",
|
263
|
+
if should_generate_new_friendly_id?
|
264
|
+
candidates = FriendlyId::Candidates.new(self, normalized_slug || send(friendly_id_config.base))
|
265
|
+
slug = slug_generator.generate(candidates) || resolve_friendly_id_conflict(candidates)
|
266
|
+
send "#{friendly_id_config.slug_column}=", slug
|
277
267
|
end
|
278
268
|
end
|
279
269
|
private :set_slug
|
280
270
|
|
271
|
+
def slug_generator
|
272
|
+
friendly_id_config.slug_generator_class.new(self.class.base_class.unscoped.friendly)
|
273
|
+
end
|
274
|
+
private :slug_generator
|
275
|
+
|
281
276
|
# This module adds the +:slug_column+, and +:sequence_separator+, and
|
282
277
|
# +:slug_generator_class+ configuration options to
|
283
278
|
# {FriendlyId::Configuration FriendlyId::Configuration}.
|
@@ -293,17 +288,10 @@ issue}[https://github.com/norman/friendly_id/issues/180] for discussion.
|
|
293
288
|
|
294
289
|
# The string used to separate a slug base from a numeric sequence.
|
295
290
|
#
|
296
|
-
# By default, +--+ is used to separate the slug from the sequence.
|
297
|
-
# FriendlyId uses two dashes to distinguish sequences from slugs with
|
298
|
-
# numbers in their name.
|
299
|
-
#
|
300
291
|
# You can change the default separator by setting the
|
301
292
|
# {FriendlyId::Slugged::Configuration#sequence_separator
|
302
293
|
# sequence_separator} configuration option.
|
303
|
-
#
|
304
|
-
# For obvious reasons, you should avoid setting it to "+-+" unless you're
|
305
|
-
# sure you will never want to have a friendly id with a number in it.
|
306
|
-
# @return String The sequence separator string. Defaults to "+--+".
|
294
|
+
# @return String The sequence separator string. Defaults to "+-+".
|
307
295
|
def sequence_separator
|
308
296
|
@sequence_separator or defaults[:sequence_separator]
|
309
297
|
end
|
@@ -3,14 +3,15 @@ require "rails/generators/active_record"
|
|
3
3
|
|
4
4
|
# This generator adds a migration for the {FriendlyId::History
|
5
5
|
# FriendlyId::History} addon.
|
6
|
-
class FriendlyIdGenerator <
|
7
|
-
|
8
|
-
|
6
|
+
class FriendlyIdGenerator < ActiveRecord::Generators::Base
|
7
|
+
# ActiveRecord::Generators::Base inherits from Rails::Generators::NamedBase which requires a NAME parameter for the
|
8
|
+
# new table name. Our generator always uses 'friendly_id_slugs', so we just set a random name here.
|
9
|
+
argument :name, type: :string, default: 'random_name'
|
9
10
|
|
10
11
|
source_root File.expand_path('../../friendly_id', __FILE__)
|
11
12
|
|
12
13
|
# Copies the migration template to db/migrate.
|
13
|
-
def copy_files
|
14
|
+
def copy_files
|
14
15
|
migration_template 'migration.rb', 'db/migrate/create_friendly_id_slugs.rb'
|
15
16
|
end
|
16
17
|
|
data/test/helper.rb
CHANGED
data/test/history_test.rb
CHANGED
@@ -2,7 +2,7 @@ require "helper"
|
|
2
2
|
|
3
3
|
class Manual < ActiveRecord::Base
|
4
4
|
extend FriendlyId
|
5
|
-
friendly_id :name, :use => :history
|
5
|
+
friendly_id :name, :use => [:slugged, :history]
|
6
6
|
end
|
7
7
|
|
8
8
|
class HistoryTest < MiniTest::Unit::TestCase
|
@@ -29,6 +29,7 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
29
29
|
test "should create new slug record when friendly_id changes" do
|
30
30
|
with_instance_of(model_class) do |record|
|
31
31
|
record.name = record.name + "b"
|
32
|
+
record.slug = nil
|
32
33
|
record.save!
|
33
34
|
assert_equal 2, FriendlyId::Slug.count
|
34
35
|
end
|
@@ -38,10 +39,11 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
38
39
|
with_instance_of(model_class) do |record|
|
39
40
|
old_friendly_id = record.friendly_id
|
40
41
|
record.name = record.name + "b"
|
42
|
+
record.slug = nil
|
41
43
|
record.save!
|
42
44
|
begin
|
43
|
-
assert model_class.find(old_friendly_id)
|
44
|
-
assert model_class.exists?(old_friendly_id), "should exist? by old id"
|
45
|
+
assert model_class.friendly.find(old_friendly_id)
|
46
|
+
assert model_class.friendly.exists?(old_friendly_id), "should exist? by old id"
|
45
47
|
rescue ActiveRecord::RecordNotFound
|
46
48
|
flunk "Could not find record by old id"
|
47
49
|
end
|
@@ -52,8 +54,9 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
52
54
|
transaction do
|
53
55
|
record = model_class.create! :name => "hello"
|
54
56
|
assert_equal 1, FriendlyId::Slug.count
|
55
|
-
record = model_class.find("hello")
|
57
|
+
record = model_class.friendly.find("hello")
|
56
58
|
record.name = "hello again"
|
59
|
+
record.slug = nil
|
57
60
|
record.save!
|
58
61
|
assert_equal 2, FriendlyId::Slug.count
|
59
62
|
end
|
@@ -64,17 +67,7 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
64
67
|
old_friendly_id = record.friendly_id
|
65
68
|
record.name = record.name + "b"
|
66
69
|
record.save!
|
67
|
-
assert !model_class.find(old_friendly_id).readonly?
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
test "should create correct sequence numbers even when some conflicted slugs have changed" do
|
72
|
-
transaction do
|
73
|
-
record1 = model_class.create! :name => 'hello'
|
74
|
-
record2 = model_class.create! :name => 'hello!'
|
75
|
-
record2.update_attributes :name => 'goodbye'
|
76
|
-
record3 = model_class.create! :name => 'hello!'
|
77
|
-
assert_equal 'hello--3', record3.slug
|
70
|
+
assert !model_class.friendly.find(old_friendly_id).readonly?
|
78
71
|
end
|
79
72
|
end
|
80
73
|
|
@@ -96,16 +89,7 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
96
89
|
first_record.save!
|
97
90
|
second_record = model_class.create! :name => "foo"
|
98
91
|
assert second_record.slug != "foo"
|
99
|
-
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
test 'should increment the sequence by one for each historic slug' do
|
104
|
-
transaction do
|
105
|
-
previous_record = model_class.create! :name => "foo"
|
106
|
-
first_record = model_class.create! :name => 'another'
|
107
|
-
second_record = model_class.create! :name => 'another'
|
108
|
-
assert second_record.slug == "another--2"
|
92
|
+
assert_match(/foo-.+/, second_record.slug)
|
109
93
|
end
|
110
94
|
end
|
111
95
|
|
@@ -114,13 +98,28 @@ class HistoryTest < MiniTest::Unit::TestCase
|
|
114
98
|
first_record = model_class.create! :name => "foo"
|
115
99
|
second_record = model_class.create! :name => 'another'
|
116
100
|
|
117
|
-
second_record.update_attributes :name => 'foo'
|
118
|
-
|
119
|
-
|
120
|
-
|
101
|
+
second_record.update_attributes :name => 'foo', :slug => nil
|
102
|
+
assert_match(/foo-.*/, second_record.slug)
|
103
|
+
|
104
|
+
first_record.update_attributes :name => 'another', :slug => nil
|
105
|
+
assert_match(/another-.*/, first_record.slug)
|
121
106
|
end
|
122
107
|
end
|
123
108
|
|
109
|
+
test 'should name table according to prefix and suffix' do
|
110
|
+
transaction do
|
111
|
+
begin
|
112
|
+
prefix = "prefix_"
|
113
|
+
without_prefix = FriendlyId::Slug.table_name
|
114
|
+
ActiveRecord::Base.table_name_prefix = prefix
|
115
|
+
FriendlyId::Slug.reset_table_name
|
116
|
+
assert_equal prefix + without_prefix, FriendlyId::Slug.table_name
|
117
|
+
ensure
|
118
|
+
ActiveRecord::Base.table_name_prefix = ""
|
119
|
+
FriendlyId::Slug.table_name = without_prefix
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
124
123
|
end
|
125
124
|
|
126
125
|
class HistoryTestWithSti < HistoryTest
|
@@ -137,8 +136,6 @@ class HistoryTestWithSti < HistoryTest
|
|
137
136
|
end
|
138
137
|
end
|
139
138
|
|
140
|
-
|
141
|
-
|
142
139
|
class City < ActiveRecord::Base
|
143
140
|
has_many :restaurants
|
144
141
|
end
|
@@ -158,45 +155,55 @@ class ScopedHistoryTest < MiniTest::Unit::TestCase
|
|
158
155
|
end
|
159
156
|
|
160
157
|
test "should find old scoped slugs" do
|
161
|
-
|
162
|
-
|
163
|
-
|
158
|
+
transaction do
|
159
|
+
city = City.create!
|
160
|
+
with_instance_of(Restaurant) do |record|
|
161
|
+
record.city = city
|
164
162
|
|
165
|
-
|
166
|
-
|
163
|
+
record.name = "x"
|
164
|
+
record.slug = nil
|
165
|
+
record.save!
|
167
166
|
|
168
|
-
|
169
|
-
|
167
|
+
record.name = "y"
|
168
|
+
record.slug = nil
|
169
|
+
record.save!
|
170
170
|
|
171
|
-
|
171
|
+
assert_equal city.restaurants.friendly.find("x"), city.restaurants.friendly.find("y")
|
172
|
+
end
|
172
173
|
end
|
173
174
|
end
|
174
175
|
|
175
176
|
test "should consider old scoped slugs when creating slugs" do
|
176
|
-
|
177
|
-
|
178
|
-
|
177
|
+
transaction do
|
178
|
+
city = City.create!
|
179
|
+
with_instance_of(Restaurant) do |record|
|
180
|
+
record.city = city
|
179
181
|
|
180
|
-
|
181
|
-
|
182
|
+
record.name = "x"
|
183
|
+
record.slug = nil
|
184
|
+
record.save!
|
182
185
|
|
183
|
-
|
184
|
-
|
186
|
+
record.name = "y"
|
187
|
+
record.slug = nil
|
188
|
+
record.save!
|
185
189
|
|
186
|
-
|
187
|
-
|
190
|
+
second_record = model_class.create! :city => city, :name => 'x'
|
191
|
+
assert_match(/x-.+/, second_record.friendly_id)
|
188
192
|
|
189
|
-
|
190
|
-
|
193
|
+
third_record = model_class.create! :city => city, :name => 'y'
|
194
|
+
assert_match(/y-.+/, third_record.friendly_id)
|
195
|
+
end
|
191
196
|
end
|
192
197
|
end
|
193
198
|
|
194
199
|
test "should allow equal slugs in different scopes" do
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
200
|
+
transaction do
|
201
|
+
city = City.create!
|
202
|
+
second_city = City.create!
|
203
|
+
record = model_class.create! :city => city, :name => 'x'
|
204
|
+
second_record = model_class.create! :city => second_city, :name => 'x'
|
199
205
|
|
200
|
-
|
206
|
+
assert_equal record.slug, second_record.slug
|
207
|
+
end
|
201
208
|
end
|
202
|
-
end
|
209
|
+
end
|