friendly_id 2.2.1 → 2.2.2

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.
data/History.txt CHANGED
@@ -1,3 +1,8 @@
1
+ == 2.2.2 2009-10-26
2
+
3
+ * 1 minor enhancement:
4
+ * Fixed Rake tasks creating duplicate slugs and not properly clearing cached slugs (closes GH issues #14 and #15)
5
+
1
6
  == 2.2.1 2009-10-23
2
7
 
3
8
  * 2 minor enhancements:
data/Manifest.txt CHANGED
@@ -16,13 +16,17 @@ lib/friendly_id/non_sluggable_instance_methods.rb
16
16
  lib/friendly_id/slug.rb
17
17
  lib/friendly_id/sluggable_class_methods.rb
18
18
  lib/friendly_id/sluggable_instance_methods.rb
19
+ lib/friendly_id/tasks.rb
19
20
  lib/friendly_id/version.rb
20
21
  lib/tasks/friendly_id.rake
21
22
  lib/tasks/friendly_id.rb
23
+ test/cached_slug_test.rb
22
24
  test/contest.rb
23
25
  test/custom_slug_normalizer_test.rb
24
26
  test/models/book.rb
27
+ test/models/city.rb
25
28
  test/models/country.rb
29
+ test/models/district.rb
26
30
  test/models/event.rb
27
31
  test/models/novel.rb
28
32
  test/models/person.rb
@@ -35,4 +39,5 @@ test/scoped_model_test.rb
35
39
  test/slug_test.rb
36
40
  test/slugged_model_test.rb
37
41
  test/sti_test.rb
42
+ test/tasks_test.rb
38
43
  test/test_helper.rb
@@ -0,0 +1,57 @@
1
+ module FriendlyId
2
+ class Tasks
3
+ class << self
4
+
5
+ def make_slugs(klass, options = {})
6
+ klass = parse_class_name(klass)
7
+ validate_uses_slugs(klass)
8
+ options = {:limit => 100, :include => :slugs, :order => "#{klass.table_name}.id ASC",
9
+ :conditions => "slugs.id IS NULL"}.merge(options)
10
+ while records = klass.find(:all, options) do
11
+ break if records.size == 0
12
+ records.each do |r|
13
+ r.save!
14
+ yield(r) if block_given?
15
+ end
16
+ end
17
+ end
18
+
19
+ def delete_slugs_for(klass)
20
+ klass = parse_class_name(klass)
21
+ validate_uses_slugs(klass)
22
+ Slug.destroy_all(["sluggable_type = ?", klass.to_s])
23
+ if klass.friendly_id_options[:cache_column]
24
+ klass.update_all("#{klass.friendly_id_options[:cache_column]} = NULL")
25
+ end
26
+ end
27
+
28
+ def delete_old_slugs(days = nil, class_name = nil)
29
+ days = days.blank? ? 45 : days.to_i
30
+ klass = class_name.blank? ? nil : parse_class_name(class_name.to_s)
31
+ conditions = ["created_at < ?", DateTime.now - days.days]
32
+ if klass
33
+ conditions[0] << " AND sluggable_type = ?"
34
+ conditions << klass.to_s
35
+ end
36
+ slugs = Slug.find :all, :conditions => conditions
37
+ slugs.each { |s| s.destroy unless s.is_most_recent? }
38
+ end
39
+
40
+ def parse_class_name(class_name)
41
+ return class_name if class_name.class == Class
42
+ if (class_name.split('::').size > 1)
43
+ class_name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
44
+ else
45
+ Object.const_get(class_name)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def validate_uses_slugs(klass)
52
+ raise "Class '%s' doesn't use slugs" % klass.to_s unless klass.friendly_id_options[:use_slug]
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -4,7 +4,7 @@ module FriendlyId #:nodoc:
4
4
  module Version #:nodoc:
5
5
  MAJOR = 2
6
6
  MINOR = 2
7
- TINY = 1
7
+ TINY = 2
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  end
10
10
  end
@@ -1,50 +1,27 @@
1
- # encoding: utf-8
1
+ require "friendly_id/tasks"
2
2
 
3
3
  namespace :friendly_id do
4
4
  desc "Make slugs for a model."
5
5
  task :make_slugs => :environment do
6
- raise 'USAGE: rake friendly_id:make_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
7
- if !sluggable_class.friendly_id_options[:use_slug]
8
- raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
9
- end
10
- while records = sluggable_class.find(:all, :include => :slugs, :conditions => "slugs.id IS NULL", :limit => 1000) do
11
- break if records.size == 0
12
- records.each do |r|
13
- r.send(:set_slug)
14
- r.save!
15
- puts "#{sluggable_class.to_s}(#{r.id}) friendly_id set to \"#{r.slug.name}\""
16
- end
6
+ validate_model_given
7
+ FriendlyId::Tasks.make_slugs(ENV["MODEL"]) do |r|
8
+ puts "%s(%d) friendly_id set to '%s'" % [r.class.to_s, r.id, r.slug.name]
17
9
  end
18
10
  end
19
11
 
20
12
  desc "Regenereate slugs for a model."
21
13
  task :redo_slugs => :environment do
22
- raise 'USAGE: rake friendly_id:redo_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
23
- if !sluggable_class.friendly_id_options[:use_slug]
24
- raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
25
- end
26
- Slug.destroy_all(["sluggable_type = ?", sluggable_class.to_s])
14
+ validate_model_given
15
+ FriendlyId::Tasks.delete_slugs_for(ENV["MODEL"])
27
16
  Rake::Task["friendly_id:make_slugs"].invoke
28
17
  end
29
18
 
30
- desc "Kill obsolete slugs older than 45 days."
19
+ desc "Kill obsolete slugs older than DAYS=45 days."
31
20
  task :remove_old_slugs => :environment do
32
- if ENV["DAYS"].nil?
33
- @days = 45
34
- else
35
- @days = ENV["DAYS"].to_i
36
- end
37
- slugs = Slug.find(:all, :conditions => ["created_at < ?", DateTime.now - @days.days])
38
- slugs.each do |s|
39
- s.destroy if !s.is_most_recent?
40
- end
21
+ FriendlyId::Task.delete_old_slugs(ENV["DAYS"], ENV["MODEL"])
41
22
  end
42
23
  end
43
24
 
44
- def sluggable_class
45
- if (ENV["MODEL"].split('::').size > 1)
46
- ENV["MODEL"].split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
47
- else
48
- Object.const_get(ENV["MODEL"])
49
- end
50
- end
25
+ def validate_model_given
26
+ raise 'USAGE: rake friendly_id:make_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
27
+ end
@@ -8,12 +8,15 @@ class CachedSlugModelTest < Test::Unit::TestCase
8
8
  context "A slugged model with a cached_slugs column" do
9
9
 
10
10
  setup do
11
- City.delete_all
12
- Slug.delete_all
13
11
  @paris = City.new(:name => "Paris")
14
12
  @paris.save!
15
13
  end
16
14
 
15
+ teardown do
16
+ City.delete_all
17
+ Slug.delete_all
18
+ end
19
+
17
20
  should "have a slug" do
18
21
  assert_not_nil @paris.slug
19
22
  end
@@ -8,6 +8,9 @@ class CustomSlugNormalizerTest < Test::Unit::TestCase
8
8
 
9
9
  setup do
10
10
  Thing.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:column => :name, :use_slug => true)
