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 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.