Skip to content

Commit fd9710d

Browse files
committed
Don't check uniqueness if !permalink_changed?
Bug fix: check uniqueness when assigned using permalink= Increase test coverage for uniqueness and update checking.
1 parent 255525f commit fd9710d

File tree

3 files changed

+91
-24
lines changed

3 files changed

+91
-24
lines changed

README

+15-1
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,18 @@ These are set to nil if Iconv is not loaded. You can also manually set them to
2020
end
2121

2222
Use the :if or :unless options to specify a Proc, method, or string to be called or evaluated. The permalink
23-
will only be generated if the option evaluates to true.
23+
will only be generated if the option evaluates to true.
24+
25+
[Added 3.11.2009 by Martin Emde] Make permalink_fu update your permalink everytime the dependent field(s) change.
26+
27+
class Article < ActiveRecord::Base
28+
has_permalink :title, :update => true
29+
end
30+
31+
Without :update set to true, your permalink will be set one time and subsequent changes to the field
32+
(title in this example) will not affect the permalink field. To regenerate the permalink field,
33+
set it to nil or a blank string within your model.
34+
35+
Old versions of rails without _changed? attribute support will result in the permalink field being regenerated every save.
36+
37+
[Fixed 3.11.2009 by Martin Emde] Permalink was not being checked for uniqueness when set directly with permalink=

lib/permalink_fu.rb

+13-9
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ module PluginMethods
5454
# has_permalink :title, :unique => false
5555
#
5656
# # update the permalink every time the attribute(s) change
57-
# # (relies on _changed? methods to function effectively)
57+
# # without _changed? methods (old rails version) this will rewrite the permalink every time
5858
# has_permalink :title, :update => true
5959
#
6060
# end
@@ -109,20 +109,23 @@ module InstanceMethods
109109
protected
110110
def create_common_permalink
111111
return unless should_create_permalink?
112-
if read_attribute(self.class.permalink_field).blank? || self.class.permalink_options[:update]
112+
if read_attribute(self.class.permalink_field).blank? || permalink_fields_changed?
113113
send("#{self.class.permalink_field}=", create_permalink_for(self.class.permalink_attributes))
114114
end
115-
if read_attribute(self.class.permalink_field).blank?
116-
send("#{self.class.permalink_field}=", PermalinkFu.random_permalink)
117-
end
115+
116+
# Quit now if we have the changed method available and nothing has changed
117+
permalink_changed = "#{self.class.permalink_field}_changed?"
118+
return if respond_to?(permalink_changed) && !send(permalink_changed)
119+
120+
# Otherwise find the limit and crop the permalink
118121
limit = self.class.columns_hash[self.class.permalink_field].limit
119122
base = send("#{self.class.permalink_field}=", read_attribute(self.class.permalink_field)[0..limit - 1])
120123
[limit, base]
121124
end
122125

123126
def create_unique_permalink
124127
limit, base = create_common_permalink
125-
return if limit.nil?
128+
return if limit.nil? # nil if the permalink has not changed or :if/:unless fail
126129
counter = 1
127130
# oh how i wish i could use a hash for conditions
128131
conditions = ["#{self.class.permalink_field} = ?", base]
@@ -149,13 +152,12 @@ def create_unique_permalink
149152
end
150153

151154
def create_permalink_for(attr_names)
152-
attr_names.collect { |attr_name| send(attr_name).to_s } * " "
155+
str = attr_names.collect { |attr_name| send(attr_name).to_s } * " "
156+
str.blank? ? PermalinkFu.random_permalink : str
153157
end
154158

155159
private
156160
def should_create_permalink?
157-
existing_value = send("#{self.class.permalink_field}")
158-
return false unless permalink_fields_changed? || existing_value.blank?
159161
if self.class.permalink_options[:if]
160162
evaluate_method(self.class.permalink_options[:if])
161163
elsif self.class.permalink_options[:unless]
@@ -165,7 +167,9 @@ def should_create_permalink?
165167
end
166168
end
167169

170+
# Don't even check _changed? methods unless :update is set
168171
def permalink_fields_changed?
172+
return false unless self.class.permalink_options[:update]
169173
self.class.permalink_attributes.any? do |attribute|
170174
changed_method = "#{attribute}_changed?"
171175
respond_to?(changed_method) ? send(changed_method) : true

test/permalink_fu_test.rb

+63-14
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@ def self.exists?(conditions)
111111
has_permalink :title
112112
end
113113

114+
class PermalinkChangeableMockModel < BaseModel
115+
def self.exists?(conditions)
116+
if conditions[1] == 'foo'
117+
true
118+
else
119+
false
120+
end
121+
end
122+
123+
has_permalink :title
124+
125+
def permalink_changed?
126+
@permalink_changed
127+
end
128+
129+
def permalink_will_change!
130+
@permalink_changed = true
131+
end
132+
end
133+
114134
class CommonMockModel < BaseModel
115135
def self.exists?(conditions)
116136
false # oh noes
@@ -147,21 +167,21 @@ def permalink
147167
end
148168
end
149169

