friendly_id 3.0.4 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog.md CHANGED
@@ -6,6 +6,13 @@ suggestions, ideas and improvements to FriendlyId.
6
6
  * Table of Contents
7
7
  {:toc}
8
8
 
9
+ ## 3.0.5 (2010-06-10)
10
+
11
+ * Fixed support for Rails 3.0 beta4 (Bruno Michel)
12
+ * Made rake tasks skip validations (Emilio Tagua).
13
+ * Fixed incorrect status of records found with a numeric friendly_id.
14
+ * Made slug an explicit has_one relation to enable eager-loading via :include => :slug
15
+
9
16
  ## 3.0.4 (2010-04-27)
10
17
 
11
18
  * Fixed backwards-compatiblity with ActiveSupport 2.3.4 (Thanks Juergen Fesslmeier).
data/Guide.md CHANGED
@@ -77,7 +77,7 @@ with Rails 2.2.x, 2.3.x. and 3.0.
77
77
  After installing the gem, add an entry in environment.rb:
78
78
 
79
79
  config.gem "friendly_id", :version => "~> 2.3"
80
-
80
+
81
81
  ### Rails 3.0
82
82
 
83
83
  After installing the gem, add an entry in the Gemfile:
@@ -349,11 +349,13 @@ Checking the slugs table all the time has an impact on performance, so as of
349
349
  ### Automatic setup
350
350
 
351
351
  To enable slug caching, simply add a column named "cached_slug" to your model.
352
+ Is also advised to index this column for performance reason.
352
353
  FriendlyId will automatically use this column if it detects it:
353
354
 
354
355
  class AddCachedSlugToUsers < ActiveRecord::Migration
355
356
  def self.up
356
357
  add_column :users, :cached_slug, :string
358
+ add_index :users, :cached_slug
357
359
  end
358
360
 
359
361
  def self.down
@@ -556,4 +558,4 @@ enabled. But if it is, then your patches would be very welcome!
556
558
  find model using array of ids x1000 | 0.862 | 0.882 | 6.152 | 1.919 |
557
559
  find model using id, then to_param x1000 | 0.658 | 2.200 | 8.398 | 1.539 |
558
560
  ================================================================================================
559
- Total | 2.077 | 4.217 | 21.041 | 4.856 |
561
+ Total | 2.077 | 4.217 | 21.041 | 4.856 |
@@ -82,7 +82,7 @@ module FriendlyId
82
82
 
83
83
  def find
84
84
  @result = model_class.scoped(find_options).first(options)
85
- handle_friendly_result if friendly?
85
+ handle_friendly_result if @result or friendly_id_config.scope?
86
86
  @result
87
87
  rescue ::ActiveRecord::RecordNotFound => @error
88
88
  friendly_id_config.scope? ? raise_scoped_error : (raise @error)
@@ -120,9 +120,17 @@ module FriendlyId
120
120
 
121
121
  def find
122
122
  @result = model_class.scoped(find_options).first(options)
123
- handle_friendly_result if friendly?
124
- @result
123
+ if @result
124
+ handle_friendly_result
125
+ @result
126
+ else
127
+ uncached_find
128
+ end
125
129
  rescue ActiveRecord::RecordNotFound
130
+ uncached_find
131
+ end
132
+
133
+ def uncached_find
126
134
  SingleFinder.new(id, model_class, options).find
127
135
  end
128
136
 
@@ -136,7 +144,8 @@ module FriendlyId
136
144
  def self.included(base)
137
145
  base.class_eval do
138
146
  has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
139
- before_save :build_slug
147
+ has_one :slug, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
148
+ before_save :build_a_slug
140
149
  after_save :set_slug_cache
141
150
  after_update :update_scope
142
151
  after_update :update_dependent_scopes
@@ -151,18 +160,6 @@ module FriendlyId
151
160
  slugs.find_by_name_and_sequence(name, sequence)
152
161
  end
153
162
 
154
- # The model instance's current {FriendlyId::ActiveRecordAdapter::Slug slug}.
155
- def slug
156
- return @slug if new_record?
157
- @slug ||= slugs.first(:order => "id DESC")
158
- end
159
-
160
- # Set the slug.
161
- def slug=(slug)
162
- @new_friendly_id = slug.to_friendly_id unless slug.nil?
163
- super
164
- end
165
-
166
163
  # Returns the friendly id, or if none is available, the numeric id.
167
164
  def to_param