11
+ end
12
+
13
+ teardown do
11
14
  Thing.delete_all
12
15
  Slug.delete_all
13
16
  end
@@ -29,7 +32,7 @@ class CustomSlugNormalizerTest < Test::Unit::TestCase
29
32
  Thing.create!(:name => "test")
30
33
  end
31
34
  end
32
-
35
+
33
36
  end
34
37
 
35
- end
38
+ end
@@ -0,0 +1,4 @@
1
+ class City < ActiveRecord::Base
2
+ attr_accessible :name
3
+ has_friendly_id :name, :use_slug => true, :cache_column => 'my_slug'
4
+ end
@@ -0,0 +1,3 @@
1
+ class District < ActiveRecord::Base
2
+ has_friendly_id :name
3
+ end
@@ -7,10 +7,13 @@ class NonSluggedTest < Test::Unit::TestCase
7
7
  context "A non-slugged model with default FriendlyId options" do
8
8
 
9
9
  setup do
10
- User.delete_all
11
10
  @user = User.create!(:login => "joe", :email => "joe@example.org")
12
11
  end
13
12
 
13
+ teardown do
14
+ User.delete_all
15
+ end
16
+
14
17
  should "have friendly_id options" do
15
18
  assert_not_nil User.friendly_id_options
16
19
  end
