friendly_id_globalize3 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/Changelog.md +354 -0
  2. data/Contributors.md +43 -0
  3. data/Guide.md +686 -0
  4. data/MIT-LICENSE +19 -0
  5. data/README.md +99 -0
  6. data/Rakefile +75 -0
  7. data/extras/README.txt +3 -0
  8. data/extras/bench.rb +40 -0
  9. data/extras/extras.rb +38 -0
  10. data/extras/prof.rb +19 -0
  11. data/extras/template-gem.rb +26 -0
  12. data/extras/template-plugin.rb +28 -0
  13. data/generators/friendly_id/friendly_id_generator.rb +30 -0
  14. data/generators/friendly_id/templates/create_slugs.rb +18 -0
  15. data/lib/friendly_id.rb +93 -0
  16. data/lib/friendly_id/active_record.rb +74 -0
  17. data/lib/friendly_id/active_record_adapter/configuration.rb +68 -0
  18. data/lib/friendly_id/active_record_adapter/finders.rb +148 -0
  19. data/lib/friendly_id/active_record_adapter/relation.rb +165 -0
  20. data/lib/friendly_id/active_record_adapter/simple_model.rb +63 -0
  21. data/lib/friendly_id/active_record_adapter/slug.rb +77 -0
  22. data/lib/friendly_id/active_record_adapter/slugged_model.rb +122 -0
  23. data/lib/friendly_id/active_record_adapter/tasks.rb +72 -0
  24. data/lib/friendly_id/configuration.rb +178 -0
  25. data/lib/friendly_id/datamapper.rb +5 -0
  26. data/lib/friendly_id/railtie.rb +22 -0
  27. data/lib/friendly_id/sequel.rb +5 -0
  28. data/lib/friendly_id/slug_string.rb +25 -0
  29. data/lib/friendly_id/slugged.rb +105 -0
  30. data/lib/friendly_id/status.rb +35 -0
  31. data/lib/friendly_id/test.rb +350 -0
  32. data/lib/friendly_id/version.rb +9 -0
  33. data/lib/generators/friendly_id_generator.rb +25 -0
  34. data/lib/tasks/friendly_id.rake +19 -0
  35. data/rails/init.rb +2 -0
  36. data/test/active_record_adapter/ar_test_helper.rb +150 -0
  37. data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
  38. data/test/active_record_adapter/cached_slug_test.rb +76 -0
  39. data/test/active_record_adapter/core.rb +138 -0
  40. data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
  41. data/test/active_record_adapter/custom_table_name_test.rb +22 -0
  42. data/test/active_record_adapter/default_scope_test.rb +30 -0
  43. data/test/active_record_adapter/optimistic_locking_test.rb +18 -0
  44. data/test/active_record_adapter/scoped_model_test.rb +119 -0
  45. data/test/active_record_adapter/simple_test.rb +76 -0
  46. data/test/active_record_adapter/slug_test.rb +34 -0
  47. data/test/active_record_adapter/slugged.rb +33 -0
  48. data/test/active_record_adapter/slugged_status_test.rb +28 -0
  49. data/test/active_record_adapter/sti_test.rb +22 -0
  50. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
  51. data/test/active_record_adapter/support/database.mysql.yml +4 -0
  52. data/test/active_record_adapter/support/database.postgres.yml +6 -0
  53. data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
  54. data/test/active_record_adapter/support/models.rb +104 -0
  55. data/test/active_record_adapter/tasks_test.rb +82 -0
  56. data/test/friendly_id_test.rb +96 -0
  57. data/test/test_helper.rb +13 -0
  58. metadata +193 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2010 Norman Clarke, Adrian Mugnolo and Emilio Tagua.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # FriendlyId
