friendly_id 3.3.3.0 → 4.0.0.beta7

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 (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
-