@@ -95,4 +98,4 @@ class NonSluggedTest < Test::Unit::TestCase
95
98
 
96
99
  end
97
100
 
98
- end
101
+ end
@@ -7,15 +7,18 @@ class ScopedModelTest < Test::Unit::TestCase
7
7
  context "A slugged model that uses a scope" do
8
8
 
9
9
  setup do
10
- Person.delete_all
11
- Country.delete_all
12
- Slug.delete_all
13
10
  @usa = Country.create!(:name => "USA")
14
11
  @canada = Country.create!(:name => "Canada")
15
12
  @person = Person.create!(:name => "John Smith", :country => @usa)
16
13
  @person2 = Person.create!(:name => "John Smith", :country => @canada)
17
14
  end
18
15
 
16
+ teardown do
17
+ Person.delete_all
18
+ Country.delete_all
19
+ Slug.delete_all
20
+ end
21
+
19
22
  should "find all scoped records without scope" do
20
23
  assert_equal 2, Person.find(:all, @person.friendly_id).size
21
24
  end
@@ -50,4 +53,4 @@ class ScopedModelTest < Test::Unit::TestCase
50
53
 
51
54
  end
52
55
 
53
- end
56
+ end
data/test/slug_test.rb CHANGED
@@ -6,7 +6,7 @@ class SlugTest < Test::Unit::TestCase
6
6
 
7
7
  context "a slug" do
8
8
 
9
- setup do
9
+ teardown do
10
10
  Slug.delete_all
11
11
  Post.delete_all
12
12
  end
@@ -8,12 +8,15 @@ class SluggedModelTest < Test::Unit::TestCase
8
8
 
9
9
  setup do
10
10
  Post.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:column => :title, :use_slug => true)
11
+ @post = Post.new :title => "Test post", :content => "Test content", :published => true
12
+ @post.save!
13
+ end
14
+
15
+ teardown do
11
16
  Post.delete_all
12
17
  Person.delete_all
13
18
  Slug.delete_all
14
19
  Thing.delete_all
15
- @post = Post.new :title => "Test post", :content => "Test content", :published => true
16
- @post.save!
17
20
  end
18
21
 
19
22
  should "have friendly_id options" do
data/test/sti_test.rb CHANGED
@@ -8,12 +8,15 @@ class STIModelTest < Test::Unit::TestCase
8
8
 
9
9
  setup do
10
10
  Novel.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:column => :title, :use_slug => true)
11
- Novel.delete_all
12
- Slug.delete_all
13
11
  @novel = Novel.new :title => "Test novel"
14
12
  @novel.save!
15
13
  end
16
14
 
15
+ teardown do
16
+ Novel.delete_all
17
+ Slug.delete_all
18
+ end
19
+
17
20
  should "have a slug" do
18
21
  assert_not_nil @novel.slug
19
22
  end
