friendly_id 3.3.3.0 → 4.0.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +24 -0
  3. data/.yardopts +4 -0
  4. data/Changelog.md +9 -10
  5. data/README.md +39 -48
  6. data/Rakefile +56 -58
  7. data/WhatsNew.md +95 -0
  8. data/bench.rb +63 -0
  9. data/friendly_id.gemspec +40 -0
  10. data/gemfiles/Gemfile.rails-3.0.rb +18 -0
  11. data/gemfiles/Gemfile.rails-3.0.rb.lock +52 -0
  12. data/gemfiles/Gemfile.rails-3.1.rb +18 -0
  13. data/gemfiles/Gemfile.rails-3.1.rb.lock +57 -0
  14. data/lib/friendly_id.rb +126 -80
  15. data/lib/friendly_id/active_record_adapter/relation.rb +10 -2
  16. data/lib/friendly_id/active_record_adapter/slugged_model.rb +3 -9
  17. data/lib/friendly_id/base.rb +132 -0
  18. data/lib/friendly_id/configuration.rb +65 -152
  19. data/lib/friendly_id/finder_methods.rb +20 -0
  20. data/lib/friendly_id/history.rb +88 -0
  21. data/lib/friendly_id/migration.rb +18 -0
  22. data/lib/friendly_id/model.rb +22 -0
  23. data/lib/friendly_id/object_utils.rb +40 -0
  24. data/lib/friendly_id/reserved.rb +46 -0
  25. data/lib/friendly_id/scoped.rb +131 -0
  26. data/lib/friendly_id/slug.rb +9 -0
  27. data/lib/friendly_id/slug_sequencer.rb +82 -0
  28. data/lib/friendly_id/slugged.rb +191 -76
  29. data/lib/friendly_id/version.rb +2 -2
  30. data/test/base_test.rb +54 -0
  31. data/test/configuration_test.rb +27 -0
  32. data/test/core_test.rb +30 -0
  33. data/test/databases.yml +19 -0
  34. data/test/helper.rb +88 -0
  35. data/test/history_test.rb +55 -0
  36. data/test/object_utils_test.rb +26 -0
  37. data/test/reserved_test.rb +26 -0
  38. data/test/schema.rb +59 -0
  39. data/test/scoped_test.rb +57 -0
  40. data/test/shared.rb +118 -0
  41. data/test/slugged_test.rb +83 -0
  42. data/test/sti_test.rb +48 -0
  43. metadata +110 -102
  44. data/Contributors.md +0 -46
  45. data/Guide.md +0 -626
  46. data/extras/README.txt +0 -3
  47. data/extras/bench.rb +0 -40
  48. data/extras/extras.rb +0 -38
  49. data/extras/prof.rb +0 -19
  50. data/extras/template-gem.rb +0 -26
  51. data/extras/template-plugin.rb +0 -28
  52. data/generators/friendly_id/friendly_id_generator.rb +0 -30
  53. data/generators/friendly_id/templates/create_slugs.rb +0 -18
  54. data/lib/tasks/friendly_id.rake +0 -19
  55. data/rails/init.rb +0 -2
  56. data/test/active_record_adapter/ar_test_helper.rb +0 -149
  57. data/test/active_record_adapter/basic_slugged_model_test.rb +0 -14
  58. data/test/active_record_adapter/cached_slug_test.rb +0 -76
  59. data/test/active_record_adapter/core.rb +0 -138
  60. data/test/active_record_adapter/custom_normalizer_test.rb +0 -20
  61. data/test/active_record_adapter/custom_table_name_test.rb +0 -22
  62. data/test/active_record_adapter/default_scope_test.rb +0 -30
  63. data/test/active_record_adapter/optimistic_locking_test.rb +0 -18
  64. data/test/active_record_adapter/scoped_model_test.rb +0 -129
  65. data/test/active_record_adapter/simple_test.rb +0 -76
  66. data/test/active_record_adapter/slug_test.rb +0 -34
  67. data/test/active_record_adapter/slugged.rb +0 -33
  68. data/test/active_record_adapter/slugged_status_test.rb +0 -28
  69. data/test/active_record_adapter/sti_test.rb +0 -22
  70. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +0 -2
  71. data/test/active_record_adapter/support/database.mysql.yml +0 -4
  72. data/test/active_record_adapter/support/database.mysql2.yml +0 -4
  73. data/test/active_record_adapter/support/database.postgres.yml +0 -6
  74. data/test/active_record_adapter/support/database.sqlite3.yml +0 -2
  75. data/test/active_record_adapter/support/models.rb +0 -104
  76. data/test/active_record_adapter/tasks_test.rb +0 -82
  77. data/test/compatibility/ancestry/Gemfile.lock +0 -34
  78. data/test/friendly_id_test.rb +0 -96
  79. data/test/test_helper.rb +0 -13
