friendly_id 1.9.9

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.
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!