@@ -0,0 +1,106 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require "friendly_id/tasks"
3
+ require "mocha"
4
+
5
+ class TasksTest < Test::Unit::TestCase
6
+
7
+ context "FriendlyId tasks" do
8
+
9
+ should "parse a top-level class name and return a class" do
10
+ assert_equal String, FriendlyId::Tasks.parse_class_name("String")
11
+ end
12
+
13
+ should "parse a namespaced class name and return a class" do
14
+ assert_equal Test::Unit, FriendlyId::Tasks.parse_class_name("Test::Unit")
15
+ end
16
+
17
+ end
18
+
19
+ context "The 'make slugs' task" do
20
+
21
+ setup do
22
+ City.create! :name => "Buenos Aires"
23
+ City.create! :name => "Rio de Janeiro"
24
+ City.create! :name => "Tokyo"
25
+ City.create! :name => "Nairobi"
26
+ Slug.delete_all
27
+ end
28
+
29
+ teardown do
30
+ City.delete_all
31
+ Slug.delete_all
32
+ end
33
+
34
+ should "make one slug per model" do
35
+ assert_equal 0, Slug.count
36
+ FriendlyId::Tasks.make_slugs("City")
37
+ assert_equal 4, Slug.count
38
+ end
39
+
40
+ end
41
+
42
+ context "The 'delete_slugs_for' task" do
43
+
44
+ setup do
45
+ @post = Post.create! :title => "Slugs Considered Harmful"
46
+ @city = City.create! :name => "Buenos Aires"
47
+ end
48
+
49
+ teardown do
50
+ Post.delete_all
51
+ City.delete_all
52
+ Slug.delete_all
53
+ end
54
+
55
+ should "Delete only slugs for the specified model" do
56
+ assert_equal 2, Slug.count
57
+ FriendlyId::Tasks.delete_slugs_for("City")
58
+ assert_equal 1, Slug.count
59
+ end
60
+
61
+ should "set the cached_slug column to NULL" do
62
+ FriendlyId::Tasks.delete_slugs_for("City")
63
+ @city.reload
64
+ assert_nil @city.my_slug
65
+ end
66
+
67
+ end
68
+
69
+ context "The 'delete_old_slugs' task" do
70
+
71
+ setup do
72
+ @post = Post.create! :title => "Slugs Considered Harmful"
73
+ @city = City.create! :name => "Buenos Aires"
74
+ City.connection.execute "UPDATE slugs SET created_at = '%s' WHERE id = %d" % [
75
+ 45.days.ago.strftime("%Y-%m-%d"), @city.slug.id]
76
+ @city.name = "Ciudad de Buenos Aires"
77
+ @city.save!
78
+ end
79
+
80
+ teardown do
81
+ Post.delete_all
82
+ City.delete_all
83
+ Slug.delete_all
84
+ end
85
+
86
+ should "delete slugs older than 45 days by default" do
87
+ assert_equal 3, Slug.count
88
+ FriendlyId::Tasks.delete_old_slugs
89
+ assert_equal 2, Slug.count
90
+ end
91
+
92
+ should "respect the days argument" do
93
+ assert_equal 3, Slug.count
94
+ FriendlyId::Tasks.delete_old_slugs(100)
95
+ assert_equal 3, Slug.count
96
+ end
97
+
98
+ should "respect the class argument" do
99
+ assert_equal 3, Slug.count
100
+ FriendlyId::Tasks.delete_old_slugs(1, "Post")
101
+ assert_equal 3, Slug.count
102
+ end
103
+
104
+ end
105
+
106
+ end
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: 2.2.1
4
+ version: 2.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Norman Clarke
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2009-10-23 00:00:00 -03:00
14
+ date: 2009-10-26 00:00:00 -03:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
@@ -96,13 +96,17 @@ files:
96
96
  - lib/friendly_id/slug.rb
97
97
  - lib/friendly_id/sluggable_class_methods.rb
98
98
  - lib/friendly_id/sluggable_instance_methods.rb
99
+ - lib/friendly_id/tasks.rb
99
100
  - lib/friendly_id/version.rb
100
101
  - lib/tasks/friendly_id.rake
101
102
  - lib/tasks/friendly_id.rb
103
+ - test/cached_slug_test.rb
102
104
  - test/contest.rb
103
105
  - test/custom_slug_normalizer_test.rb
104
106
  - test/models/book.rb
107
+ - test/models/city.rb
105
108
  - test/models/country.rb
109
+ - test/models/district.rb
106
110
  - test/models/event.rb
107
111
  - test/models/novel.rb
108
112
  - test/models/person.rb
@@ -115,6 +119,7 @@ files:
115
119
  - test/slug_test.rb
116
120
  - test/slugged_model_test.rb
117
121
  - test/sti_test.rb
122
+ - test/tasks_test.rb
118
123
  - test/test_helper.rb
119
124
  has_rdoc: true
120
125
  homepage: http://friendly-id.rubyforge.org/
@@ -153,3 +158,4 @@ test_files:
153
158
  - test/slug_test.rb
154
159
  - test/slugged_model_test.rb
155
160
  - test/sti_test.rb
161
+ - test/tasks_test.rb