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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05f2fb35bb72c551d82747fa97512d24f526e4d8
4
- data.tar.gz: 82a341d6308aaa9f49bfe6cf70bcd53a45d35caf
3
+ metadata.gz: d11cd5a5fc78a05718add4cf44e44f4425700f02
4
+ data.tar.gz: 581623a3a24eff70d1e0ee307bdcaf814631a80c
5
5
  SHA512:
6
- metadata.gz: 2635b18db56f42000f06a6ec6b7dd9ebb9c7189af7b21fa7ba1794611d3a3e0cef183e1f639dfdd238e6bcd53fd98d5e6e5bce58fe007a2cbe194dfbfa05e99f
7
- data.tar.gz: 98cfc3779cbe4fc466622ebf00c16135341325965979fc6b985cde411201a2a98aa3ed5ae61b9d66492790187f5354cc753e7ad3997ed524c9c64353fc27fb7b
6
+ metadata.gz: 9068ff6f4efaab2c47673eb31b5f700898d5fad4f3d6f3fa754e7b9f9264ee1b02689637af319995c83a2b0771a40d7c70ce422b112e8337b0281ba42a5e41bb
7
+ data.tar.gz: 5726a377bc4573bfb59cbfdc0d0d4abe224516bd691017299809da5304abd51477da9c8c7bb0f48916afd44b4de9b77ddc81716e0e9be69e24427018eb7abe51
@@ -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
- sudo: false
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: jruby-19mode
23
+ gemfile: gemfiles/Gemfile.rails-4.2.rb
24
+ env: DB=postgres
19
25
 
20
- # Per https://github.com/travis-ci/travis-ci/issues/2821
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
-
@@ -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 upport for Ruby < 1.9.3 and Rails < 4.0.
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 anwered.
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
@@ -67,7 +67,7 @@ namespace :db do
67
67
  %x{#{commands[driver] || true}}
68
68
  end
69
69
 
70
- desc "Create the database"
70
+ desc "Drop the database"
71
71
  task :drop => :load_path do
72
72
  require "helper"
73
73
  driver = FriendlyId::Test::Database.driver
@@ -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', '>= 1.3.0.beta2'
9
- gem 'activerecord-jdbcmysql-adapter', '>= 1.3.0.beta2'
10
- gem 'activerecord-jdbcpostgresql-adapter', '>= 1.3.0.beta2'
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.0'
6
- gem 'railties', '~> 4.1.0'
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', '>= 1.3.0.beta2'
12
- gem 'activerecord-jdbcmysql-adapter', '>= 1.3.0.beta2'
13
- gem 'activerecord-jdbcpostgresql-adapter', '>= 1.3.0.beta2'
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 'rails', '~> 4.2.0.rc3' do
6
- gem 'activerecord'
7
- gem 'railties'
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-jdbcsqlite3-adapter', '>= 1.3.0.beta2'
16
- gem 'activerecord-jdbcmysql-adapter', '>= 1.3.0.beta2'
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
 
@@ -42,13 +42,14 @@ with numeric ids:
42
42
  =end
43
43
  module FriendlyId
44
44
 
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"
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
@@ -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 diff = changes[friendly_id_config.query_field]
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
@@ -23,6 +23,9 @@ module FriendlyId
23
23
  unless pre_candidates.all? {|x| reserved?(x)}
24
24
  pre_candidates.reject! {|x| reserved?(x)}
25
25
  end
26
+
27
+ return pre_candidates unless block_given?
28
+
26
29
  pre_candidates.each {|x| yield x}
27
30
  end
28
31
 
@@ -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` method that
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
@@ -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
- friendly_id :name_and_location
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, adding your own conditions,
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
- # internaly to process strings into slugs.
257
+ # internally to process strings into slugs.
255
258
  #
256
- # However, if FriendlyId's default slug generation doesn't suite your needs,
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}.
@@ -1,3 +1,3 @@
1
1
  module FriendlyId
2
- VERSION = "5.1.0"
2
+ VERSION = "5.2.0.beta.1"
3
3
  end
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class CoreTest < Minitest::Test
3
+ class CoreTest < TestCaseClass
4
4
  include FriendlyId::Test
5
5
 
6
6
  test "friendly_id can be added using 'extend'" do
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class CandidatesTest < Minitest::Test
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
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class ConfigurationTest < Minitest::Test
3
+ class ConfigurationTest < TestCaseClass
4
4
 
5
5
  include FriendlyId::Test
6
6
 
@@ -11,7 +11,7 @@ class Author < ActiveRecord::Base
11
11
  has_many :books
12
12
  end
13
13
 
14
- class CoreTest < Minitest::Test
14
+ class CoreTest < TestCaseClass
15
15
 
16
16
  include FriendlyId::Test
17
17
  include FriendlyId::Test::Shared::Core
@@ -7,7 +7,7 @@ class JournalistWithFriendlyFinders < ActiveRecord::Base
7
7
  friendly_id :name, use: [:slugged, :finders]
8
8
  end
9
9
 
10
- class Finders < Minitest::Test
10
+ class Finders < TestCaseClass
11
11
 
12
12
  include FriendlyId::Test
13
13
 
@@ -12,7 +12,18 @@ if ENV['COVERALLS'] || ENV['COVERAGE']
12
12
  end
13
13
  end
14
14
 
15
- require 'minitest'
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
@@ -5,7 +5,7 @@ class Manual < ActiveRecord::Base
5
5
  friendly_id :name, :use => [:slugged, :history]
6
6
  end
7
7
 
8
- class HistoryTest < Minitest::Test
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 < Minitest::Test
230
+ class ScopedHistoryTest < TestCaseClass
218
231
  include FriendlyId::Test
219
232
  include FriendlyId::Test::Shared::Core
220
233
 
@@ -1,7 +1,7 @@
1
1
  require "helper"
2
2
 
3
3
 
4
- class ObjectUtilsTest < Minitest::Test
4
+ class ObjectUtilsTest < TestCaseClass
5
5
 
6
6
  include FriendlyId::Test
7
7
 
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class ReservedTest < Minitest::Test
3
+ class ReservedTest < TestCaseClass
4
4
 
5
5
  include FriendlyId::Test
6
6
 
@@ -20,7 +20,7 @@ class Publisher < ActiveRecord::Base
20
20
  has_many :novels
21
21
  end
22
22
 
23
- class ScopedTest < Minitest::Test
23
+ class ScopedTest < TestCaseClass
24
24
 
25
25
  include FriendlyId::Test
26
26
  include FriendlyId::Test::Shared::Core
@@ -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
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class SimpleI18nTest < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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
@@ -19,7 +19,7 @@ class Novelist < ActiveRecord::Base
19
19
  end
20
20
  end
21
21
 
22
- class SluggedTest < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
352
+ class FailedValidationAfterUpdateRegressionTest < TestCaseClass
312
353
 
313
354
  include FriendlyId::Test
314
355
 
@@ -1,6 +1,6 @@
1
1
  require "helper"
2
2
 
3
- class StiTest < Minitest::Test
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 < Minitest::Test
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 < Minitest::Test
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.1.0
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-16 00:00:00.000000000 Z
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: '0'
222
+ version: 1.3.1
221
223
  requirements: []
222
224
  rubyforge_project: friendly_id
223
- rubygems_version: 2.4.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.