friendly_id 2.2.1 → 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
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