friendly_id 5.2.4 → 5.2.5

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