friendly_id 5.0.0.rc1 → 5.0.0.rc2

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: eb59a1c03451e5b3848ab8fa00da5aa476276d79
4
- data.tar.gz: f6057d3fad4267d1cea422b0eb72b78f433afc78
3
+ metadata.gz: 2b8e4a5f27bb5857aa5761f04a7300d96df81078
4
+ data.tar.gz: 85674fed68afdc2ba0122cff6edb0424eeb3dcaa
5
5
  SHA512:
6
- metadata.gz: 9bbd5d21be8df888836e6e98b14c43d45eb89da31816830c5287038dce5f1a60c9920adf3b69a498e96b5d0cb6addfe952c5983d307228f5c57726095d9160c7
7
- data.tar.gz: cae59c6cbc8406c0cedf700ad1201a23b01d74ec6b7d871333d8c156e273332485f581b59d2df9e59da689e4c4219dffe6a8f770f7a48e9f8af1b8b0a18b8de3
6
+ metadata.gz: 39e09066a6e62b4243e19f53c8640e15bfb05ed0c70fd4dbbfdfaf3cbb8aab162392e875ec79545e460ecdb716d2f8fd007c97a356963ccde9852b273216828c
7
+ data.tar.gz: c1b52cbedd571df3644ce78a94b22df6a747b9b22d5d40bede443dcedd9f69c7ad6771f18e3183b1761f6ace0d512590767c349bafe75f9d5c209c537777cd7c
@@ -1,41 +1,11 @@
1
1
  # FriendlyId
2
2
 
