friendly_id 5.2.4 → 5.2.5

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
- SHA1:
3
- metadata.gz: 19856ab4f2e02ba7eed649beef7728be7eae0a58
4
- data.tar.gz: bcf94acb37ff90b70916a96a34a2035570490128
2
+ SHA256:
3
+ metadata.gz: cd9172f2c624c948c208a4e89b1d8de61f895d8d2cf0d233a3622d1f03da53f2
4
+ data.tar.gz: 1cb881d8be37a992cbac2d8def2c18338829e84b577bb364b36901a627849533
5
5
  SHA512:
6
- metadata.gz: 41b11ab959bcf15522a627431421bdb4542fd8768ee1695d6bf68d2351f479c4cd49e6f42e9b46a8f6aac0557bf199b8ad58582c02b049624204e12283740643
7
- data.tar.gz: 221d38007de3e624191d6905bd0430dc90bce1a9b1212cc20e8bb9bfbd06aa60fc8e0e44c74ec52b9d424210806683ed39446bd623eae09cc07d0542f58db0e6
6
+ metadata.gz: 9bca1be76cbc05a4d5dde5b936121ecf89b44c9949eb9b7ad574a20affd34fef104a443379b13d9a1a4843bbba9812366bd99d9b6db7eb56ace44581fecdad4a
7
+ data.tar.gz: d9c2ba647f04848ab3a690fd064e1669316bfc69116aa868a17034bed6916758e643365e425bc88f024fdec9bc7b5faaba89a5200696e0c1a3fddac04f140a30
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ gemspec
5
5
  group :development, :test do
6
6
  platforms :ruby do
7
7
  gem 'byebug'
8
+ gem 'pry'
8
9
  end
9
10
 
10
11
  platforms :jruby do
data/README.md CHANGED
@@ -39,7 +39,7 @@ FriendlyId offers many advanced features, including:
39
39
  Add this line to your application's Gemfile:
40
40
 
41
41
  ```ruby
42
- gem 'friendly_id', '~> 5.1.0' # Note: You MUST use 5.0.0 or greater for Rails 4.0+
42
+ gem 'friendly_id', '~> 5.2.4' # Note: You MUST use 5.0.0 or greater for Rails 4.0+
43
43
  ```
44
44
 
45
45
  And then execute:
@@ -48,14 +48,18 @@ And then execute:
48
48
  bundle install
49
49
  ```
50
50
 
51
- Generate the friendly configuration file
51
+ Add a `slug` column to the desired table (e.g. `Users`)
52
+ ```shell
53
+ rails g migration AddSlugToUsers slug:uniq
54
+ ```
55
+
56
+ Generate the friendly configuration file and a new migration
52
57
 
53
58
  ```shell
54
59
  rails generate friendly_id
