friendly_id 5.1.0 → 5.2.0.beta.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 +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.
|