data/extras/README.txt DELETED
@@ -1,3 +0,0 @@
1
- These templates are here to generate FriendlyId-enabled Rails apps for
2
- testing. They are for developers, they are not intended for generating
3
- "real" applications.
data/extras/bench.rb DELETED
@@ -1,40 +0,0 @@
1
- $:.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
- $:.unshift File.expand_path(File.dirname(__FILE__))
3
- $:.uniq!
4
-
5
- require "extras"
6
- require 'rbench'
7
- FACTOR = 10
8
-
9
- RBench.run(TIMES) do
10
-
11
- column :times
12
- column :default
13
- column :no_slug
14
- column :slug
15
- column :cached_slug
16
-
17
- report 'find model by id', (TIMES * FACTOR).ceil do
18
- default { User.find(get_id) }
19
- no_slug { User.find(USERS.rand) }
20
- slug { Post.find(POSTS.rand) }
21
- cached_slug { District.find(DISTRICTS.rand) }
22
- end
23
-
24
- report 'find model using array of ids', (TIMES * FACTOR).ceil do
25
- default { User.find(get_id(2)) }
26
- no_slug { User.find(USERS.rand(2)) }
27
- slug { Post.find(POSTS.rand(2)) }
28
- cached_slug { District.find(DISTRICTS.rand(2)) }
29
- end
30
-
31
- report 'find model using id, then to_param', (TIMES * FACTOR).ceil do
32
- default { User.find(get_id).to_param }
33
- no_slug { User.find(USERS.rand).to_param }
34
- slug { Post.find(POSTS.rand).to_param }
35
- cached_slug { District.find(DISTRICTS.rand).to_param }
36
- end
37
-
38
- summary 'Total'
39
-
40
- end
data/extras/extras.rb DELETED
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env ruby -KU
2
- require File.dirname(__FILE__) + '/../test/test_helper'
3
- require File.dirname(__FILE__) + '/../test/active_record_adapter/ar_test_helper'
4
- require 'ffaker'
5
-
6
- TIMES = (ENV['N'] || 100).to_i
7
- POSTS = []
8
- DISTRICTS = []
9
- USERS = []
10
-
11
- User.delete_all
12
- Post.delete_all
13
- District.delete_all
14
- Slug.delete_all
15
-
16
- 100.times do
17
- name = Faker::Name.name
18
- USERS << (User.create! :name => name).friendly_id
19
- POSTS << (Post.create! :name => name).friendly_id
20
- DISTRICTS << (District.create! :name => name).friendly_id
21
- end
22
-
23
- def get_id(returns = 1)
24
- (1..100).to_a.rand(returns)
25
- end
26
-
27
- class Array
28
- def rand(returns = 1)
29
- @return = []
30
- returns.times do
31
- until @return.length == returns do
32
- val = self[Kernel.rand(length)]
33
- @return << val unless @return.include? val
34
- end
35
- end
36
- return returns == 1 ? @return.first : @return
37
- end
38
- end
data/extras/prof.rb DELETED
@@ -1,19 +0,0 @@
1
- $:.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
- $:.unshift File.expand_path(File.dirname(__FILE__))
3
- $:.uniq!
4
-
5
- require "extras"
6
- require 'ruby-prof'
7
-
8
- # RubyProf.measure_mode = RubyProf::MEMORY
9
- GC.disable
10
- RubyProf.start
11
- 100.times do
12
- Post.find(slug = POSTS.rand)
13
- end
14
- result = RubyProf.stop
15
- GC.enable
16
- # printer = RubyProf::CallTreePrinter.new(result)
17
- printer = RubyProf::GraphPrinter.new(result)
18
- version = ActiveRecord::VERSION::STRING.gsub(".", "")
19
- printer.print(File.new("prof#{version}.txt", "w"))
@@ -1,26 +0,0 @@
1
- run "rm public/index.html"
2
- gem "friendly_id"
3
- gem "haml"
4
- gem "will_paginate"
5
- run "haml --rails ."
6
- generate "friendly_id"
7
- generate :haml_scaffold, "post title:string"
8
- route "map.root :controller => 'posts', :action => 'index'"
9
- rake "db:migrate"
10
- rake "db:fixtures:load"
11
- file 'app/models/post.rb',
12
- %q{class Post < ActiveRecord::Base
13
- has_friendly_id :title, :use_slug => true
14
- end}
15
- file 'test/fixtures/slugs.yml',
16
- %q{
17
- one:
18
- name: mystring
19
- sequence: 1
20
- sluggable: one (Post)
21
-
22
- two:
23
- name: mystring
24
- sequence: 1
25
- sluggable: two (Post)
26
- }
@@ -1,28 +0,0 @@
1
- run "rm public/index.html"
2
- inside 'vendor/plugins' do
3
- run "git clone ../../../ friendly_id"
4
- end
5
- gem "haml"
6
- gem "will_paginate"
7
- run "haml --rails ."
8
- generate "friendly_id"
9
- generate :haml_scaffold, "post title:string"
10
- route "map.root :controller => 'posts', :action => 'index'"
11
- rake "db:migrate"
12
- rake "db:fixtures:load"
13
- file 'app/models/post.rb',
14
- %q{class Post < ActiveRecord::Base
15
- has_friendly_id :title, :use_slug => true
16
- end}
17
- file 'test/fixtures/slugs.yml',
18
- %q{
19
- one:
20
- name: mystring
21
- sequence: 1
22
- sluggable: one (Post)
23
-
24
- two:
25
- name: mystring
26
- sequence: 2
27
- sluggable: two (Post)
28
- }
@@ -1,30 +0,0 @@
1
- class FriendlyIdGenerator < Rails::Generator::Base
2
-
3
- RAKE_TASKS = File.join("..", "..", "..", "lib", "tasks", "friendly_id.rake")
4
-
5
- def manifest
6
- record do |m|
7
- unless options[:skip_migration]
8
- m.migration_template('create_slugs.rb', 'db/migrate', :migration_file_name => 'create_slugs')
9
- end
10
- unless options[:skip_tasks]
11
- m.directory "lib/tasks"
12
- m.file RAKE_TASKS, "lib/tasks/friendly_id.rake"
13
- end
14
- end
15
- end
16
-
17
- protected
18
-
19
- def add_options!(opt)
20
- opt.separator ''
21
- opt.separator 'Options:'
22
- opt.on("--skip-migration", "Don't generate a migration for the slugs table") do |value|
23
- options[:skip_migration] = value
24
- end
25
- opt.on("--skip-tasks", "Don't add friendly_id Rake tasks to lib/tasks") do |value|
26
- options[:skip_tasks] = value
27
- end
28
- end
29
-
30
- end
@@ -1,18 +0,0 @@
1
- class CreateSlugs < ActiveRecord::Migration
2
- def self.up
3
- create_table :slugs do |t|
4
- t.string :name
5
- t.integer :sluggable_id
6
- t.integer :sequence, :null => false, :default => 1
7
- t.string :sluggable_type, :limit => 40
8
- t.string :scope
9
- t.datetime :created_at
10
- end
11
- add_index :slugs, :sluggable_id
12
- add_index :slugs, [:name, :sluggable_type, :sequence, :scope], :name => "index_slugs_on_n_s_s_and_s", :unique => true
13
- end
14
-
15
- def self.down
16
- drop_table :slugs
17
- end
18
- end
@@ -1,19 +0,0 @@
1
- namespace :friendly_id do
2
- desc "Make slugs for a model."
3
- task :make_slugs => :environment do
4
- FriendlyId::TaskRunner.new.make_slugs do |record|
5
- puts "#{record.class}(#{record.id}): friendly_id set to '#{record.slug.name}'" if record.slug
6
- end
7
- end
8
-
9
- desc "Regenereate slugs for a model."
10
- task :redo_slugs => :environment do
11
- FriendlyId::TaskRunner.new.delete_slugs
12
- Rake::Task["friendly_id:make_slugs"].invoke
13
- end
14
-
15
- desc "Destroy obsolete slugs older than DAYS=45 days."
16
- task :remove_old_slugs => :environment do
17
- FriendlyId::TaskRunner.new.delete_old_slugs
18
- end
19
- end
data/rails/init.rb DELETED
@@ -1,2 +0,0 @@
1
- require "friendly_id"
2
- require "friendly_id/active_record"
@@ -1,149 +0,0 @@
1
- require File.expand_path('../../test_helper', __FILE__)
2
- require "logger"
3
- require "active_record"
4
- begin
5
- require "active_support/log_subscriber"
6
- rescue LoadError
7
- end
8
-
9
- # If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
10
- ActiveRecord::Base.logger = Logger.new($stdout) if ENV["LOG"]
11
-
12
- require "friendly_id/active_record"
13
- require File.expand_path("../../../generators/friendly_id/templates/create_slugs", __FILE__)
14
- require File.expand_path("../support/models", __FILE__)
15
- require File.expand_path('../core', __FILE__)
16
- require File.expand_path('../slugged', __FILE__)
17
-
18
- driver = (ENV["DB"] or "sqlite3").downcase
19
- db_yaml = File.expand_path("../support/database.#{driver}.yml", __FILE__)
20
- ActiveRecord::Base.establish_connection(YAML::load(File.open(db_yaml)))
21
-
22
- class ActiveRecord::Base
23
- def log_protected_attribute_removal(*args) end
24
- end
25
-
26
- ActiveRecord::Base.connection.tables.each do |table|
27
- ActiveRecord::Base.connection.drop_table(table)
28
- end
29
- ActiveRecord::Migration.verbose = false
30
- CreateSlugs.up
31
- CreateSupportModels.up
32
-
33
- # A model that uses the automagically configured "cached_slug" column
34
- class District < ActiveRecord::Base
35
- has_friendly_id :name, :use_slug => true
36
- before_save :say_hello
37
-
38
- def say_hello
39
- @said_hello = true
40
- end
41
- end
42
-
43
- # A model with optimistic locking enabled
44
- class Region < ActiveRecord::Base
45
- has_friendly_id :name, :use_slug => true
46
- after_create do |obj|
47
- other_instance = Region.find obj.id
48
- other_instance.update_attributes :note => name + "!"
49
- end
50
- end
51
-
52
- # A model that specifies a custom cached slug column
53
- class City < ActiveRecord::Base
54
- has_friendly_id :name, :use_slug => true, :cache_column => "my_slug"
55
- end
56
-
57
- # A model with a custom slug text normalizer
58
- class Person < ActiveRecord::Base
59
- has_friendly_id :name, :use_slug => true
60
-
61
- def normalize_friendly_id(string)
62
- string.upcase
63
- end
64
-
65
- end
66
-
67
- # A model that doesn't use FriendlyId
68
- class Unfriendly < ActiveRecord::Base
69
- end
70
-
71
- # A slugged model that uses a scope
72
- class Resident < ActiveRecord::Base
73
- belongs_to :country
74
- has_friendly_id :name, :use_slug => true, :scope => :country
75
- end
76
-
77
- # Like resident, but has a cached slug
78
- class Tourist < ActiveRecord::Base
79
- belongs_to :country
80
- has_friendly_id :name, :use_slug => true, :scope => :country
81
- end
82
-
83
- # A slugged model used as a scope
84
- class Country < ActiveRecord::Base
85
- has_many :people
86
- has_many :residents
87
- has_friendly_id :name, :use_slug => true
88
- end
89
-
90
- # A model that doesn't use slugs
91
- class User < ActiveRecord::Base
92
- has_friendly_id :name
93
- has_many :houses
94
- end
95
-
96
- # Another model that doesn"t use slugs
97
- class Author < ActiveRecord::Base
98
- has_friendly_id :name
99
- end
100
-
101
-
102
- # A model that uses a non-slugged model for its scope
103
- class House < ActiveRecord::Base
104
- belongs_to :user
105
- has_friendly_id :name, :use_slug => true, :scope => :user
106
- end
107
-
108
- # A model that uses default slug settings and has a named scope
109
- class Post < ActiveRecord::Base
110
- has_friendly_id :name, :use_slug => true
111
- scope :published, :conditions => { :published => true }
112
- end
113
-
114
- # Model that uses a custom table name
115
- class Place < ActiveRecord::Base
116
- self.table_name = "legacy_table"
117
- has_friendly_id :name, :use_slug => true
118
- end
119
-
120
- # A model that uses a datetime field for its friendly_id
121
- class Event < ActiveRecord::Base
122
- has_friendly_id :event_date, :use_slug => true
123
- end
124
-
125
- # A base model for single table inheritence
126
- class Book < ActiveRecord::Base ; end
127
-
128
- # A model that uses STI
129
- class Novel < ::Book
130
- has_friendly_id :name, :use_slug => true
131
- end
132
-
133
- # A model with no table
134
- class Question < ActiveRecord::Base
135
- has_friendly_id :name, :use_slug => true
136
- end
137
-
138
- # A model to test polymorphic associations
139
- class Site < ActiveRecord::Base
140
- belongs_to :owner, :polymorphic => true
141
- has_friendly_id :name, :use_slug => true
142
- end
143
-
144
- # A model used as a polymorphic owner
145
- class Company < ActiveRecord::Base
146
- has_many :sites, :as => :owner
147
- end
148
-
149
- puts "Using: #{RUBY_VERSION}, #{driver}, AR#{ENV["AR"] or 3}"
@@ -1,14 +0,0 @@
1
- require File.expand_path('../ar_test_helper', __FILE__)
2
-
3
- module FriendlyId
4
- module Test
5
- module ActiveRecordAdapter
6
- class BasicSluggedModelTest < ::Test::Unit::TestCase
7
- include FriendlyId::Test::Generic
8
- include FriendlyId::Test::Slugged
9
- include FriendlyId::Test::ActiveRecordAdapter::Slugged
10
- include FriendlyId::Test::ActiveRecordAdapter::Core
11
- end
12
- end
13
- end
14
- end
@@ -1,76 +0,0 @@
1
- require File.expand_path("../ar_test_helper", __FILE__)
2
-
3
- module FriendlyId
4
- module Test
5
- module ActiveRecordAdapter
6
-
7
- class CachedSlugTest < ::Test::Unit::TestCase
8
-
9
- include FriendlyId::Test::Generic
10
- include FriendlyId::Test::Slugged
11
- include FriendlyId::Test::ActiveRecordAdapter::Slugged
12
- include FriendlyId::Test::ActiveRecordAdapter::Core
13
-
14
- def klass
15
- District
16
- end
17
-
18
- def other_class
19
- Post
20
- end
21
-
22
- def cached_slug
23
- instance.send(cache_column)
24
- end
25
-
26
- def cache_column
27
- klass.friendly_id_config.cache_column
28
- end
29
-
30
- test "should have a cached_slug" do
31
- assert_equal cached_slug, instance.slug.to_friendly_id
32
- end
33
-
34
- test "should protect the cached slug value" do
35
- old_value = cached_slug
36
- instance.update_attributes(cache_column => "Madrid")
37
- instance.reload
38
- assert_equal old_value, cached_slug
39
- end
40
-
41
- test "should update the cached slug when updating the slug" do
42
- instance.update_attributes(:name => "new name")
43
- assert_equal instance.slug.to_friendly_id, cached_slug
44
- end
45
-
46
- test "should not update the cached slug column if it has not changed" do
47
- instance.note = "a note"
48
- instance.expects("#{cache_column}=".to_sym).never
49
- instance.save!
50
- end
51
-
52
- test "should cache the incremented sequence for duplicate slug names" do
53
- instance_2 = klass.create!(:name => instance.name)
54
- assert_match(/2\z/, instance_2.send(cache_column))
55
- end
56
-
57
- test "#friendly_id should check the cached value by default" do
58
- instance.expects(:slug).never
59
- instance.friendly_id
60
- end
61
-
62
- test "#friendly_id should skip the cache if invoked with true" do
63
- instance.expects(:slug)
64
- instance.friendly_id(true)
65
- end
66
-
67
- test "should not fire callbacks when updating slug cache" do
68
- instance.expects(:say_hello).once
69
- instance.update_attributes(:name => "new name")
70
- assert_equal instance.slug.to_friendly_id, cached_slug
71
- end
72
- end
73
- end
74
- end
75
- end
76
-