150-
class ChangedModel < BaseModel
170+
class ChangedWithoutUpdateModel < BaseModel
151171
has_permalink :title
152172
def title_changed?; true; end
153173
end
154174

155-
class NoChangeModel < BaseModel
156-
has_permalink :title
157-
def title_changed?; false; end
158-
end
159-
160175
class ChangedWithUpdateModel < BaseModel
161176
has_permalink :title, :update => true
162177
def title_changed?; true; end
163178
end
164179

180+
class NoChangeModel < BaseModel
181+
has_permalink :title, :update => true
182+
def title_changed?; false; end
183+
end
184+
165185
class IfProcConditionModel < BaseModel
166186
has_permalink :title, :if => Proc.new { |obj| false }
167187
end
@@ -239,13 +259,26 @@ def test_multiple_attribute_permalink
239259
end
240260
end
241261
end
242-
262+
243263
def test_should_create_unique_permalink
264+
@m = MockModel.new
265+
@m.title = 'foo'
266+
@m.validate
267+
assert_equal 'foo-2', @m.permalink
268+
269+
@m.title = 'bar'
270+
@m.permalink = nil
271+
@m.validate
272+
assert_equal 'bar-3', @m.permalink
273+
end
274+
275+
def test_should_create_unique_permalink_when_assigned_directly
244276
@m = MockModel.new
245277
@m.permalink = 'foo'
246278
@m.validate
247279
assert_equal 'foo-2', @m.permalink
248280

281+
# should always check itself for uniqueness when not respond_to?(:permalink_changed?)
249282
@m.permalink = 'bar'
250283
@m.validate
251284
assert_equal 'bar-3', @m.permalink
@@ -265,6 +298,21 @@ def test_should_not_check_itself_for_unique_permalink
265298
@m.validate
266299
assert_equal 'bar-2', @m.permalink
267300
end
301+
302+
def test_should_check_itself_for_unique_permalink_if_permalink_field_changed
303+
@m = PermalinkChangeableMockModel.new
304+
@m.permalink_will_change!
305+
@m.permalink = 'foo'
306+
@m.validate
307+
assert_equal 'foo-2', @m.permalink
308+
end
309+
310+
def test_should_not_check_itself_for_unique_permalink_if_permalink_field_not_changed
311+
@m = PermalinkChangeableMockModel.new
312+
@m.permalink = 'foo'
313+
@m.validate
314+
assert_equal 'foo', @m.permalink
315+
end
268316

269317
def test_should_create_unique_scoped_permalink
270318
@m = ScopedModel.new
@@ -361,11 +409,12 @@ def test_should_not_update_permalink_unless_field_changed
361409
assert_equal 'unchanged', @m.read_attribute(:permalink)
362410
end
363411

364-
def test_should_update_permalink_if_field_changed
365-
@m = OverrideModel.new
412+
def test_should_not_update_permalink_without_update_set_even_if_field_changed
413+
@m = ChangedWithoutUpdateModel.new
366414
@m.title = 'the permalink'
415+
@m.permalink = 'unchanged'
367416
@m.validate
368-
assert_equal 'the-permalink', @m.read_attribute(:permalink)
417+
assert_equal 'unchanged', @m.read_attribute(:permalink)
369418
end
370419

371420
def test_should_update_permalink_if_changed_method_does_not_exist
@@ -391,7 +440,7 @@ def test_should_update_permalink_if_the_existing_permalink_is_blank
391440
assert_equal 'the-permalink', @m.read_attribute(:permalink)
392441
end
393442

394-
def test_should_update_permalink_if_the_title_is_nil
443+
def test_should_assign_a_random_permalink_if_the_title_is_nil
395444
@m = NoChangeModel.new
396445
@m.title = nil
397446
@m.validate
@@ -400,7 +449,7 @@ def test_should_update_permalink_if_the_title_is_nil
400449
end
401450

402451
def test_should_update_permalink_the_first_time_the_title_is_set
403-
@m = ChangedModel.new
452+
@m = ChangedWithoutUpdateModel.new
404453
@m.title = "old title"
405454
@m.validate
406455
assert_equal "old-title", @m.read_attribute(:permalink)
@@ -410,8 +459,8 @@ def test_should_update_permalink_the_first_time_the_title_is_set
410459
end
411460

412461
def test_should_not_update_permalink_if_already_set_even_if_title_changed
413-
@m = ChangedModel.new
414-
@m.permalink = "old-permalink"
462+
@m = ChangedWithoutUpdateModel.new
463+
@m.permalink = "old permalink"
415464
@m.title = "new title"
416465
@m.validate
417466
assert_equal "old-permalink", @m.read_attribute(:permalink)

0 commit comments

Comments
 (0)