168
165
  friendly_id_config.cache_column ? to_param_from_cache : to_param_from_slug
@@ -185,9 +182,10 @@ module FriendlyId
185
182
  end
186
183
 
187
184
  # Build the new slug using the generated friendly id.
188
- def build_slug
185
+ def build_a_slug
189
186
  return unless new_slug_needed?
190
- self.slug = slugs.build :name => slug_text.to_s, :scope => friendly_id_config.scope_for(self)
187
+ @slug = slugs.build :name => slug_text.to_s, :scope => friendly_id_config.scope_for(self)
188
+ @new_friendly_id = @slug.to_friendly_id
191
189
  end
192
190
 
193
191
  # Reset the cached friendly_id?
@@ -198,8 +196,13 @@ module FriendlyId
198
196
  # Reset the cached friendly_id.
199
197
  def set_slug_cache
200
198
  if new_cache_needed?
201
- send "#{friendly_id_config.cache_column}=", slug.to_friendly_id
202
- send :update_without_callbacks
199
+ begin
200
+ send "#{friendly_id_config.cache_column}=", slug.to_friendly_id
201
+ update_without_callbacks
202
+ rescue ActiveRecord::StaleObjectError
203
+ reload
204
+ retry
205
+ end
203
206
  end
204
207
  end
205
208
 
@@ -226,6 +229,13 @@ module FriendlyId
226
229
  friendly_id_config.cache_column?
227
230
  end
228
231
 
232
+ # This method was removed in ActiveRecord 3.0.
233
+ if !ActiveRecord::Base.private_method_defined? :update_without_callbacks
234
+ def update_without_callbacks
235
+ save :callbacks => false
236
+ end
237
+ end
238
+
229
239
  end
230
240
  end
231
241
  end
@@ -31,7 +31,7 @@ module FriendlyId
31
31
  while records = find(:all, options) do
32
32
  break if records.size == 0
33
33
  records.each do |record|
34
- record.save!
34
+ record.save(:validate => false)
35
35
  yield(record) if block_given?
36
36
  end
37
37
  options[:conditions] = cond + " and #{klass.table_name}.id > #{records.last.id}"
@@ -66,4 +66,4 @@ module FriendlyId
66
66
 
67
67
  end
68
68
 
69
- end
69
+ end
@@ -191,7 +191,7 @@ module FriendlyId
191
191
  # @param *args <Symbol>
192
192
  # @return String
193
193
  def approximate_ascii!(*args)
194
- @maps = (self.class.approximations + args + [:common]).flatten.uniq
194
+ @maps = (self.class.approximations + args.flatten + [:common]).flatten.uniq
195
195
  @wrapped_string = normalize_utf8(:c).unpack("U*").map { |char| approx_char(char) }.flatten.pack("U*")
196
196
  end
197
197
 
@@ -206,7 +206,7 @@ module FriendlyId
206
206
  # though your milage may vary with Greek and Turkic strings.
207
207
  # @return String
208
208
  def downcase!
209
- @wrapped_string = apply_mapping :lowercase_mapping
209
+ @wrapped_string = ActiveSupport::Multibyte::Unicode.apply_mapping(@wrapped_string, :lowercase_mapping)
210
210
  end
211
211
 
212
212
  # Remove any non-word characters.
@@ -235,7 +235,7 @@ module FriendlyId
235
235
  # @param config [FriendlyId::Configuration]
236
236
  # @return String
237
237
  def normalize_for!(config)
238
- approximate_ascii! if config.approximate_ascii?
238
+ approximate_ascii!(config.ascii_approximation_options) if config.approximate_ascii?
239
239
  to_ascii! if config.strip_non_ascii?
240
240
  normalize!
241
241
  end
@@ -333,7 +333,7 @@ module FriendlyId
333
333
  # though your milage may vary with Greek and Turkic strings.
334
334
  # @return String
335
335
  def upcase!
336
- @wrapped_string = apply_mapping :uppercase_mapping
336
+ @wrapped_string = ActiveSupport::Multibyte::Unicode.apply_mapping(@wrapped_string, :uppercase_mapping)
337
337
  end
338
338
 
339
339
  # Validate that the slug string is not blank or reserved, and truncate
@@ -96,7 +96,6 @@ module FriendlyId
96
96
  !slug? || slug_text_changed?
97
97
  end
98
98
  end
99
-
100
99
  end
101
100
  end