55
60
  ```
56
61
 
57
- >Temp solution for Rails 5.1+ : Before running the migration, go into the generated migration file and specify the Rails version:
58
- `class CreateFriendlyIdSlugs < ActiveRecord::Migration[5.1]`
62
+ Note: You can delete the `CreateFriendlyIdSlugs` migration if you won't use the slug history feature. ([Read more](https://norman.github.io/friendly_id/FriendlyId/History.html))
59
63
 
60
64
  Run the migration scripts
61
65
 
@@ -9,8 +9,8 @@ gem 'i18n', '~> 0.7.0'
9
9
  # Database Configuration
10
10
  group :development, :test do
11
11
  platforms :jruby do
12
- gem 'activerecord-jdbcmysql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: '50-stable'
13
- gem 'activerecord-jdbcpostgresql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: '50-stable'
12
+ gem 'activerecord-jdbcmysql-adapter', '~> 50.1'
13
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 50.1'
14
14
  gem 'kramdown'
15
15
  end
16
16
 
@@ -8,8 +8,8 @@ gem 'railties', '~> 5.1.0'
8
8
  # Database Configuration
9
9
  group :development, :test do
10
10
  platforms :jruby do
11
- gem 'activerecord-jdbcmysql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
12
- gem 'activerecord-jdbcpostgresql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
11
+ gem 'activerecord-jdbcmysql-adapter', '~> 50.1'
12
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 50.1'
13
13
  gem 'kramdown'
14
14
  end
15
15
 
@@ -8,8 +8,8 @@ gem 'railties', '~> 5.2.0'
8
8
  # Database Configuration
9
9
  group :development, :test do
10
10
  platforms :jruby do
11
- gem 'activerecord-jdbcmysql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
12
- gem 'activerecord-jdbcpostgresql-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', branch: 'master'
11
+ gem 'activerecord-jdbcmysql-adapter', '~> 51.1'
12
+ gem 'activerecord-jdbcpostgresql-adapter', '~> 51.1'
13
13
  gem 'kramdown'
14
14
  end
15
15
 
@@ -261,12 +261,7 @@ often better and easier to use {FriendlyId::Slugged slugs}.
261
261
  # Either the friendly_id, or the numeric id cast to a string.
262
262
  def to_param
263
263
  if friendly_id_config.routes == :friendly
264
- if attribute_changed?(friendly_id_config.query_field)
265
- diff = changes[friendly_id_config.query_field]
266
- diff.first || diff.second
267
- else
268
- friendly_id.presence.to_param || super
269
- end
264
+ friendly_id.presence.to_param || super
270
265
  else
271
266
  super
272
267
  end
@@ -20,20 +20,22 @@ module FriendlyId
20
20
  return super if args.count != 1 || id.unfriendly_id?
21
21
  first_by_friendly_id(id).tap {|result| return result unless result.nil?}
22
22
  return super if potential_primary_key?(id)
23
- raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
23
+ raise_not_found_exception id
24
+
24
25
  end
25
26
 
26
27
  # Returns true if a record with the given id exists.
27
28
  def exists?(conditions = :none)
28
- return super unless conditions.friendly_id?
29
- exists_by_friendly_id?(conditions)
29
+ return super if conditions.unfriendly_id?
30
+ return true if exists_by_friendly_id?(conditions)
31
+ super
30
32
  end
31
33
 
32
34
  # Finds exclusively by the friendly id, completely bypassing original
33
35
  # `find`.
34
36
  # @raise ActiveRecord::RecordNotFound
35
37
  def find_by_friendly_id(id)
36
- first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound, "can't find record with friendly id: #{id.inspect}"
38
+ first_by_friendly_id(id) or raise raise_not_found_exception(id)
37
39
  end
38
40
 
39
41
  def exists_by_friendly_id?(id)
@@ -60,5 +62,14 @@ module FriendlyId
60
62
  find_by(friendly_id_config.query_field => id)
61
63
  end
62
64
 
65
+ def raise_not_found_exception(id)
66
+ message = "can't find record with friendly id: #{id.inspect}"
67
+ if ActiveRecord.version < Gem::Version.create('5.0') then
68
+ raise ActiveRecord::RecordNotFound.new(message)
69
+ else
70
+ raise ActiveRecord::RecordNotFound.new(message, name, friendly_id_config.query_field, id)
71
+ end
72
+ end
73
+
63
74
  end
64
75
  end
@@ -110,9 +110,10 @@ method.
110
110
  # to be conflicts. This will allow a record to revert to a previously
111
111
  # used slug.
112
112
  def scope_for_slug_generator
113
- relation = super
114
- return relation if new_record?
115
- relation = relation.joins(:slugs).merge(Slug.where('sluggable_id <> ?', id))
113
+ relation = super.joins(:slugs)
114
+ unless new_record?
115
+ relation = relation.merge(Slug.where('sluggable_id <> ?', id))
116
+ end
116
117
  if friendly_id_config.uses?(:scoped)
117
118
  relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
118
119
  end
@@ -19,7 +19,7 @@ FriendlyId.defaults do |config|
19
19
  config.reserved_words = %w(new edit index session login logout users admin
20
20
  stylesheets assets javascripts images)
21
21
 
22
- # This adds an option to to treat reserved words as conflicts rather than exceptions.
22
+ # This adds an option to treat reserved words as conflicts rather than exceptions.
23
23
  # When there is no good candidate, a UUID will be appended, matching the existing
24
24
  # conflict behavior.
25
25
 
@@ -1,11 +1,11 @@
1
- migration_class =
1
+ MIGRATION_CLASS =
2
2
  if ActiveRecord::VERSION::MAJOR >= 5
3
- ActiveRecord::Migration[4.2]
3
+ ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
4
4
  else
5
5
  ActiveRecord::Migration
6
6
  end
7
7
 
8
- class CreateFriendlyIdSlugs < migration_class
8
+ class CreateFriendlyIdSlugs < MIGRATION_CLASS
9
9
  def change
10
10
  create_table :friendly_id_slugs do |t|
11
11
  t.string :slug, :null => false
@@ -14,9 +14,8 @@ class CreateFriendlyIdSlugs < migration_class
14
14
  t.string :scope
15
15
  t.datetime :created_at
16
16
  end
17
- add_index :friendly_id_slugs, :sluggable_id
17
+ add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
18
18
  add_index :friendly_id_slugs, [:slug, :sluggable_type], length: { slug: 140, sluggable_type: 50 }
19
19
  add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: { slug: 70, sluggable_type: 50, scope: 70 }, unique: true
20
- add_index :friendly_id_slugs, :sluggable_type
21
20
  end
22
21
  end
@@ -122,7 +122,10 @@ an example of one way to set this up:
122
122
  end
123
123
 
124
124
  def scope_for_slug_generator
125
- relation = self.class.unscoped.friendly
125
+ if friendly_id_config.uses?(:History)
126
+ return super
127
+ end
128
+ relation = self.class.base_class.unscoped.friendly
126
129
  friendly_id_config.scope_columns.each do |column|
127
130
  relation = relation.where(column => send(column))
128
131
  end
@@ -136,6 +139,10 @@ an example of one way to set this up:
136
139
  end
137
140
  private :slug_generator
138
141
 
142
+ def should_generate_new_friendly_id?
143
+ (changed & friendly_id_config.scope_columns).any? || super
144
+ end
145
+
139
146
  # This module adds the `:scope` configuration option to
140
147
  # {FriendlyId::Configuration FriendlyId::Configuration}.
141
148
  module Configuration
@@ -11,7 +11,7 @@ module FriendlyId
11
11
  candidate,
12
12
  friendly_id_config.slug_column,
13
13
  friendly_id_config.sequence_separator,
14
- self.class.base_class).next_slug
14
+ slug_base_class).next_slug
15
15
  end
16
16
 
17
17
  class SequentialSlugCalculator
@@ -73,5 +73,15 @@ module FriendlyId
73
73
  "#{length_command}(#{slug_column}) ASC, #{slug_column} ASC"
74
74
  end
75
75
  end
76
+
77
+ private
78
+
79
+ def slug_base_class
80
+ if friendly_id_config.uses?(:history)
81
+ Slug
82
+ else
83
+ self.class.base_class
84
+ end
85
+ end
76
86
  end
77
87
  end
@@ -1,3 +1,3 @@
1
1
  module FriendlyId
2
- VERSION = '5.2.4'.freeze
2
+ VERSION = '5.2.5'.freeze
3
3
  end
@@ -69,7 +69,6 @@ module FriendlyId
69
69
 
70
70
  def connect
71
71
  version = ActiveRecord::VERSION::STRING
72
- driver = FriendlyId::Test::Database.driver
73
72
  engine = RUBY_ENGINE rescue "ruby"
74
73
 
75
74
  ActiveRecord::Base.establish_connection config[driver]
@@ -90,7 +89,9 @@ module FriendlyId
90
89
  end
91
90
 
92
91
  def driver
93
- (ENV["DB"] or "sqlite3").downcase
92
+ _driver = ENV.fetch('DB', 'sqlite3').downcase
93
+ _driver = "postgres" if %w(postgresql pg).include?(_driver)
94
+ _driver
94
95
  end
95
96
 
96
97
  def in_memory?
@@ -0,0 +1,31 @@
1
+ require 'helper'
2
+
3
+ class NumericSlugTest < TestCaseClass
4
+ include FriendlyId::Test
5
+ include FriendlyId::Test::Shared::Core
6
+
7
+ def model_class
8
+ Article
9
+ end
10
+
11
+ test "should generate numeric slugs" do
12
+ transaction do
13
+ record = model_class.create! :name => "123"
14
+ assert_equal "123", record.slug
15
+ end
16
+ end
17
+
18
+ test "should find by numeric slug" do
19
+ transaction do
20
+ record = model_class.create! :name => "123"
21
+ assert_equal model_class.friendly.find("123").id, record.id
22
+ end
23
+ end
24
+
25
+ test "should exist? by numeric slug" do
26
+ transaction do
27
+ record = model_class.create! :name => "123"
28
+ assert model_class.friendly.exists?("123")
29
+ end
30
+ end
31
+ end
@@ -81,4 +81,17 @@ class ScopedTest < TestCaseClass
81
81
  end
82
82
  end
83
83
 
84
+ test "should generate new slug when scope changes" do
85
+ transaction do
86
+ novelist = Novelist.create! :name => "a"
87
+ publisher = Publisher.create! :name => "b"
88
+ novel1 = Novel.create! :name => "c", :novelist => novelist, :publisher => publisher
89
+ novel2 = Novel.create! :name => "c", :novelist => novelist, :publisher => Publisher.create(:name => "d")
90
+ assert_equal novel1.friendly_id, novel2.friendly_id
91
+ novel2.publisher = publisher
92
+ novel2.save!
93
+ assert novel2.friendly_id != novel1.friendly_id
94
+ end
95
+ end
96
+
84
97
  end
@@ -137,4 +137,63 @@ class SequentiallySluggedTestWithHistory < TestCaseClass
137
137
  assert_equal 'test-name-2', record2.slug
138
138
  end
139
139
  end
140
+
141
+ test "should work with regeneration with history when 2 slugs already exists and the second is changed" do
142
+ transaction do
143
+ record1 = model_class.create! :name => "Test name"
144
+ record2 = model_class.create! :name => "Test name"
145
+ record3 = model_class.create! :name => "Another test name"
146
+ assert_equal 'test-name', record1.slug
147
+ assert_equal 'test-name-2', record2.slug
148
+ assert_equal 'another-test-name', record3.slug
149
+
150
+ record2.name = "One more test name"
151
+ record2.slug = nil
152
+ record2.save!
153
+ assert_equal 'one-more-test-name', record2.slug
154
+
155
+ record3.name = "Test name"
156
+ record3.slug = nil
157
+ record3.save!
158
+ assert_equal 'test-name-3', record3.slug
159
+ end
160
+ end
161
+ end
162
+
163
+ class City < ActiveRecord::Base
164
+ has_many :restaurants
165
+ end
166
+
167
+ class Restaurant < ActiveRecord::Base
168
+ extend FriendlyId
169
+ belongs_to :city
170
+ friendly_id :name, :use => [:sequentially_slugged, :scoped, :history], :scope => :city
171
+ end
172
+
173
+ class SequentiallySluggedTestWithScopedHistory < TestCaseClass
174
+ include FriendlyId::Test
175
+ include FriendlyId::Test::Shared::Core
176
+
177
+ def model_class
178
+ Restaurant
179
+ end
180
+
181
+ test "should work with regeneration with scoped history" do
182
+ transaction do
183
+ city1 = City.create!
184
+ city2 = City.create!
185
+ record1 = model_class.create! :name => "Test name", :city => city1
186
+ record2 = model_class.create! :name => "Test name", :city => city1
187
+
188
+ assert_equal 'test-name', record1.slug
189
+ assert_equal 'test-name-2', record2.slug
190
+
191
+ record2.name = 'Another test name'
192
+ record2.slug = nil
193
+ record2.save!
194
+
195
+ record3 = model_class.create! :name => "Test name", :city => city1
196
+ assert_equal 'test-name-3', record3.slug
197
+ end
198
+ end
140
199
  end
@@ -152,7 +152,6 @@ class SluggedTest < TestCaseClass
152
152
  assert_equal 'foo', instance.slug
153
153
  end
154
154
  end
155
-
156
155
  end
157
156
 
158
157
  class SlugGeneratorTest < TestCaseClass
@@ -419,6 +418,85 @@ class FailedValidationAfterUpdateRegressionTest < TestCaseClass
419
418
 
420
419
  end
421
420
 
421
+ class ToParamTest < TestCaseClass
422
+
423
+ include FriendlyId::Test
424
+
425
+ class Journalist < ActiveRecord::Base
426
+ extend FriendlyId
427
+ validates_presence_of :active
428
+ friendly_id :name, :use => :slugged
429
+
430
+ attr_accessor :to_param_in_callback
431
+
432
+ after_save do
433
+ self.to_param_in_callback = to_param
434
+ end
435
+ end
436
+
437
+ test "to_param should return nil if record is unpersisted" do
438
+ assert_nil Journalist.new.to_param
439
+ end
440
+
441
+ test "to_param should return nil if record failed validation" do
442
+ journalist = Journalist.new :name => 'Clark Kent', :active => nil
443
+ refute journalist.save
444
+ assert_nil journalist.to_param
445
+ end
446
+
447
+ test "to_param should use slugged attribute if record saved successfully" do
448
+ transaction do
449
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
450
+ assert journalist.save
451
+ assert_equal 'clark-kent', journalist.to_param
452
+ end
453
+ end
454
+
455
+ test "to_param should use original slug if existing record changes but fails to save" do
456
+ transaction do
457
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
458
+ assert journalist.save
459
+ journalist.name = 'Superman'
460
+ journalist.slug = nil
461
+ journalist.active = nil
462
+ refute journalist.save
463
+ assert_equal 'clark-kent', journalist.to_param
464
+ end
465
+ end
466
+
467
+ test "to_param should use new slug if existing record changes successfully" do
468
+ transaction do
469
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
470
+ assert journalist.save
471
+ journalist.name = 'Superman'
472
+ journalist.slug = nil
473
+ assert journalist.save
474
+ assert_equal 'superman', journalist.to_param
475
+ end
476
+ end
477
+
478
+ test "to_param should use new slug within callbacks if new record is saved successfully" do
479
+ transaction do
480
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
481
+ assert journalist.save
482
+ assert_equal 'clark-kent', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
483
+ end
484
+ end
485
+
486
+ test "to_param should use new slug within callbacks if existing record changes successfully" do
487
+ transaction do
488
+ journalist = Journalist.new :name => 'Clark Kent', :active => true
489
+ assert journalist.save
490
+ assert journalist.valid?
491
+ journalist.name = 'Superman'
492
+ journalist.slug = nil
493
+ assert journalist.save, "save should be successful"
494
+ assert_equal 'superman', journalist.to_param_in_callback, "value of to_param in callback should use the new slug value"
495
+ end
496
+ end
497
+
498
+ end
499
+
422
500
  class ConfigurableRoutesTest < TestCaseClass
423
501
  include FriendlyId::Test
424
502
 
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.2.4
4
+ version: 5.2.5
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: 2018-04-24 00:00:00.000000000 Z
12
+ date: 2018-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -199,6 +199,7 @@ files:
199
199
  - test/generator_test.rb
200
200
  - test/helper.rb
201
201
  - test/history_test.rb
202
+ - test/numeric_slug_test.rb
202
203
  - test/object_utils_test.rb
203
204
  - test/reserved_test.rb
204
205
  - test/schema.rb
@@ -228,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
228
229
  version: '0'
229
230
  requirements: []
230
231
  rubyforge_project: friendly_id
231
- rubygems_version: 2.6.13
232
+ rubygems_version: 2.7.6
232
233
  signing_key:
233
234
  specification_version: 4
234
235
  summary: A comprehensive slugging and pretty-URL plugin.