friendly_id 5.0.0.rc1 → 5.0.0.rc2

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