102
- end
101
+ end
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  Module.send :include, Module.new {
2
3
  def test(name, &block)
3
4
  define_method("test_#{name.gsub(/[^a-z0-9]/i, "_")}".to_sym, &block)
@@ -141,7 +142,7 @@ module FriendlyId
141
142
  test "should make a new slug if the friendly_id method value has changed" do
142
143
  instance.name = "Changed title"
143
144
  instance.send save_method
144
- assert_equal 2, instance.slugs.size
145
+ assert_equal 2, instance.slugs(true).size
145
146
  end
146
147
 
147
148
  test "should be able to reuse an old friendly_id without incrementing the sequence" do
@@ -170,6 +171,13 @@ module FriendlyId
170
171
  assert instance2.friendly_id_status.best?
171
172
  end
172
173
 
174
+ test "should indicate correct status when found by a numeric friendly_id" do
175
+ instance = klass.send(create_method, :name => "100")
176
+ instance2 = klass.send(find_method, "100")
177
+ assert instance2.friendly_id_status.best?, "status expected to be best but isn't."
178
+ assert instance2.friendly_id_status.current?, "status expected to be current but isn't."
179
+ end
180
+
173
181
  test "should remain findable by previous slugs" do
174
182
  old_friendly_id = instance.friendly_id
175
183
  instance.name = "#{old_friendly_id} updated"
@@ -194,6 +202,19 @@ module FriendlyId
194
202
  end
195
203
  end
196
204
 
205
+ test "should approximate ascii if configured" do
206
+ klass.friendly_id_config.stubs(:approximate_ascii?).returns(true)
207
+ instance = klass.send(create_method, :name => "Cañón")
208
+ assert_equal "canon", instance.friendly_id
209
+ end
210
+
211
+ test "should approximate ascii with options if configured" do
212
+ klass.friendly_id_config.stubs(:approximate_ascii?).returns(true)
213
+ klass.friendly_id_config.stubs(:ascii_approximation_options).returns(:spanish)
214
+ instance = klass.send(create_method, :name => "Cañón")
215
+ assert_equal "cannon", instance.friendly_id
216
+ end
217
+
197
218
  end
198
219
 
199
220
  # Tests for FriendlyId::Status.
@@ -2,7 +2,7 @@ module FriendlyId
2
2
  module Version
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- TINY = 4
5
+ TINY = 5
6
6
  BUILD = nil
7
7
  STRING = [MAJOR, MINOR, TINY, BUILD].compact.join('.')
8
8
  end
@@ -1,8 +1,11 @@
1
1
  require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
+ require "logger"
3
4
  require "active_record"
4
5
  require "active_support"
5
6
 
7
+ # ActiveRecord::Base.logger = Logger.new($stdout)
8
+
6
9
  require File.expand_path("../../../lib/friendly_id/active_record", __FILE__)
7
10
  require File.expand_path("../../../generators/friendly_id/templates/create_slugs", __FILE__)
8
11
  require File.expand_path("../support/models", __FILE__)
@@ -31,6 +34,15 @@ class District < ActiveRecord::Base
31
34
  has_friendly_id :name, :use_slug => true
32
35
  end
33
36
 
37
+ # A model with optimistic locking enabled
38
+ class Region < ActiveRecord::Base
39
+ has_friendly_id :name, :use_slug => true
40
+ after_create do |obj|
41
+ other_instance = self.class.find obj.id
42
+ other_instance.update_attributes :note => name + "!"
43
+ end
44
+ end
45
+
34
46
  # A model that specifies a custom cached slug column
35
47
  class City < ActiveRecord::Base
36
48
  has_friendly_id :name, :use_slug => true, :cache_column => "my_slug"
@@ -116,4 +128,4 @@ end
116
128
  # A model used as a polymorphic owner
117
129
  class Company < ActiveRecord::Base
118
130
  has_many :sites, :as => :owner
119
- end
131
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path("../ar_test_helper", __FILE__)
2
+
3
+ module FriendlyId
4
+ module Test
5
+ module ActiveRecordAdapter
6
+ class OptimisticLockingTest < ::Test::Unit::TestCase
7
+ test "should update the cached slug when updating the slug" do
8
+ region = Region.create! :name => 'some name'
9
+ assert_nothing_raised do
10
+ region.update_attributes(:name => "new name")
11
+ end
12
+ assert_equal region.slug.to_friendly_id, region.cached_slug
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
@@ -9,6 +9,11 @@ module FriendlyId
9
9
  assert_nothing_raised do
10
10
  klass.find(instance.friendly_id, :include => :slugs)
11
11
  end
12
+
13
+ assert_nothing_raised do
14
+ klass.find(instance.friendly_id, :include => :slug)
15
+ end
16
+
12
17
  end
13
18
 
14
19
  def klass
@@ -23,6 +23,13 @@ class CreateSupportModels < ActiveRecord::Migration
23
23
  end
24
24
  add_index :cities, :my_slug, :unique => true
25
25
 
26
+ create_table :regions do |t|
27
+ t.string :name
28
+ t.string :cached_slug
29
+ t.string :note
30
+ t.integer :lock_version, :null => false, :default => 0
31
+ end
32
+ add_index :regions, :cached_slug, :unique => true
26
33
 
27
34
  create_table :countries do |t|
28
35
  t.string :name
@@ -76,7 +83,7 @@ class CreateSupportModels < ActiveRecord::Migration
76
83
  t.integer :owner_id
77
84
  t.string :owner_type
78
85
  end
79
-
86
+
80
87
  create_table :companies do |t|
81
88
  t.string :name
82
89
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- require(File.dirname(__FILE__) + "/test_helper")
2
+ require(File.expand_path("../test_helper", __FILE__))
3
3
 
4
4
  module FriendlyId
5
5
  module Test
data/test/test_helper.rb CHANGED
@@ -11,5 +11,6 @@ end
11
11
  require "test/unit"
12
12
  require "mocha"
13
13
  require "active_support"
14
+ require "ruby-debug"
14
15
  require File.expand_path("../../lib/friendly_id", __FILE__)
15
- require File.expand_path("../../lib/friendly_id/test", __FILE__)
16
+ require File.expand_path("../../lib/friendly_id/test", __FILE__)
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 3
7
7
  - 0
8
- - 4
9
- version: 3.0.4
8
+ - 5
9
+ version: 3.0.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Norman Clarke
@@ -16,13 +16,14 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-04-27 00:00:00 -03:00
19
+ date: 2010-06-10 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
23
  name: activerecord
24
24
  prerelease: false
25
25
  requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
26
27
  requirements:
27
28
  - - ">="
28
29
  - !ruby/object:Gem::Version
@@ -36,6 +37,7 @@ dependencies:
36
37
  name: activesupport
37
38
  prerelease: false
38
39
  requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
39
41
  requirements:
40
42
  - - ">="
41
43
  - !ruby/object:Gem::Version
@@ -91,6 +93,7 @@ files:
91
93
  - test/active_record_adapter/core.rb
92
94
  - test/active_record_adapter/custom_normalizer_test.rb
93
95
  - test/active_record_adapter/custom_table_name_test.rb
96
+ - test/active_record_adapter/optimistic_locking_test.rb
94
97
  - test/active_record_adapter/scoped_model_test.rb
95
98
  - test/active_record_adapter/simple_test.rb
96
99
  - test/active_record_adapter/slug_test.rb
@@ -122,6 +125,7 @@ rdoc_options: []
122
125
  require_paths:
123
126
  - lib
124
127
  required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
125
129
  requirements:
126
130
  - - ">="
127
131
  - !ruby/object:Gem::Version
@@ -129,6 +133,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
129
133
  - 0
130
134
  version: "0"
131
135
  required_rubygems_version: !ruby/object:Gem::Requirement
136
+ none: false
132
137
  requirements:
133
138
  - - ">="
134
139
  - !ruby/object:Gem::Version
@@ -138,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
143
  requirements: []
139
144
 
140
145
  rubyforge_project: friendly-id
141
- rubygems_version: 1.3.6
146
+ rubygems_version: 1.3.7
142
147
  signing_key:
143
148
  specification_version: 3
144
149
  summary: A comprehensive slugging and pretty-URL plugin.
@@ -147,6 +152,7 @@ test_files:
147
152
  - test/active_record_adapter/cached_slug_test.rb
148
153
  - test/active_record_adapter/custom_normalizer_test.rb
149
154
  - test/active_record_adapter/custom_table_name_test.rb
155
+ - test/active_record_adapter/optimistic_locking_test.rb
150
156
  - test/active_record_adapter/scoped_model_test.rb
151
157
  - test/active_record_adapter/simple_test.rb
152
158
  - test/active_record_adapter/slug_test.rb