friendly_id 5.1.0 → 5.2.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -8
- data/Changelog.md +9 -1
- data/README.md +9 -1
- data/Rakefile +1 -1
- data/gemfiles/Gemfile.rails-4.0.rb +7 -3
- data/gemfiles/Gemfile.rails-4.1.rb +5 -5
- data/gemfiles/Gemfile.rails-4.2.rb +5 -9
- data/lib/friendly_id.rb +8 -7
- data/lib/friendly_id/base.rb +2 -1
- data/lib/friendly_id/candidates.rb +3 -0
- data/lib/friendly_id/history.rb +1 -1
- data/lib/friendly_id/initializer.rb +5 -1
- data/lib/friendly_id/sequentially_slugged.rb +74 -0
- data/lib/friendly_id/slugged.rb +15 -4
- data/lib/friendly_id/version.rb +1 -1
- data/test/base_test.rb +1 -1
- data/test/candidates_test.rb +27 -1
- data/test/configuration_test.rb +1 -1
- data/test/core_test.rb +1 -1
- data/test/finders_test.rb +1 -1
- data/test/helper.rb +18 -2
- data/test/history_test.rb +15 -2
- data/test/object_utils_test.rb +1 -1
- data/test/reserved_test.rb +1 -1
- data/test/scoped_test.rb +1 -1
- data/test/sequentially_slugged_test.rb +96 -0
- data/test/simple_i18n_test.rb +3 -3
- data/test/slugged_test.rb +48 -7
- data/test/sti_test.rb +3 -3
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d11cd5a5fc78a05718add4cf44e44f4425700f02
|
4
|
+
data.tar.gz: 581623a3a24eff70d1e0ee307bdcaf814631a80c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9068ff6f4efaab2c47673eb31b5f700898d5fad4f3d6f3fa754e7b9f9264ee1b02689637af319995c83a2b0771a40d7c70ce422b112e8337b0281ba42a5e41bb
|
7
|
+
data.tar.gz: 5726a377bc4573bfb59cbfdc0d0d4abe224516bd691017299809da5304abd51477da9c8c7bb0f48916afd44b4de9b77ddc81716e0e9be69e24427018eb7abe51
|
data/.travis.yml
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
2
3
|
|
3
4
|
rvm:
|
5
|
+
- 2.2.0
|
4
6
|
- 2.0.0
|
5
7
|
- 2.1.0
|
6
8
|
- jruby-19mode
|
@@ -15,16 +17,14 @@ gemfile:
|
|
15
17
|
- gemfiles/Gemfile.rails-4.1.rb
|
16
18
|
- gemfiles/Gemfile.rails-4.2.rb
|
17
19
|
|
18
|
-
|
20
|
+
matrix:
|
21
|
+
allow_failures:
|
22
|
+
- rvm: jruby-19mode
|
23
|
+
gemfile: gemfiles/Gemfile.rails-4.2.rb
|
24
|
+
env: DB=postgres
|
19
25
|
|
20
|
-
|
21
|
-
install: bundle install --retry=3
|
26
|
+
sudo: false
|
22
27
|
|
23
28
|
before_script: 'bundle exec rake db:create db:up'
|
24
29
|
|
25
30
|
script: 'COVERALLS=true bundle exec rake test'
|
26
|
-
|
27
|
-
matrix:
|
28
|
-
allow_failures:
|
29
|
-
- gemfile: gemfiles/Gemfile.rails-4.2.rb
|
30
|
-
|
data/Changelog.md
CHANGED
@@ -3,6 +3,14 @@
|
|
3
3
|
We would like to think our many {file:Contributors contributors} for
|
4
4
|
suggestions, ideas and improvements to FriendlyId.
|
5
5
|
|
6
|
+
## 5.2.0 (NOT RELEASED YET)
|
7
|
+
|
8
|
+
* Add sequential slug module for FriendlyId 4.x-style sequential slugs. ([#644](https://github.com/norman/friendly_id/pull/644)).
|
9
|
+
* Make Candidates#each iterable without block ([#651](https://github.com/norman/friendly_id/pull/651)).
|
10
|
+
* Ensure slug history prefers the record that most recently used the slug ([#663](https://github.com/norman/friendly_id/pull/663)).
|
11
|
+
* Don't calculate all changes just to check if the param field has changed ([#667](https://github.com/norman/friendly_id/pull/667)).
|
12
|
+
* Don't set or change slug when unrelated validation failures block the record from being saved ([#642](https://github.com/norman/friendly_id/issues/642)).
|
13
|
+
|
6
14
|
## 5.1.0 (2015-01-15)
|
7
15
|
|
8
16
|
* FriendlyId will no longer allow blank strings as slugs ([#571](https://github.com/norman/friendly_id/pull/571)).
|
@@ -96,7 +104,7 @@ suggestions, ideas and improvements to FriendlyId.
|
|
96
104
|
* `find` no longer falls back to super unless id is fully numeric string (Norman Clarke).
|
97
105
|
* Default sequence separator is now '-' rather than '--'.
|
98
106
|
* Support for Globalize has been removed until Globalize supports Rails 4.
|
99
|
-
* Removed
|
107
|
+
* Removed support for Ruby < 1.9.3 and Rails < 4.0.
|
100
108
|
|
101
109
|
## 4.0.10.1 (2013-08-20)
|
102
110
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
Please ask questions on [Stack
|
8
8
|
Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
|
9
9
|
"friendly-id" tag. Prior to asking, search and see if your question has
|
10
|
-
already been
|
10
|
+
already been answered.
|
11
11
|
|
12
12
|
Please only post issues in Github issues for actual bugs.
|
13
13
|
|
@@ -135,6 +135,11 @@ add_index :friendly_id_slugs, [:slug, :sluggable_type]
|
|
135
135
|
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], unique: true
|
136
136
|
```
|
137
137
|
|
138
|
+
## Articles
|
139
|
+
|
140
|
+
[Migrating an ad-hoc URL slug system to FriendlyId](http://olivierlacan.com/posts/migrating-an-ad-hoc-url-slug-system-to-friendly-id/)
|
141
|
+
[Pretty URLs with FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid)
|
142
|
+
|
138
143
|
## Docs
|
139
144
|
|
140
145
|
The most current docs from the master branch can always be found
|
@@ -151,9 +156,12 @@ The best place to start is with the
|
|
151
156
|
[Guide](http://norman.github.io/friendly_id/file.Guide.html),
|
152
157
|
which compiles the top-level RDocs into one outlined document.
|
153
158
|
|
159
|
+
For a getting started video, you may want to watch [GoRails #9](https://gorails.com/episodes/pretty-urls-with-friendly-id)
|
160
|
+
|
154
161
|
You might also want to watch Ryan Bates's [Railscast on FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid),
|
155
162
|
which is now somewhat outdated but still relevant.
|
156
163
|
|
164
|
+
|
157
165
|
## Rails Quickstart
|
158
166
|
|
159
167
|
```shell
|
data/Rakefile
CHANGED
@@ -2,12 +2,16 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec path: '../'
|
4
4
|
|
5
|
+
gem 'activerecord', '~> 4.0.13'
|
6
|
+
gem 'railties', '~> 4.0.13'
|
7
|
+
gem 'minitest', '~> 4.5.0'
|
8
|
+
|
5
9
|
# Database Configuration
|
6
10
|
group :development, :test do
|
7
11
|
platforms :jruby do
|
8
|
-
gem 'activerecord-jdbcsqlite3-adapter', '
|
9
|
-
gem 'activerecord-jdbcmysql-adapter', '
|
10
|
-
gem 'activerecord-jdbcpostgresql-adapter', '
|
12
|
+
gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.14'
|
13
|
+
gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
|
14
|
+
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
|
11
15
|
gem 'kramdown'
|
12
16
|
end
|
13
17
|
|
@@ -2,15 +2,15 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec path: '../'
|
4
4
|
|
5
|
-
gem 'activerecord', '~> 4.1.
|
6
|
-
gem 'railties', '~> 4.1.
|
5
|
+
gem 'activerecord', '~> 4.1.9'
|
6
|
+
gem 'railties', '~> 4.1.9'
|
7
7
|
|
8
8
|
# Database Configuration
|
9
9
|
group :development, :test do
|
10
10
|
platforms :jruby do
|
11
|
-
gem 'activerecord-jdbcsqlite3-adapter', '
|
12
|
-
gem 'activerecord-jdbcmysql-adapter', '
|
13
|
-
gem 'activerecord-jdbcpostgresql-adapter', '
|
11
|
+
gem 'activerecord-jdbcsqlite3-adapter', '~> 1.3.14'
|
12
|
+
gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
|
13
|
+
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
|
14
14
|
gem 'kramdown'
|
15
15
|
end
|
16
16
|
|
@@ -2,19 +2,15 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec path: '../'
|
4
4
|
|
5
|
-
gem '
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
gem 'i18n', '0.7.0.beta1'
|
5
|
+
gem 'activerecord', '~> 4.2.1'
|
6
|
+
gem 'railties', '~> 4.2.1'
|
7
|
+
gem 'i18n', '~> 0.7.0'
|
11
8
|
|
12
9
|
# Database Configuration
|
13
10
|
group :development, :test do
|
14
11
|
platforms :jruby do
|
15
|
-
gem 'activerecord-
|
16
|
-
gem 'activerecord-
|
17
|
-
gem 'activerecord-jdbcpostgresql-adapter', '>= 1.3.0.beta2'
|
12
|
+
gem 'activerecord-jdbcmysql-adapter', '~> 1.3.14'
|
13
|
+
gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3.14'
|
18
14
|
gem 'kramdown'
|
19
15
|
end
|
20
16
|
|
data/lib/friendly_id.rb
CHANGED
@@ -42,13 +42,14 @@ with numeric ids:
|
|
42
42
|
=end
|
43
43
|
module FriendlyId
|
44
44
|
|
45
|
-
autoload :History,
|
46
|
-
autoload :Slug,
|
47
|
-
autoload :SimpleI18n,
|
48
|
-
autoload :Reserved,
|
49
|
-
autoload :Scoped,
|
50
|
-
autoload :Slugged,
|
51
|
-
autoload :Finders,
|
45
|
+
autoload :History, "friendly_id/history"
|
46
|
+
autoload :Slug, "friendly_id/slug"
|
47
|
+
autoload :SimpleI18n, "friendly_id/simple_i18n"
|
48
|
+
autoload :Reserved, "friendly_id/reserved"
|
49
|
+
autoload :Scoped, "friendly_id/scoped"
|
50
|
+
autoload :Slugged, "friendly_id/slugged"
|
51
|
+
autoload :Finders, "friendly_id/finders"
|
52
|
+
autoload :SequentiallySlugged, "friendly_id/sequentially_slugged"
|
52
53
|
|
53
54
|
# Instances of these classes will never be considered a friendly id.
|
54
55
|
# @see FriendlyId::ObjectUtils#friendly_id
|
data/lib/friendly_id/base.rb
CHANGED
@@ -244,7 +244,8 @@ often better and easier to use {FriendlyId::Slugged slugs}.
|
|
244
244
|
|
245
245
|
# Either the friendly_id, or the numeric id cast to a string.
|
246
246
|
def to_param
|
247
|
-
if
|
247
|
+
if attribute_changed?(friendly_id_config.query_field)
|
248
|
+
diff = changes[friendly_id_config.query_field]
|
248
249
|
diff.first || diff.second
|
249
250
|
else
|
250
251
|
friendly_id.presence.to_param || super
|
data/lib/friendly_id/history.rb
CHANGED
@@ -95,7 +95,7 @@ method.
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def slug_table_record(id)
|
98
|
-
select(quoted_table_name + '.*').joins(:slugs).where(slug_history_clause(id)).first
|
98
|
+
select(quoted_table_name + '.*').joins(:slugs).where(slug_history_clause(id)).order(Slug.arel_table[:id].desc).first
|
99
99
|
end
|
100
100
|
|
101
101
|
def slug_history_clause(id)
|
@@ -59,13 +59,17 @@ FriendlyId.defaults do |config|
|
|
59
59
|
#
|
60
60
|
# config.sequence_separator = '-'
|
61
61
|
#
|
62
|
+
# Note that you must use the :slugged addon **prior** to the line which
|
63
|
+
# configures the sequence separator, or else FriendlyId will raise an undefined
|
64
|
+
# method error.
|
65
|
+
#
|
62
66
|
# ## Tips and Tricks
|
63
67
|
#
|
64
68
|
# ### Controlling when slugs are generated
|
65
69
|
#
|
66
70
|
# As of FriendlyId 5.0, new slugs are generated only when the slug field is
|
67
71
|
# nil, but if you're using a column as your base method can change this
|
68
|
-
# behavior by overriding the `should_generate_new_friendly_id
|
72
|
+
# behavior by overriding the `should_generate_new_friendly_id?` method that
|
69
73
|
# FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
|
70
74
|
# more like 4.0.
|
71
75
|
#
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
module SequentiallySlugged
|
3
|
+
def self.setup(model_class)
|
4
|
+
model_class.friendly_id_config.use :slugged
|
5
|
+
end
|
6
|
+
|
7
|
+
def should_generate_new_friendly_id?
|
8
|
+
send(friendly_id_config.base).present? && super
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve_friendly_id_conflict(candidate_slugs)
|
12
|
+
SequentialSlugCalculator.new(scope_for_slug_generator,
|
13
|
+
candidate_slugs.first,
|
14
|
+
friendly_id_config.slug_column,
|
15
|
+
friendly_id_config.sequence_separator).next_slug
|
16
|
+
end
|
17
|
+
|
18
|
+
class SequentialSlugCalculator
|
19
|
+
attr_accessor :scope, :slug, :slug_column, :sequence_separator
|
20
|
+
|
21
|
+
def initialize(scope, slug, slug_column, sequence_separator)
|
22
|
+
@scope = scope
|
23
|
+
@slug = slug
|
24
|
+
@slug_column = scope.connection.quote_column_name(slug_column)
|
25
|
+
@sequence_separator = sequence_separator
|
26
|
+
end
|
27
|
+
|
28
|
+
def next_slug
|
29
|
+
slug + sequence_separator + next_sequence_number.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def next_sequence_number
|
35
|
+
last_sequence_number ? last_sequence_number + 1 : 2
|
36
|
+
end
|
37
|
+
|
38
|
+
def last_sequence_number
|
39
|
+
if match = /#{slug}#{sequence_separator}(\d+)\z/.match(slug_conflicts.last)
|
40
|
+
match[1].to_i
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def slug_conflicts
|
45
|
+
scope.
|
46
|
+
where(conflict_query, slug, sequential_slug_matcher).
|
47
|
+
order(ordering_query).pluck(slug_column)
|
48
|
+
end
|
49
|
+
|
50
|
+
def conflict_query
|
51
|
+
base = "#{slug_column} = ? OR #{slug_column} LIKE ?"
|
52
|
+
# Awful hack for SQLite3, which does not pick up '\' as the escape character
|
53
|
+
# without this.
|
54
|
+
base << " ESCAPE '\\'" if scope.connection.adapter_name =~ /sqlite/i
|
55
|
+
base
|
56
|
+
end
|
57
|
+
|
58
|
+
def sequential_slug_matcher
|
59
|
+
# Underscores (matching a single character) and percent signs (matching
|
60
|
+
# any number of characters) need to be escaped. While this looks like
|
61
|
+
# an excessive number of backslashes, it is correct.
|
62
|
+
"#{slug}#{sequence_separator}".gsub(/[_%]/, '\\\\\&') + '%'
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return the unnumbered (shortest) slug first, followed by the numbered ones
|
66
|
+
# in ascending order.
|
67
|
+
def ordering_query
|
68
|
+
length_command = "LENGTH"
|
69
|
+
length_command = "LEN" if scope.connection.adapter_name =~ /sqlserver/i
|
70
|
+
"#{length_command}(#{slug_column}) ASC, #{slug_column} ASC"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/friendly_id/slugged.rb
CHANGED
@@ -84,7 +84,9 @@ FriendlyId uses.
|
|
84
84
|
Here's an example of a class that uses a custom method to generate the slug:
|
85
85
|
|
86
86
|
class Person < ActiveRecord::Base
|
87
|
-
|
87
|
+
extend FriendlyId
|
88
|
+
friendly_id :name_and_location, use: :slugged
|
89
|
+
|
88
90
|
def name_and_location
|
89
91
|
"#{name} from #{location}"
|
90
92
|
end
|
@@ -186,7 +188,7 @@ control exactly when new friendly ids are set:
|
|
186
188
|
end
|
187
189
|
end
|
188
190
|
|
189
|
-
If you want to extend the default behavior but
|
191
|
+
If you want to extend the default behavior but add your own conditions,
|
190
192
|
don't forget to invoke `super` from your implementation:
|
191
193
|
|
192
194
|
class Category < ActiveRecord::Base
|
@@ -246,14 +248,15 @@ Github issue](https://github.com/norman/friendly_id/issues/185) for discussion.
|
|
246
248
|
defaults[:sequence_separator] ||= '-'
|
247
249
|
end
|
248
250
|
model_class.before_validation :set_slug
|
251
|
+
model_class.after_validation :unset_slug_if_invalid
|
249
252
|
end
|
250
253
|
|
251
254
|
# Process the given value to make it suitable for use as a slug.
|
252
255
|
#
|
253
256
|
# This method is not intended to be invoked directly; FriendlyId uses it
|
254
|
-
#
|
257
|
+
# internally to process strings into slugs.
|
255
258
|
#
|
256
|
-
# However, if FriendlyId's default slug generation doesn't
|
259
|
+
# However, if FriendlyId's default slug generation doesn't suit your needs,
|
257
260
|
# you can override this method in your model class to control exactly how
|
258
261
|
# slugs are generated.
|
259
262
|
#
|
@@ -322,6 +325,14 @@ Github issue](https://github.com/norman/friendly_id/issues/185) for discussion.
|
|
322
325
|
end
|
323
326
|
private :slug_generator
|
324
327
|
|
328
|
+
def unset_slug_if_invalid
|
329
|
+
if errors.present? && attribute_changed?(friendly_id_config.query_field)
|
330
|
+
diff = changes[friendly_id_config.query_field]
|
331
|
+
self.slug = diff.first
|
332
|
+
end
|
333
|
+
end
|
334
|
+
private :unset_slug_if_invalid
|
335
|
+
|
325
336
|
# This module adds the `:slug_column`, and `:sequence_separator`, and
|
326
337
|
# `:slug_generator_class` configuration options to
|
327
338
|
# {FriendlyId::Configuration FriendlyId::Configuration}.
|
data/lib/friendly_id/version.rb
CHANGED
data/test/base_test.rb
CHANGED
data/test/candidates_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class CandidatesTest <
|
3
|
+
class CandidatesTest < TestCaseClass
|
4
4
|
|
5
5
|
include FriendlyId::Test
|
6
6
|
|
@@ -114,4 +114,30 @@ class CandidatesTest < Minitest::Test
|
|
114
114
|
end
|
115
115
|
end
|
116
116
|
|
117
|
+
test "allows to iterate through candidates without passing block" do
|
118
|
+
klass = Class.new model_class do
|
119
|
+
def slug_candidates
|
120
|
+
:name
|
121
|
+
end
|
122
|
+
end
|
123
|
+
with_instances_of klass do |_, city|
|
124
|
+
candidates = FriendlyId::Candidates.new(city, city.slug_candidates)
|
125
|
+
assert_equal candidates.each, ['new-york']
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
test "iterates through candidates with passed block" do
|
130
|
+
klass = Class.new model_class do
|
131
|
+
def slug_candidates
|
132
|
+
:name
|
133
|
+
end
|
134
|
+
end
|
135
|
+
with_instances_of klass do |_, city|
|
136
|
+
collected_candidates = []
|
137
|
+
candidates = FriendlyId::Candidates.new(city, city.slug_candidates)
|
138
|
+
candidates.each { |candidate| collected_candidates << candidate }
|
139
|
+
assert_equal collected_candidates, ['new-york']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
117
143
|
end
|
data/test/configuration_test.rb
CHANGED
data/test/core_test.rb
CHANGED
data/test/finders_test.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -12,7 +12,18 @@ if ENV['COVERALLS'] || ENV['COVERAGE']
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
begin
|
16
|
+
require 'minitest'
|
17
|
+
rescue LoadError
|
18
|
+
require 'minitest/unit'
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
TestCaseClass = MiniTest::Test
|
23
|
+
rescue NameError
|
24
|
+
TestCaseClass = MiniTest::Unit::TestCase
|
25
|
+
end
|
26
|
+
|
16
27
|
require "mocha/setup"
|
17
28
|
require "active_record"
|
18
29
|
require 'active_support/core_ext/time/conversions'
|
@@ -31,7 +42,12 @@ module FriendlyId
|
|
31
42
|
module Test
|
32
43
|
|
33
44
|
def self.included(base)
|
34
|
-
Minitest.autorun
|
45
|
+
if Minitest.respond_to?(:autorun)
|
46
|
+
Minitest.autorun
|
47
|
+
else
|
48
|
+
require 'minitest/autorun'
|
49
|
+
end
|
50
|
+
rescue LoadError
|
35
51
|
end
|
36
52
|
|
37
53
|
def transaction
|
data/test/history_test.rb
CHANGED
@@ -5,7 +5,7 @@ class Manual < ActiveRecord::Base
|
|
5
5
|
friendly_id :name, :use => [:slugged, :history]
|
6
6
|
end
|
7
7
|
|
8
|
-
class HistoryTest <
|
8
|
+
class HistoryTest < TestCaseClass
|
9
9
|
|
10
10
|
include FriendlyId::Test
|
11
11
|
include FriendlyId::Test::Shared::Core
|
@@ -117,6 +117,19 @@ class HistoryTest < Minitest::Test
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
test "should prefer product that used slug most recently" do
|
121
|
+
transaction do
|
122
|
+
first_record = model_class.create! name: "foo"
|
123
|
+
second_record = model_class.create! name: "bar"
|
124
|
+
|
125
|
+
first_record.update! slug: "not_foo"
|
126
|
+
second_record.update! slug: "foo" #now both records have used foo; second_record most recently
|
127
|
+
second_record.update! slug: "not_bar"
|
128
|
+
|
129
|
+
assert_equal model_class.friendly.find("foo"), second_record
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
120
133
|
test 'should name table according to prefix and suffix' do
|
121
134
|
transaction do
|
122
135
|
begin
|
@@ -214,7 +227,7 @@ class Restaurant < ActiveRecord::Base
|
|
214
227
|
friendly_id :name, :use => [:scoped, :history], :scope => :city
|
215
228
|
end
|
216
229
|
|
217
|
-
class ScopedHistoryTest <
|
230
|
+
class ScopedHistoryTest < TestCaseClass
|
218
231
|
include FriendlyId::Test
|
219
232
|
include FriendlyId::Test::Shared::Core
|
220
233
|
|
data/test/object_utils_test.rb
CHANGED
data/test/reserved_test.rb
CHANGED
data/test/scoped_test.rb
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Article < ActiveRecord::Base
|
4
|
+
extend FriendlyId
|
5
|
+
friendly_id :name, :use => :sequentially_slugged
|
6
|
+
end
|
7
|
+
|
8
|
+
class SequentiallySluggedTest < TestCaseClass
|
9
|
+
include FriendlyId::Test
|
10
|
+
include FriendlyId::Test::Shared::Core
|
11
|
+
|
12
|
+
def model_class
|
13
|
+
Article
|
14
|
+
end
|
15
|
+
|
16
|
+
test "should generate numerically sequential slugs" do
|
17
|
+
transaction do
|
18
|
+
records = 12.times.map { model_class.create! :name => "Some news" }
|
19
|
+
assert_equal "some-news", records[0].slug
|
20
|
+
(1...12).each {|i| assert_equal "some-news-#{i + 1}", records[i].slug}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
test "should cope when slugs are missing from the sequence" do
|
25
|
+
transaction do
|
26
|
+
record_1 = model_class.create!(:name => 'A thing')
|
27
|
+
record_2 = model_class.create!(:name => 'A thing')
|
28
|
+
record_3 = model_class.create!(:name => 'A thing')
|
29
|
+
|
30
|
+
assert_equal 'a-thing', record_1.slug
|
31
|
+
assert_equal 'a-thing-2', record_2.slug
|
32
|
+
assert_equal 'a-thing-3', record_3.slug
|
33
|
+
|
34
|
+
record_2.destroy
|
35
|
+
|
36
|
+
record_4 = model_class.create!(:name => 'A thing')
|
37
|
+
|
38
|
+
assert_equal 'a-thing-4', record_4.slug
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
test "should cope with strange column names" do
|
43
|
+
model_class = Class.new(ActiveRecord::Base) do
|
44
|
+
self.table_name = "journalists"
|
45
|
+
extend FriendlyId
|
46
|
+
friendly_id :name, :use => :sequentially_slugged, :slug_column => "strange name"
|
47
|
+
end
|
48
|
+
|
49
|
+
transaction do
|
50
|
+
record_1 = model_class.create! name: "Julian Assange"
|
51
|
+
record_2 = model_class.create! name: "Julian Assange"
|
52
|
+
|
53
|
+
assert_equal 'julian-assange', record_1.attributes["strange name"]
|
54
|
+
assert_equal 'julian-assange-2', record_2.attributes["strange name"]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
test "should correctly sequence slugs that end in a number" do
|
59
|
+
transaction do
|
60
|
+
record1 = model_class.create! :name => "Peugeuot 206"
|
61
|
+
assert_equal "peugeuot-206", record1.slug
|
62
|
+
record2 = model_class.create! :name => "Peugeuot 206"
|
63
|
+
assert_equal "peugeuot-206-2", record2.slug
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
test "should correctly sequence slugs that begin with a number" do
|
68
|
+
transaction do
|
69
|
+
record1 = model_class.create! :name => "2010 to 2015 Records"
|
70
|
+
assert_equal "2010-to-2015-records", record1.slug
|
71
|
+
record2 = model_class.create! :name => "2010 to 2015 Records"
|
72
|
+
assert_equal "2010-to-2015-records-2", record2.slug
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
test "should sequence with a custom sequence separator" do
|
77
|
+
model_class = Class.new(ActiveRecord::Base) do
|
78
|
+
self.table_name = "novelists"
|
79
|
+
extend FriendlyId
|
80
|
+
friendly_id :name, :use => :sequentially_slugged, :sequence_separator => ':'
|
81
|
+
end
|
82
|
+
|
83
|
+
transaction do
|
84
|
+
record_1 = model_class.create! name: "Julian Barnes"
|
85
|
+
record_2 = model_class.create! name: "Julian Barnes"
|
86
|
+
|
87
|
+
assert_equal 'julian-barnes', record_1.slug
|
88
|
+
assert_equal 'julian-barnes:2', record_2.slug
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
test "should not generate a slug when the sluggable attribute is blank" do
|
93
|
+
record = model_class.create!(:name => '')
|
94
|
+
assert_nil record.slug
|
95
|
+
end
|
96
|
+
end
|
data/test/simple_i18n_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class SimpleI18nTest <
|
3
|
+
class SimpleI18nTest < TestCaseClass
|
4
4
|
include FriendlyId::Test
|
5
5
|
|
6
6
|
class Journalist < ActiveRecord::Base
|
@@ -88,7 +88,7 @@ class SimpleI18nTest < Minitest::Test
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
class RegressionTest <
|
91
|
+
class RegressionTest < TestCaseClass
|
92
92
|
include FriendlyId::Test
|
93
93
|
|
94
94
|
test "should not overwrite other locale's slugs on update_attributes" do
|
@@ -107,7 +107,7 @@ class SimpleI18nTest < Minitest::Test
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
class ConfigurationTest <
|
110
|
+
class ConfigurationTest < TestCaseClass
|
111
111
|
test "should add locale to slug column for a non-default locale" do
|
112
112
|
I18n.with_locale :es do
|
113
113
|
assert_equal "slug_es", Journalist.friendly_id_config.slug_column
|
data/test/slugged_test.rb
CHANGED
@@ -19,7 +19,7 @@ class Novelist < ActiveRecord::Base
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class SluggedTest <
|
22
|
+
class SluggedTest < TestCaseClass
|
23
23
|
|
24
24
|
include FriendlyId::Test
|
25
25
|
include FriendlyId::Test::Shared::Core
|
@@ -92,9 +92,50 @@ class SluggedTest < Minitest::Test
|
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
|
+
test "should not set slug on create if unrelated validations fail" do
|
96
|
+
klass = Class.new model_class do
|
97
|
+
validates_presence_of :active
|
98
|
+
friendly_id :name, :use => :slugged
|
99
|
+
|
100
|
+
def self.name
|
101
|
+
"Journalist"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
transaction do
|
106
|
+
instance = klass.new :name => 'foo'
|
107
|
+
refute instance.save
|
108
|
+
refute instance.valid?
|
109
|
+
assert_nil instance.slug
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
test "should not update slug on save if unrelated validations fail" do
|
114
|
+
klass = Class.new model_class do
|
115
|
+
validates_presence_of :active
|
116
|
+
friendly_id :name, :use => :slugged
|
117
|
+
|
118
|
+
def self.name
|
119
|
+
"Journalist"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
transaction do
|
124
|
+
instance = klass.new :name => 'foo', :active => true
|
125
|
+
assert instance.save
|
126
|
+
assert instance.valid?
|
127
|
+
instance.name = 'foobar'
|
128
|
+
instance.slug = nil
|
129
|
+
instance.active = nil
|
130
|
+
refute instance.save
|
131
|
+
refute instance.valid?
|
132
|
+
assert_equal 'foo', instance.slug
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
95
136
|
end
|
96
137
|
|
97
|
-
class SlugGeneratorTest <
|
138
|
+
class SlugGeneratorTest < TestCaseClass
|
98
139
|
|
99
140
|
include FriendlyId::Test
|
100
141
|
|
@@ -158,7 +199,7 @@ class SlugGeneratorTest < Minitest::Test
|
|
158
199
|
|
159
200
|
end
|
160
201
|
|
161
|
-
class SlugSeparatorTest <
|
202
|
+
class SlugSeparatorTest < TestCaseClass
|
162
203
|
|
163
204
|
include FriendlyId::Test
|
164
205
|
|
@@ -210,7 +251,7 @@ class SlugSeparatorTest < Minitest::Test
|
|
210
251
|
|
211
252
|
end
|
212
253
|
|
213
|
-
class DefaultScopeTest <
|
254
|
+
class DefaultScopeTest < TestCaseClass
|
214
255
|
|
215
256
|
include FriendlyId::Test
|
216
257
|
|
@@ -235,7 +276,7 @@ class DefaultScopeTest < Minitest::Test
|
|
235
276
|
|
236
277
|
end
|
237
278
|
|
238
|
-
class UuidAsPrimaryKeyFindTest <
|
279
|
+
class UuidAsPrimaryKeyFindTest < TestCaseClass
|
239
280
|
|
240
281
|
include FriendlyId::Test
|
241
282
|
|
@@ -284,7 +325,7 @@ class UuidAsPrimaryKeyFindTest < Minitest::Test
|
|
284
325
|
|
285
326
|
end
|
286
327
|
|
287
|
-
class UnderscoreAsSequenceSeparatorRegressionTest <
|
328
|
+
class UnderscoreAsSequenceSeparatorRegressionTest < TestCaseClass
|
288
329
|
|
289
330
|
include FriendlyId::Test
|
290
331
|
|
@@ -308,7 +349,7 @@ class UnderscoreAsSequenceSeparatorRegressionTest < Minitest::Test
|
|
308
349
|
end
|
309
350
|
|
310
351
|
# https://github.com/norman/friendly_id/issues/148
|
311
|
-
class FailedValidationAfterUpdateRegressionTest <
|
352
|
+
class FailedValidationAfterUpdateRegressionTest < TestCaseClass
|
312
353
|
|
313
354
|
include FriendlyId::Test
|
314
355
|
|
data/test/sti_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
|
-
class StiTest <
|
3
|
+
class StiTest < TestCaseClass
|
4
4
|
|
5
5
|
include FriendlyId::Test
|
6
6
|
include FriendlyId::Test::Shared::Core
|
@@ -76,7 +76,7 @@ class StiTestWithHistory < StiTest
|
|
76
76
|
end
|
77
77
|
|
78
78
|
|
79
|
-
class StiTestWithFinders <
|
79
|
+
class StiTestWithFinders < TestCaseClass
|
80
80
|
|
81
81
|
include FriendlyId::Test
|
82
82
|
|
@@ -110,7 +110,7 @@ class StiTestWithFinders < Minitest::Test
|
|
110
110
|
|
111
111
|
end
|
112
112
|
|
113
|
-
class StiTestSubClass <
|
113
|
+
class StiTestSubClass < TestCaseClass
|
114
114
|
|
115
115
|
include FriendlyId::Test
|
116
116
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friendly_id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.2.0.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Norman Clarke
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-01
|
12
|
+
date: 2015-06-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -177,6 +177,7 @@ files:
|
|
177
177
|
- lib/friendly_id/object_utils.rb
|
178
178
|
- lib/friendly_id/reserved.rb
|
179
179
|
- lib/friendly_id/scoped.rb
|
180
|
+
- lib/friendly_id/sequentially_slugged.rb
|
180
181
|
- lib/friendly_id/simple_i18n.rb
|
181
182
|
- lib/friendly_id/slug.rb
|
182
183
|
- lib/friendly_id/slug_generator.rb
|
@@ -196,6 +197,7 @@ files:
|
|
196
197
|
- test/reserved_test.rb
|
197
198
|
- test/schema.rb
|
198
199
|
- test/scoped_test.rb
|
200
|
+
- test/sequentially_slugged_test.rb
|
199
201
|
- test/shared.rb
|
200
202
|
- test/simple_i18n_test.rb
|
201
203
|
- test/slugged_test.rb
|
@@ -215,12 +217,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
215
217
|
version: 1.9.3
|
216
218
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
217
219
|
requirements:
|
218
|
-
- - "
|
220
|
+
- - ">"
|
219
221
|
- !ruby/object:Gem::Version
|
220
|
-
version:
|
222
|
+
version: 1.3.1
|
221
223
|
requirements: []
|
222
224
|
rubyforge_project: friendly_id
|
223
|
-
rubygems_version: 2.4.
|
225
|
+
rubygems_version: 2.4.5
|
224
226
|
signing_key:
|
225
227
|
specification_version: 4
|
226
228
|
summary: A comprehensive slugging and pretty-URL plugin.
|