2
+
3
+ FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
4
+ Ruby on Rails. It allows you to create pretty URL's and work with
5
+ human-friendly strings as if they were numeric ids for Active Record models.
6
+
7
+ Using FriendlyId, it's easy to make your application use URL's like:
8
+
9
+ http://example.com/states/washington
10
+
11
+ instead of:
12
+
13
+ http://example.com/states/4323454
14
+
15
+ ## FriendlyId Features
16
+
17
+ FriendlyId offers many advanced features, including: slug history and
18
+ versioning, scoped slugs, reserved words, custom slug generators, and
19
+ excellent Unicode support. For complete information on using FriendlyId,
20
+ please see the [FriendlyId Guide](http://norman.github.com/friendly_id/file.Guide.html).
21
+
22
+ FriendlyId is compatible with Active Record **2.3.x** and **3.0**.
23
+
24
+ ## Docs, Info and Support
25
+
26
+ * [FriendlyId Guide](http://norman.github.com/friendly_id/file.Guide.html)
27
+ * [API Docs](http://norman.github.com/friendly_id)
28
+ * [Google Group](http://groups.google.com/group/friendly_id)
29
+ * [Source Code](http://github.com/norman/friendly_id/)
30
+ * [Issue Tracker](http://github.com/norman/friendly_id/issues)
31
+
32
+ ## Rails Quickstart
33
+
34
+ Note that the example below uses Rails 3. But don't worry: FriendlyId will
35
+ continue to support 2.3.x until Rails 3.1 is released.
36
+
37
+ gem install friendly_id
38
+
39
+ rails new my_app
40
+
41
+ cd my_app
42
+
43
+ # add to Gemfile
44
+ gem "friendly_id", "~> 3.2"
45
+
46
+ rails generate friendly_id
47
+ rails generate scaffold user name:string cached_slug:string
48
+
49
+ rake db:migrate
50
+
51
+ # edit app/models/user.rb
52
+ class User < ActiveRecord::Base
53
+ has_friendly_id :name, :use_slug => true
54
+ end
55
+
56
+ User.create! :name => "Joe Schmoe"
57
+
58
+ rails server
59
+
60
+ GET http://0.0.0.0:3000/users/joe-schmoe
61
+
62
+ ## Sequel and DataMapper, too
63
+
64
+ [Alex Coles](http://github.com/myabc) maintains an implemntation of
65
+ [FriendlyId for DataMapper](http://github.com/myabc/friendly_id_datamapper) that supports almost
66
+ all the features of the Active Record version.
67
+
68
+ Norman Clarke maintains an implementation of
69
+ [FriendlyId forSequel](http://github.com/norman/friendly_id_sequel) with some of the features
70
+ of the Active Record version.
71
+
72
+ ## Bugs
73
+
74
+ Please report them on the [Github issue tracker](http://github.com/norman/friendly_id/issues)
75
+ for this project.
76
+
77
+ If you have a bug to report, please include the following information:
78
+
79
+ * **Version information for FriendlyId, Rails and Ruby.**
80
+ * Stack trace and error message.
81
+ * Any snippets of relevant model, view or controller code that shows how your
82
+ are using FriendlyId.
83
+
84
+ If you are able to, it helps even more if you can fork FriendlyId on Github,
85
+ and add a test that reproduces the error you are experiencing.
86
+
87
+ ## Credits
88
+
89
+ FriendlyId was created by Norman Clarke, Adrian Mugnolo, and Emilio Tagua.
90
+
91
+ If you like FriendlyId, please recommend us on Working With Rails:
92
+
93
+ * [http://bit.ly/recommend-norman](http://bit.ly/recommend-norman)
94
+ * [http://bit.ly/recommend-emilio](http://bit.ly/recommend-emilio)
95
+ * [http://bit.ly/recommend-adrian](http://bit.ly/recommend-adrian)
96
+
97
+ Thanks!
98
+
99
+ Copyright (c) 2008-2010, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,75 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require "rake"
4
+ require "rake/testtask"
5
+ require "rake/gempackagetask"
6
+ require "rake/clean"
7
+
8
+ task :default => :test
9
+
10
+ CLEAN << "pkg" << "doc" << "coverage" << ".yardoc"
11
+ Rake::GemPackageTask.new(eval(File.read("friendly_id.gemspec"))) { |pkg| }
12
+
13
+ begin
14
+ require "yard"
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.options = ["--output-dir=doc"]
17
+ t.options << "--files" << ["Guide.md", "Contributors.md", "Changelog.md"].join(",")
18
+ end
19
+ rescue LoadError
20
+ end
21
+
22
+ begin
23
+ require "rcov/rcovtask"
24
+ Rcov::RcovTask.new do |r|
25
+ r.test_files = FileList["test/**/*_test.rb"]
26
+ r.verbose = true
27
+ r.rcov_opts << "--exclude gems/*"
28
+ end
29
+ rescue LoadError
30
+ end
31
+
32
+
33
+ Rake::TestTask.new(:test) { |t| t.pattern = "test/**/*_test.rb" }
34
+
35
+ namespace :test do
36
+ task :rails do
37
+ rm_rf "fid"
38
+ sh "rails --template extras/template-gem.rb fid"
39
+ sh "cd fid; rake test"
40
+ end
41
+ Rake::TestTask.new(:friendly_id) { |t| t.pattern = "test/*_test.rb" }
42
+ Rake::TestTask.new(:ar) { |t| t.pattern = "test/active_record_adapter/*_test.rb" }
43
+
44
+ desc "Test against lots of versions"
45
+ task :pre_release do
46
+ ["ree-1.8.7-2010.02", "ruby-1.9.2-p0"].each do |ruby|
47
+ ["sqlite3", "mysql", "postgres"].each do |driver|
48
+ [2, 3].each do |ar_version|
49
+ command = "rake-#{ruby} test AR=#{ar_version} DB=#{driver}"
50
+ puts command
51
+ puts `#{command}`
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ namespace :rails do
58
+ task :plugin do
59
+ rm_rf "fid"
60
+ sh "rails --template extras/template-plugin.rb fid"
61
+ sh "cd fid; rake test"
62
+ end
63
+ end
64
+ end
65
+
66
+ task :pushdocs do
67
+ branch = `git branch | grep "*"`.chomp.gsub("* ", "")
68
+ sh "git stash"
69
+ sh "git checkout gh-pages"
70
+ sh "cp -rp doc/* ."
71
+ sh 'git commit -a -m "Regenerated docs"'
72
+ sh "git push origin gh-pages"
73
+ sh "git checkout #{branch}"
74
+ sh "git stash apply"
75
+ end
data/extras/README.txt ADDED
@@ -0,0 +1,3 @@
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 ADDED
@@ -0,0 +1,40 @@
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 ADDED
@@ -0,0 +1,38 @@
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 ADDED
@@ -0,0 +1,19 @@
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"))
@@ -0,0 +1,26 @@
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
+ }
@@ -0,0 +1,28 @@
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
+ }
@@ -0,0 +1,30 @@
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
@@ -0,0 +1,18 @@
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
@@ -0,0 +1,93 @@
1
+ require "babosa"
2
+ require "forwardable"
3
+ require "friendly_id/slug_string"
4
+ require "friendly_id/configuration"
5
+ require "friendly_id/status"
6
+ require "friendly_id/slugged"
7
+
8
+ # FriendlyId is a comprehensive Ruby library for slugging and permalinks with
9
+ # ActiveRecord.
10
+ # @author Norman Clarke
11
+ # @author Emilio Tagua
12
+ # @author Adrian Mugnolo
13
+ module FriendlyId
14
+
15
+ # An error based on this class is raised when slug generation fails
16
+ class SlugGenerationError < StandardError ; end
17
+
18
+ # Raised when the slug text is blank.
19
+ class BlankError < SlugGenerationError ; end
20
+
21
+ # Raised when the slug text is reserved.
22
+ class ReservedError < SlugGenerationError ; end
23
+
24
+ module Base
25
+ # Set up a model to use a friendly_id. This method accepts a hash with
26
+ # {FriendlyId::Configuration several possible options}.
27
+ #
28
+ # @param [#to_sym] method The column or method that should be used as the
29
+ # basis of the friendly_id string.
30
+ #
31
+ # @param [Hash] options For valid configuration options, see
32
+ # {FriendlyId::Configuration}.
33
+ #
34
+ # @example
35
+ #
36
+ # class User < ActiveRecord::Base
37
+ # has_friendly_id :user_name
38
+ # end
39
+ #
40
+ # class Post < ActiveRecord::Base
41
+ # has_friendly_id :title, :use_slug => true, :approximate_ascii => true
42
+ # end
43
+ #
44
+ # @see FriendlyId::Configuration
45
+ def has_friendly_id(method, options = {})
46
+ raise NotImplementedError
47
+ end
48
+
49
+ # Does the model class use the FriendlyId plugin?
50
+ def uses_friendly_id?
51
+ respond_to? :friendly_id_config
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ class String
58
+ def parse_friendly_id(separator = nil)
59
+ separator ||= FriendlyId::Configuration::DEFAULTS[:sequence_separator]
60
+ name, sequence = split(/#{Regexp.escape(separator)}(\d+)?\z/)
61
+ return name, (sequence ||= 1).to_i
62
+ end
63
+ end
64
+
65
+ class Object
66
+
67
+ # Is the object a friendly id? Note that the return value here is
68
+ # +false+ if the +id+ is definitely not friendly, and +nil+ if it can
69
+ # not be determined.
70
+ # The return value will be:
71
+ # * +true+ - if the id is definitely friendly (i.e., a string with non-numeric characters)
72
+ # * +false+ - if the id is definitely unfriendly (i.e., an Integer, a model instance, etc.)
73
+ # * +nil+ - if it can not be determined (i.e., a numeric string like "206".)
74
+ # @return [true, false, nil]
75
+ # @see #unfriendly?
76
+ def friendly_id?
77
+ if kind_of?(Integer) or kind_of?(Symbol) or self.class.respond_to? :friendly_id_config
78
+ false
79
+ elsif to_i.to_s != to_s
80
+ true
81
+ end
82
+ end
83
+
84
+ # Is the object a numeric id?
85
+ # @return [true, false, nil] +true+ if definitely unfriendly, +false+ if
86
+ # definitely friendly, else +nil+.
87
+ # @see #friendly?
88
+ def unfriendly_id?
89
+ val = friendly_id? ; !val unless val.nil?
90
+ end
91
+ end
92
+
93
+ require "friendly_id/railtie" if defined?(Rails) && Rails.version >= "3"