3
- ## Getting Help
3
+ Please ask questions on [Stack
4
+ Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
5
+ "friendly_id" or "friendly-id" tag. Prior to asking, search and see if your
6
+ question has already been anwered.
4
7
 
5
- Before opening a new issue, please search the existing issues to see if your
6
- question has been answered before.
7
-
8
- Please also take a look at the extensive documentation, especially the Guide.
9
-
10
- ## FAQ
11
-
12
- Here are the answers to some common questions.
13
-
14
- ### I'm using Rails 4...
15
-
16
- Then you **must** use FriendlyId 5, which is currently available in the master
17
- branch of the repository, and will be released to Rubygems soon.
18
-
19
- ### I'm seeing a bunch of odd characters at the end of the id
20
-
21
- For example: `2bc08962-b3dd-4f29-b2e6-244710c86106`. FriendlyId 5 uses a UUID
22
- rather than a sequence to simplify slug generation and avoid concurrency problems.
23
-
24
- ### How do I set up my routes for FriendlyId?
25
-
26
- Exactly like in any other Rails app. FriendlyId doesn't change any routing
27
- functionality, it only changes the ids that are shown in your routes.
28
-
29
- TIP: Make your app work the way you want to with numeric id's. Then add
30
- FriendlyId later.
31
-
32
- ### I'm seeing `undefined method `friendly_id'`
33
-
34
- You probably just added the friendly_id gem. Run `bundle` and restart your
35
- Rails app.
36
-
37
- ### I'm seeing `uninitialized constant FriendlyId`
38
-
39
- You probably just added the friendly_id gem. Run `bundle` and restart your
40
- Rails app.
8
+ Please only post issues in Github issues for actual bugs.
41
9
 
10
+ I am asking people to do this because the same questions keep getting asked
11
+ over and over and over again in the issues.
@@ -3,6 +3,17 @@
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.0.0.rc2 (2013-09-29)
7
+
8
+ * When the :finders addon has been included, use it in FriendlyId's internal
9
+ finds to boost performance.
10
+ * Use instance methods rather than class methods in migrations.
11
+ * On find, fall back to super when the primary key is a character type. Thanks
12
+ to [Jamie Davidson](https://github.com/jhdavids8).
13
+ * Fix reversion to previously used slug from history table when
14
+ `should_generate_new_friendly_id?` is overridden.
15
+ * Fix sequencing of numeric slugs
16
+
6
17
  ## 5.0.0.rc1 (2013-08-28)
7
18
 
8
19
  * Removed some outdated tests.
data/README.md CHANGED
@@ -1,5 +1,17 @@
1
1
  [![Build Status](https://travis-ci.org/norman/friendly_id.png)](https://travis-ci.org/norman/friendly_id)
2
2
 
3
+ **GETTING HELP**
4
+
5
+ Please ask questions on [Stack
6
+ Overflow](http://stackoverflow.com/questions/tagged/friendly-id) using the
7
+ "friendly_id" or "friendly-id" tag. Prior to asking, search and see if your
8
+ question has already been anwered.
9
+
10
+ Please only post issues in Github issues for actual bugs.
11
+
12
+ I am asking people to do this because the same questions keep getting asked
13
+ over and over and over again in the issues.
14
+
3
15
  **VERSION NOTE**
4
16
 
5
17
  **Rails 4**:
@@ -115,7 +127,7 @@ generated in `config/initializers/friendly_id.rb`. This file contains notes
115
127
  describing how to restore (or not) some of the defaults from FriendlyId 4.0.
116
128
 
117
129
  If you want to use the `:history` and `:scoped` addons together, you must add a
118
- `:scope` column to your friendly_id slugs table and replace the unique index on
130
+ `:scope` column to your friendly_id_slugs table and replace the unique index on
119
131
  `:slug` and `:sluggable_type` with a unique index on those two columns, plus
120
132
  the new `:scope` column.
121
133
 
@@ -151,7 +163,7 @@ cd my_app
151
163
  ```
152
164
  ```ruby
153
165
  # Gemfile
154
- gem 'friendly_id', '5.0.0.beta4' # Note: You MUST use 5.0.0 or greater for Rails 4.0+
166
+ gem 'friendly_id', '5.0.0.rc2' # Note: You MUST use 5.0.0 or greater for Rails 4.0+
155
167
  ```
156
168
  ```shell
157
169
  rails generate friendly_id
data/bench.rb CHANGED
@@ -72,6 +72,10 @@ Benchmark.bmbm do |x|
72
72
  N.times {transaction {Journalist.create :name => Faker::Name.name}}
73
73
  end
74
74
 
75
+ x.report 'insert (in-table-slug; included FinderMethods)' do
76
+ N.times {transaction {Restaurant.create :name => Faker::Name.name}}
77
+ end
78
+
75
79
  x.report 'insert (external slug)' do
76
80
  N.times {transaction {Manual.create :name => Faker::Name.name}}
77
81
  end
@@ -216,6 +216,10 @@ often better and easier to use {FriendlyId::Slugged slugs}.
216
216
  config.model_class = self
217
217
  end
218
218
  end
219
+
220
+ def primary_key_type
221
+ @primary_key_type ||= columns.find(&:primary).type
222
+ end
219
223
  end
220
224
 
221
225
  # Instance methods that will be added to all classes using FriendlyId.
@@ -1,7 +1,7 @@
1
1
  module FriendlyId
2
2
 
3
3
  module FinderMethods
4
-
4
+
5
5
  # Finds a record using the given id.
6
6
  #
7
7
  # If the id is "unfriendly", it will call the original find method.
@@ -19,7 +19,7 @@ module FriendlyId
19
19
  id = args.first
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
- return super if Integer(id, 10) rescue nil
22
+ return super if potential_primary_key?(id)
23
23
  raise ActiveRecord::RecordNotFound
24
24
  end
25
25
 
@@ -36,14 +36,25 @@ module FriendlyId
36
36
  first_by_friendly_id(id) or raise ActiveRecord::RecordNotFound
37
37
  end
38
38
 
39
+ def exists_by_friendly_id?(id)
40
+ where(friendly_id_config.query_field => id).exists?
41
+ end
42
+
39
43
  private
40
44
 
41
- def first_by_friendly_id(id)
42
- where(friendly_id_config.query_field => id).first
45
+ def potential_primary_key?(id)
46
+ case primary_key_type
47
+ when :integer
48
+ Integer(id, 10) rescue false
49
+ when :uuid
50
+ id.match /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
51
+ else
52
+ true
53
+ end
43
54
  end
44
55
 
45
- def exists_by_friendly_id?(id)
46
- where(friendly_id_config.query_field => id).exists?
56
+ def first_by_friendly_id(id)
57
+ where(friendly_id_config.query_field => id).first
47
58
  end
48
59
 
49
60
  end
@@ -82,16 +82,16 @@ method.
82
82
  module HistoryFinderMethods
83
83
  include FinderMethods
84
84
 
85
+ def exists_by_friendly_id?(id)
86
+ joins(:slugs).where(arel_table[friendly_id_config.query_field].eq(id).or(slug_history_clause(id))).exists?
87
+ end
88
+
85
89
  private
86
90
 
87
91
  def first_by_friendly_id(id)
88
92
  joins(:slugs).where(slug_history_clause(id)).readonly(false).first
89
93
  end
90
94
 
91
- def exists_by_friendly_id?(id)
92
- joins(:slugs).where(arel_table[friendly_id_config.query_field].eq(id).or(slug_history_clause(id))).exists?
93
- end
94
-
95
95
  def slug_history_clause(id)
96
96
  Slug.arel_table[:sluggable_type].eq(base_class.to_s).and(Slug.arel_table[:slug].eq(id))
97
97
  end
@@ -99,6 +99,19 @@ method.
99
99
 
100
100
  private
101
101
 
102
+ # If we're updating, don't consider historic slugs for the same record
103
+ # to be conflicts. This will allow a record to revert to a previously
104
+ # used slug.
105
+ def scope_for_slug_generator
106
+ relation = super
107
+ return relation if new_record?
108
+ relation = relation.merge(Slug.where('sluggable_id <> ?', id))
109
+ if friendly_id_config.uses?(:scoped)
110
+ relation = relation.where(:scope => serialized_scope)
111
+ end
112
+ relation
113
+ end
114
+
102
115
  def create_slug
103
116
  return unless friendly_id
104
117
  return if slugs.first.try(:slug) == friendly_id
@@ -34,6 +34,10 @@ FriendlyId.defaults do |config|
34
34
  # all applications, so you must explicity opt-in to this behavior. You can
35
35
  # always also configure it on a per-model basis if you prefer.
36
36
  #
37
+ # Something else to consider is that using the :finders addon boosts
38
+ # performance because it will avoid Rails-internal code that makes runtime
39
+ # calls to `Module.extend`.
40
+ #
37
41
  # config.use :finders
38
42
  #
39
43
  # ## Slugs
@@ -81,4 +85,4 @@ FriendlyId.defaults do |config|
81
85
  # text.to_slug.normalize! :transliterations => [:russian, :latin]
82
86
  # end
83
87
  # }
84
- end
88
+ end
@@ -1,6 +1,5 @@
1
1
  class CreateFriendlyIdSlugs < ActiveRecord::Migration
2
-
3
- def self.up
2
+ def change
4
3
  create_table :friendly_id_slugs do |t|
5
4
  t.string :slug, :null => false
6
5
  t.integer :sluggable_id, :null => false
@@ -13,8 +12,4 @@ class CreateFriendlyIdSlugs < ActiveRecord::Migration
13
12
  add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], :unique => true
14
13
  add_index :friendly_id_slugs, :sluggable_type
15
14
  end
16
-
17
- def self.down
18
- drop_table :friendly_id_slugs
19
- end
20
15
  end
@@ -8,7 +8,7 @@ module FriendlyId
8
8
  end
9
9
 
10
10
  def available?(slug)
11
- !@scope.exists?(slug)
11
+ !@scope.exists_by_friendly_id?(slug)
12
12
  end
13
13
 
14
14
  def add(slug)
@@ -297,13 +297,20 @@ Github issue](https://github.com/norman/friendly_id/issues/185) for discussion.
297
297
  end
298
298
  private :set_slug
299
299
 
300
- def slug_generator
301
- scope = self.class.base_class.unscoped.friendly
300
+ def scope_for_slug_generator
301
+ scope = self.class.base_class.unscoped
302
+ scope = scope.friendly unless friendly_id_config.uses? :finders
303
+
302
304
  if changed.include?(friendly_id_config.slug_column)
303
305
  column = self.class.quoted_table_name + '.' + self.class.quoted_primary_key
304
306
  scope = scope.where("#{column} <> ?", send(self.class.primary_key))
305
307
  end
306
- friendly_id_config.slug_generator_class.new(scope)
308
+ scope
309
+ end
310
+ private :scope_for_slug_generator
311
+
312
+ def slug_generator
313
+ friendly_id_config.slug_generator_class.new(scope_for_slug_generator)
307
314
  end
308
315
  private :slug_generator
309
316
 
@@ -1,3 +1,3 @@
1
1
  module FriendlyId
2
- VERSION = "5.0.0.rc1"
2
+ VERSION = "5.0.0.rc2"
3
3
  end
@@ -53,7 +53,7 @@ module FriendlyId
53
53
  puts "-" * 72
54
54
  if in_memory?
55
55
  ActiveRecord::Migration.verbose = false
56
- Schema.up
56
+ Schema.migrate :up
57
57
  puts "#{message} (in-memory)"
58
58
  else
59
59
  puts message
@@ -62,6 +62,14 @@ class HistoryTest < MiniTest::Unit::TestCase
62
62
  end
63
63
  end
64
64
 
65
+ test "should not be read only when found by slug" do
66
+ with_instance_of(model_class) do |record|
67
+ refute model_class.friendly.find(record.friendly_id).readonly?
68
+ assert record.update_attribute :name, 'foo'
69
+ assert record.update_attributes name: 'foo'
70
+ end
71
+ end
72
+
65
73
  test "should not be read only when found by old slug" do
66
74
  with_instance_of(model_class) do |record|
67
75
  old_friendly_id = record.friendly_id
@@ -74,10 +82,13 @@ class HistoryTest < MiniTest::Unit::TestCase
74
82
  test "should handle renames" do
75
83
  with_instance_of(model_class) do |record|
76
84
  record.name = 'x'
85
+ record.slug = nil
77
86
  assert record.save
78
87
  record.name = 'y'
88
+ record.slug = nil
79
89
  assert record.save
80
90
  record.name = 'x'
91
+ record.slug = nil
81
92
  assert record.save
82
93
  end
83
94
  end
@@ -122,6 +133,32 @@ class HistoryTest < MiniTest::Unit::TestCase
122
133
  end
123
134
  end
124
135
 
136
+ class HistoryTestWithAutomaticSlugRegeneration < HistoryTest
137
+ class Manual < ActiveRecord::Base
138
+ extend FriendlyId
139
+ friendly_id :name, :use => [:slugged, :history]
140
+
141
+ def should_generate_new_friendly_id?
142
+ slug.blank? or name_changed?
143
+ end
144
+ end
145
+
146
+ def model_class
147
+ Manual
148
+ end
149
+
150
+ test 'should allow reversion back to a previously used slug' do
151
+ with_instance_of(model_class, name: 'foo') do |record|
152
+ record.name = 'bar'
153
+ record.save!
154
+ assert_equal 'bar', record.friendly_id
155
+ record.name = 'foo'
156
+ record.save!
157
+ assert_equal 'foo', record.friendly_id
158
+ end
159
+ end
160
+ end
161
+
125
162
  class HistoryTestWithSti < HistoryTest
126
163
  class Journalist < ActiveRecord::Base
127
164
  extend FriendlyId
@@ -14,7 +14,7 @@ module FriendlyId
14
14
  def up
15
15
  # TODO: use schema version to avoid ugly hacks like this
16
16
  return if @done
17
- CreateFriendlyIdSlugs.up
17
+ CreateFriendlyIdSlugs.migrate :up
18
18
 
19
19
  tables.each do |table_name|
20
20
  create_table table_name do |t|
@@ -23,6 +23,15 @@ module FriendlyId
23
23
  end
24
24
  end
25
25
 
26
+ tables_with_string_primary_key.each do |table_name|
27
+ create_table table_name, primary_key: :string_key, id: false do |t|
28
+ t.string :name
29
+ t.string :string_key, null: false
30
+ t.string :slug
31
+ end
32
+ add_index table_name, :slug, unique: true
33
+ end
34
+
26
35
  slugged_tables.each do |table_name|
27
36
  add_column table_name, :slug, :string
28
37
  add_index table_name, :slug, :unique => true
@@ -64,6 +73,10 @@ module FriendlyId
64
73
  %w[journalists articles novelists novels manuals]
65
74
  end
66
75
 
76
+ def tables_with_string_primary_key
77
+ ["menu_items"]
78
+ end
79
+
67
80
  def scoped_tables
68
81
  ["restaurants"]
69
82
  end
@@ -134,6 +134,13 @@ class SlugGeneratorTest < MiniTest::Unit::TestCase
134
134
  end
135
135
  end
136
136
 
137
+ test "should correctly sequence numeric slugs" do
138
+ transaction do
139
+ n2 = 2.times.map {Article.create :name => '123'}.last
140
+ assert_match(/\A123-.*/, n2.friendly_id)
141
+ end
142
+ end
143
+
137
144
  end
138
145
 
139
146
  class SlugSeparatorTest < MiniTest::Unit::TestCase
@@ -205,6 +212,40 @@ class DefaultScopeTest < MiniTest::Unit::TestCase
205
212
  end
206
213
  end
207
214
 
215
+ class StringAsPrimaryKeyFindTest < MiniTest::Unit::TestCase
216
+ include FriendlyId::Test
217
+
218
+ class MenuItem < ActiveRecord::Base
219
+ extend FriendlyId
220
+ friendly_id :name, :use => :slugged
221
+ before_create :init_primary_key
222
+
223
+ def self.primary_key
224
+ "string_key"
225
+ end
226
+
227
+ private
228
+ def init_primary_key
229
+ self.string_key = SecureRandom.uuid
230
+ end
231
+ end
232
+
233
+ def model_class
234
+ MenuItem
235
+ end
236
+
237
+ test "should have a string as a primary key" do
238
+ assert_equal model_class.primary_key, "string_key"
239
+ assert_equal model_class.columns.find(&:primary).name, "string_key"
240
+ end
241
+
242
+ test "should be findable by the string primary key" do
243
+ with_instance_of(model_class) do |record|
244
+ assert model_class.friendly.find record.id
245
+ end
246
+ end
247
+ end
248
+
208
249
  class UnderscoreAsSequenceSeparatorRegressionTest < MiniTest::Unit::TestCase
209
250
  include FriendlyId::Test
210
251
 
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.0.0.rc1
4
+ version: 5.0.0.rc2
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: 2013-08-28 00:00:00.000000000 Z
12
+ date: 2013-09-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord