friendly_id 1.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data.tar.gz.sig +0 -0
  2. data/History.txt +81 -0
  3. data/MIT-LICENSE +19 -0
  4. data/Manifest.txt +53 -0
  5. data/README.rdoc +313 -0
  6. data/Rakefile +43 -0
  7. data/coverage/index.html +409 -0
  8. data/coverage/lib-friendly_id-non_sluggable_class_methods_rb.html +646 -0
  9. data/coverage/lib-friendly_id-non_sluggable_instance_methods_rb.html +638 -0
  10. data/coverage/lib-friendly_id-shoulda_macros_rb.html +641 -0
  11. data/coverage/lib-friendly_id-sluggable_class_methods_rb.html +714 -0
  12. data/coverage/lib-friendly_id-sluggable_instance_methods_rb.html +710 -0
  13. data/coverage/lib-friendly_id-string_helpers_rb.html +685 -0
  14. data/coverage/lib-friendly_id_rb.html +665 -0
  15. data/coverage/lib-slug_rb.html +695 -0
  16. data/coverage/rails-init_rb.html +606 -0
  17. data/friendly_id.gemspec +38 -0
  18. data/generators/friendly_id/friendly_id_generator.rb +12 -0
  19. data/generators/friendly_id/templates/create_slugs.rb +18 -0
  20. data/generators/friendly_id_20_upgrade/friendly_id_20_upgrade_generator.rb +11 -0
  21. data/generators/friendly_id_20_upgrade/templates/upgrade_friendly_id_to_20.rb +19 -0
  22. data/init.rb +1 -0
  23. data/lib/friendly_id.rb +61 -0
  24. data/lib/friendly_id/non_sluggable_class_methods.rb +41 -0
  25. data/lib/friendly_id/non_sluggable_instance_methods.rb +33 -0
  26. data/lib/friendly_id/shoulda_macros.rb +36 -0
  27. data/lib/friendly_id/slug.rb +90 -0
  28. data/lib/friendly_id/sluggable_class_methods.rb +109 -0
  29. data/lib/friendly_id/sluggable_instance_methods.rb +105 -0
  30. data/lib/friendly_id/version.rb +8 -0
  31. data/lib/tasks/friendly_id.rake +48 -0
  32. data/lib/tasks/friendly_id.rb +1 -0
  33. data/test/database.yml +3 -0
  34. data/test/fixtures/countries.yml +4 -0
  35. data/test/fixtures/country.rb +4 -0
  36. data/test/fixtures/people.yml +7 -0
  37. data/test/fixtures/person.rb +6 -0
  38. data/test/fixtures/post.rb +3 -0
  39. data/test/fixtures/posts.yml +19 -0
  40. data/test/fixtures/slugs.yml +45 -0
  41. data/test/fixtures/user.rb +3 -0
  42. data/test/fixtures/users.yml +7 -0
  43. data/test/non_slugged_test.rb +63 -0
  44. data/test/rails/2.x/app/controllers/application.rb +0 -0
  45. data/test/rails/2.x/config/boot.rb +109 -0
  46. data/test/rails/2.x/config/database.yml +3 -0
  47. data/test/rails/2.x/config/environment.rb +7 -0
  48. data/test/rails/2.x/config/environments/test.rb +6 -0
  49. data/test/rails/2.x/config/routes.rb +0 -0
  50. data/test/schema.rb +38 -0
  51. data/test/scoped_model_test.rb +21 -0
  52. data/test/slug_test.rb +87 -0
  53. data/test/sluggable_test.rb +181 -0
  54. data/test/test_helper.rb +35 -0
  55. metadata +155 -0
  56. metadata.gz.sig +1 -0
@@ -0,0 +1,105 @@
1
+ module FriendlyId::SluggableInstanceMethods
2
+
3
+ NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION = 2
4
+
5
+ attr :finder_slug
6
+ attr_accessor :finder_slug_name
7
+
8
+ def finder_slug
9
+ @finder_slug ||= init_finder_slug
10
+ end
11
+
12
+ # Was the record found using one of its friendly ids?
13
+ def found_using_friendly_id?
14
+ finder_slug
15
+ end
16
+
17
+ # Was the record found using its numeric id?
18
+ def found_using_numeric_id?
19
+ !found_using_friendly_id?
20
+ end
21
+
22
+ # Was the record found using an old friendly id?
23
+ def found_using_outdated_friendly_id?
24
+ finder_slug.id != slug.id
25
+ end
26
+
27
+ # Was the record found using an old friendly id, or its numeric id?
28
+ def has_better_id?
29
+ slug and found_using_numeric_id? || found_using_outdated_friendly_id?
30
+ end
31
+
32
+ # Returns the friendly id.
33
+ def friendly_id
34
+ slug(true).to_friendly_id
35
+ end
36
+ alias best_id friendly_id
37
+
38
+ # Has the basis of our friendly id changed, requiring the generation of a
39
+ # new slug?
40
+ def new_slug_needed?
41
+ !slug || slug_text != slug.name
42
+ end
43
+
44
+ # Returns the most recent slug, which is used to determine the friendly
45
+ # id.
46
+ def slug(reload = false)
47
+ @most_recent_slug = nil if reload
48
+ @most_recent_slug ||= slugs.first
49
+ end
50
+
51
+ # Returns the friendly id, or if none is available, the numeric id.
52
+ def to_param
53
+ slug ? slug.to_friendly_id : id.to_s
54
+ end
55
+
56
+ # Set the slug using the generated friendly id.
57
+ def set_slug
58
+ if self.class.friendly_id_options[:use_slug] && new_slug_needed?
59
+ @most_recent_slug = nil
60
+ slug_attributes = {:name => slug_text}
61
+ if friendly_id_options[:scope]
62
+ scope = send(friendly_id_options[:scope])
63
+ slug_attributes[:scope] = scope.respond_to?(:to_param) ? scope.to_param : scope.to_s
64
+ end
65
+ # If we're renaming back to a previously used friendly_id, delete the
66
+ # slug so that we can recycle the name without having to use a sequence.
67
+ slugs.find(:all, :conditions => {:name => slug_text, :scope => scope}).each { |s| s.destroy }
68
+ s = slugs.build slug_attributes
69
+ s.send(:set_sequence)
70
+ end
71
+ end
72
+
73
+ # Get the processed string used as the basis of the friendly id.
74
+ def slug_text
75
+ base = send friendly_id_options[:column]
76
+ if self.friendly_id_options[:strip_diacritics]
77
+ base = Slug::normalize(Slug::strip_diacritics(base))
78
+ else
79
+ base = Slug::normalize(base)
80
+ end
81
+ if base.length > friendly_id_options[:max_length]
82
+ base = base[0...friendly_id_options[:max_length]]
83
+ end
84
+ if friendly_id_options[:reserved].include?(base)
85
+ raise FriendlyId::SlugGenerationError.new("The slug text is a reserved value")
86
+ end
87
+ return base
88
+ end
89
+
90
+ private
91
+
92
+ def finder_slug=(finder_slug)
93
+ @finder_slug_name = finder_slug.name
94
+ slug = finder_slug
95
+ slug.sluggable = self
96
+ slug
97
+ end
98
+
99
+ def init_finder_slug
100
+ return false if !@finder_slug_name
101
+ slug = Slug.find(:first, :conditions => {:sluggable_id => id, :name => @finder_slug_name})
102
+ finder_slug = slug
103
+ end
104
+
105
+ end
@@ -0,0 +1,8 @@
1
+ module FriendlyId #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 9
5
+ TINY = 9
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -0,0 +1,48 @@
1
+ namespace :friendly_id do
2
+ desc "Make slugs for a model."
3
+ task :make_slugs => :environment do
4
+ raise 'USAGE: rake friendly_id:make_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
5
+ if !sluggable_class.friendly_id_options[:use_slug]
6
+ raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
7
+ end
8
+ while records = sluggable_class.find(:all, :include => :slugs, :conditions => "slugs.id IS NULL", :limit => 1000) do
9
+ break if records.size == 0
10
+ records.each do |r|
11
+ r.set_slug
12
+ r.save!
13
+ puts "#{sluggable_class.to_s}(#{r.id}) friendly_id set to \"#{r.slug.name}\""
14
+ end
15
+ end
16
+ end
17
+
18
+ desc "Regenereate slugs for a model."
19
+ task :redo_slugs => :environment do
20
+ raise 'USAGE: rake friendly_id:redo_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
21
+ if !sluggable_class.friendly_id_options[:use_slug]
22
+ raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
23
+ end
24
+ Slug.destroy_all(["sluggable_type = ?", sluggable_class.to_s])
25
+ Rake::Task["friendly_id:make_slugs"].invoke
26
+ end
27
+
28
+ desc "Kill obsolete slugs older than 45 days."
29
+ task :remove_old_slugs => :environment do
30
+ if ENV["DAYS"].nil?
31
+ @days = 45
32
+ else
33
+ @days = ENV["DAYS"].to_i
34
+ end
35
+ slugs = Slug.find(:all, :conditions => ["created_at < ?", DateTime.now - @days.days])
36
+ slugs.each do |s|
37
+ s.destroy if !s.is_most_recent?
38
+ end
39
+ end
40
+ end
41
+
42
+ def sluggable_class
43
+ if (ENV["MODEL"].split('::').size > 1)
44
+ ENV["MODEL"].split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
45
+ else
46
+ Object.const_get(ENV["MODEL"])
47
+ end
48
+ end
@@ -0,0 +1 @@
1
+ load 'tasks/friendly_id.rake'
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,4 @@
1
+ argentina:
2
+ name: Argentina
3
+ usa:
4
+ name: USA
@@ -0,0 +1,4 @@
1
+ class Country < ActiveRecord::Base
2
+ has_many :people
3
+ has_friendly_id :name, :use_slug => true
4
+ end
@@ -0,0 +1,7 @@
1
+ john_smith:
2
+ name: John Smith
3
+ country: argentina
4
+
5
+ john_smith2:
6
+ name: John Smith
7
+ country: usa
@@ -0,0 +1,6 @@
1
+ class Person < ActiveRecord::Base
2
+
3
+ belongs_to :country
4
+ has_friendly_id :name, :use_slug => true, :scope => :country
5
+
6
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ has_friendly_id :name, :use_slug => true, :reserved => ['new', 'edit']
3
+ end
@@ -0,0 +1,19 @@
1
+ without_slug:
2
+ name: Without a slug
3
+ content: Content without a slug
4
+
5
+ with_one_slug:
6
+ name: With one slug
7
+ content: Content with one slug
8
+
9
+ with_two_slugs:
10
+ name: With two slugs
11
+ content: Content with two slugs
12
+
13
+ common_title:
14
+ name: Common Title
15
+ content: A post with a very common title
16
+
17
+ common_title2:
18
+ name: Common Title
19
+ content: A second post with a very common title
@@ -0,0 +1,45 @@
1
+ one:
2
+ name: with-one-slug
3
+ sluggable: with_one_slug
4
+ sluggable_type: Post
5
+ sequence: 1
6
+
7
+ two_old:
8
+ name: with-two-slugs
9
+ sluggable: with_two_slugs (Post)
10
+ sequence: 1
11
+
12
+ two_new:
13
+ name: with-two-slugs-new
14
+ sluggable: with_two_slugs (Post)
15
+ sequence: 1
16
+
17
+ common_title:
18
+ name: common-title
19
+ sluggable: common_title (Post)
20
+ sequence: 1
21
+
22
+ common_title2:
23
+ name: common-title
24
+ sluggable: common_title2 (Post)
25
+ sequence: 2
26
+
27
+ john_smith:
28
+ name: john-smith
29
+ sluggable: john_smith (Person)
30
+ sequence: 1
31
+ scope: argentina
32
+
33
+ john_smith2:
34
+ name: john-smith
35
+ sluggable: john_smith2 (Person)
36
+ sequence: 1
37
+ scope: usa
38
+
39
+ argentina:
40
+ name: argentina
41
+ sluggable: argentina (Country)
42
+
43
+ usa:
44
+ name: usa
45
+ sluggable: usa (Country)
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_friendly_id :login
3
+ end
@@ -0,0 +1,7 @@
1
+ joe:
2
+ login: joe
3
+ email: joe@example.org
4
+
5
+ jane:
6
+ login: jane
7
+ email: jane@example.org
@@ -0,0 +1,63 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class NonSluggedTest < Test::Unit::TestCase
4
+
5
+ fixtures :users
6
+
7
+ def setup
8
+ end
9
+
10
+ def test_should_find_user_using_friendly_id
11
+ assert User.find(users(:joe).friendly_id)
12
+ end
13
+
14
+ def test_to_param_should_return_a_string
15
+ assert_equal String, users(:joe).to_param.class
16
+ end
17
+
18
+ def test_should_find_users_using_friendly_ids
19
+ assert_equal 2, User.find([users(:joe).friendly_id, users(:jane).friendly_id]).length
20
+ end
21
+
22
+ def test_should_not_find_users_using_non_existent_friendly_ids
23
+ assert_raises ActiveRecord::RecordNotFound do
24
+ User.find(['non-existent-slug', 'yet-another-non-existent-slug'])
25
+ end
26
+ end
27
+
28
+ def test_finder_options_are_not_ignored
29
+ assert_raises ActiveRecord::RecordNotFound do
30
+ User.find(users(:joe).friendly_id, :conditions => "1 = 2")
31
+ end
32
+ end
33
+
34
+ def test_user_should_have_friendly_id_options
35
+ assert_not_nil User.friendly_id_options
36
+ end
37
+
38
+ def test_user_should_not_be_found_using_friendly_id_unless_it_really_was
39
+ @user = User.new
40
+ assert !@user.found_using_friendly_id?
41
+ end
42
+
43
+ def test_user_should_be_considered_found_by_numeric_id_as_default
44
+ @user = User.new
45
+ assert @user.found_using_numeric_id?
46
+ end
47
+
48
+ def test_user_should_indicate_if_it_was_found_using_numeric_id
49
+ @user = User.find(users(:joe).id)
50
+ assert @user.found_using_numeric_id?
51
+ end
52
+
53
+ def test_user_should_indicate_if_it_was_found_using_friendly_id
54
+ @user = User.find(users(:joe).friendly_id)
55
+ assert @user.found_using_friendly_id?
56
+ end
57
+
58
+ def test_should_indicate_there_is_a_better_id_if_found_by_numeric_id
59
+ @user = User.find(users(:joe).id)
60
+ assert @user.has_better_id?
61
+ end
62
+
63
+ end
File without changes
@@ -0,0 +1,109 @@
1
+ # Don't change this file!
2
+ # Configure your app in config/environment.rb and config/environments/*.rb
3
+
4
+ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
5
+
6
+ module Rails
7
+ class << self
8
+ def boot!
9
+ unless booted?
10
+ preinitialize
11
+ pick_boot.run
12
+ end
13
+ end
14
+
15
+ def booted?
16
+ defined? Rails::Initializer
17
+ end
18
+
19
+ def pick_boot
20
+ (vendor_rails? ? VendorBoot : GemBoot).new
21
+ end
22
+
23
+ def vendor_rails?
24
+ File.exist?("#{RAILS_ROOT}/vendor/rails")
25
+ end
26
+
27
+ def preinitialize
28
+ load(preinitializer_path) if File.exist?(preinitializer_path)
29
+ end
30
+
31
+ def preinitializer_path
32
+ "#{RAILS_ROOT}/config/preinitializer.rb"
33
+ end
34
+ end
35
+
36
+ class Boot
37
+ def run
38
+ load_initializer
39
+ Rails::Initializer.run(:set_load_path)
40
+ end
41
+ end
42
+
43
+ class VendorBoot < Boot
44
+ def load_initializer
45
+ require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
46
+ Rails::Initializer.run(:install_gem_spec_stubs)
47
+ end
48
+ end
49
+
50
+ class GemBoot < Boot
51
+ def load_initializer
52
+ self.class.load_rubygems
53
+ load_rails_gem
54
+ require 'initializer'
55
+ end
56
+
57
+ def load_rails_gem
58
+ if version = self.class.gem_version
59
+ gem 'rails', version
60
+ else
61
+ gem 'rails'
62
+ end
63
+ rescue Gem::LoadError => load_error
64
+ $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
65
+ exit 1
66
+ end
67
+
68
+ class << self
69
+ def rubygems_version
70
+ Gem::RubyGemsVersion rescue nil
71
+ end
72
+
73
+ def gem_version
74
+ if defined? RAILS_GEM_VERSION
75
+ RAILS_GEM_VERSION
76
+ elsif ENV.include?('RAILS_GEM_VERSION')
77
+ ENV['RAILS_GEM_VERSION']
78
+ else
79
+ parse_gem_version(read_environment_rb)
80
+ end
81
+ end
82
+
83
+ def load_rubygems
84
+ require 'rubygems'
85
+ min_version = '1.3.1'
86
+ unless rubygems_version >= min_version
87
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
88
+ exit 1
89
+ end
90
+
91
+ rescue LoadError
92
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
93
+ exit 1
94
+ end
95
+
96
+ def parse_gem_version(text)
97
+ $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
98
+ end
99
+
100
+ private
101
+ def read_environment_rb
102
+ File.read("#{RAILS_ROOT}/config/environment.rb")
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ # All that for this:
109
+ Rails.boot!