friendly_id4 4.0.0.pre → 4.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,74 +1,92 @@
1
- # FriendlyId 4
1
+ <hr>
2
+ **NOTE** This is FriendlyId4 - a rewrite of FriendlyId. For more info about this
3
+ rewrite, and the changes it brings, read [this
4
+ document](https://github.com/norman/friendly_id_4/blob/master/ABOUT.md).
2
5
 
3
- This is an in-progress rethink of the FriendlyId plugin. It will probably be
4
- released some time in August or September 2011, once I've had the chance to
5
- actually use it in a real website for a while.
6
+ For the current stable FriendlyId, please see:
6
7
 
7
- Please don't use this yet for anything real but feel free to try it and give
8
- your feedback via the issues tracker.
8
+ [https://github.com/norman/friendly_id](https://github.com/norman/friendly_id_4)
9
+ <hr>
10
+ # FriendlyId
9
11
 
10
- ## Back to basics
12
+ FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
13
+ Ruby on Rails. It allows you to create pretty URL's and work with
14
+ human-friendly strings as if they were numeric ids for Active Record models.
11
15
 
12
- This isn't the "big rewrite," it's the "small rewrite."
16
+ Using FriendlyId, it's easy to make your application use URL's like:
13
17
 
14
- Adding new features with each release is not sustainable. This release *removes*
15
- features, but makes it possible to add them back as addons. We can also remove
16
- some complexity by relying on the better default functionality provided by newer
17
- versions of Active Support and Active Record.
18
+ http://example.com/states/washington
18
19
 
19
- Let's see how small we can make this!
20
+ instead of:
20
21
 
21
- Here's what's changed:
22
+ http://example.com/states/4323454
22
23
 
23
- ## Active Record 3+ only
24
+ ## FriendlyId Features
24
25
 
25
- For 2.3 support, you can use FriendlyId 3, which will continue to be maintained
26
- until people don't want it any more.
26
+ FriendlyId offers many advanced features, including: slug history and
27
+ versioning, scoped slugs, reserved words, custom slug generators, and
28
+ excellent Unicode support. For complete information on using FriendlyId,
29
+ please see the [FriendlyId Guide](http://norman.github.com/friendly_id/file.Guide.html).
27
30
 
28
- ## Remove Babosa
31
+ FriendlyId is compatible with Active Record **3.0** and **3.1**.
29
32
 
30
- Babosa is FriendlyId 3's slugging library.
33
+ ## Docs, Info and Support
31
34
 
32
- FriendlyId 4 doesn't use it by default any more because the most important
33
- pieces of it were already accepted into Active Support 3. You can still just
34
- override `#normalize_friendly_id` in your model if you want to use Babosa.
35
+ * [FriendlyId Guide](http://norman.github.com/friendly_id/file.Guide.html)
36
+ * [API Docs](http://norman.github.com/friendly_id)
37
+ * [Google Group](http://groups.google.com/group/friendly_id)
38
+ * [Source Code](http://github.com/norman/friendly_id/)
39
+ * [Issue Tracker](http://github.com/norman/friendly_id/issues)
35
40
 
36
- ## In-table slugs
41
+ ## Rails Quickstart
37
42
 
38
- FriendlyId no longer creates a separate slugs table - it just stores the
39
- generated slug value in the model table, which is simpler, faster and what most
40
- people seem to want. Keeping slugs in a separate table is an optional add-on for
41
- FriendlyId 4 (not implemented yet).
43
+ gem install friendly_id
42
44
 
43
- ## No more finder status
45
+ rails new my_app
44
46
 
45
- FriendlyId 3 offered finder statuses to help you determine when an outdated
46
- or non-friendly id was used to find the record, so that you could decide whether
47
- to permanently redirect to the canonical URL. However, there's a simpler way to
48
- do that, so this feature has been removed:
47
+ cd my_app
49
48
 
50
- if request.path != person_path(@person)
51
- return redirect_to @person, :status => :moved_permanently
49
+ # add to Gemfile
50
+ gem "friendly_id", "~> 4.0.0"
51
+
52
+ rails generate scaffold user name:string slug:string
53
+
54
+ # edit db/migrate/*_create_users.rb
55
+ add_index :users, :slug, :unique => true
56
+
57
+ rake db:migrate
58
+
59
+ # edit app/models/user.rb
60
+ class User < ActiveRecord::Base
61
+ include FriendlyId::Slugged
62
+ has_friendly_id :name
52
63
  end
53
64
 
54
- ## No more multiple finds
65
+ User.create! :name => "Joe Schmoe"
66
+
67
+ rails server
68
+
69
+ GET http://localhost:3000/users/joe-schmoe
55
70
 
56
- Person.find "joe-schmoe" # Supported
57
- Person.find ["joe-schmoe", "john-doe"] # No longer supported
71
+ ## Bugs
58
72
 
59
- If you want find by more than one friendly id, build your own query:
73
+ Please report them on the [Github issue tracker](http://github.com/norman/friendly_id/issues)
74
+ for this project.
60
75
 
61
- Person.where(:slug => ["joe-schmoe", "john-doe"])
76
+ If you have a bug to report, please include the following information:
62
77
 
63
- This lets us do *far* less monkeypatching in Active Record.
78
+ * **Version information for FriendlyId, Rails and Ruby.**
79
+ * Stack trace and error message.
80
+ * Any snippets of relevant model, view or controller code that shows how your
81
+ are using FriendlyId.
64
82
 
65
- ## No more reserved words
83
+ If you are able to, it helps even more if you can fork FriendlyId on Github,
84
+ and add a test that reproduces the error you are experiencing.
66
85
 
67
- Rather than use a custom reserved words validator, use the validations provided
68
- by Active Record. FriendlyId still reserves "new" and "edit" by default to avoid
69
- routing problems.
86
+ ## Credits
70
87
 
71
- validates_exclusion_of :name, :in => ["bad", "word"]
88
+ FriendlyId was created by Norman Clarke, Adrian Mugnolo, and Emilio Tagua, and
89
+ has had significant contributions over the years from [many
90
+ volunteers](https://github.com/norman/friendly_id/contributors).
72
91
 
73
- You can configure the default words reserved by FriendlyId in
74
- `FriendlyId::Configuration::DEFAULTS[:reserved_words]`.
92
+ Copyright (c) 2008-2011 Norman Clarke, released under the MIT license.
data/Rakefile CHANGED
@@ -1,24 +1,20 @@
1
- require "rake"
2
- require "rake/testtask"
3
- require "rake/gempackagetask"
4
- require "rake/clean"
1
+ require "cutest"
5
2
 
6
3
  task :default => :test
4
+ task :test do
5
+ Cutest.run(Dir["test/*_test.rb"])
6
+ end
7
7
 
8
- CLEAN << "pkg" << "doc" << "coverage" << ".yardoc"
9
-
10
- Rake::TestTask.new(:test) { |t| t.pattern = "test/**/*test.rb" }
8
+ task :clean do
9
+ %x{rm -rf *.gem doc}
10
+ end
11
11
 
12
- begin
13
- require 'reek/rake/task'
14
- Reek::Rake::Task.new do |t|
15
- t.fail_on_error = false
16
- end
17
- rescue LoadError
12
+ task :gem do
13
+ %x{gem build friendly_id.gemspec}
18
14
  end
19
15
 
20
- gemspec = File.expand_path("../friendly_id.gemspec", __FILE__)
21
- if File.exists? gemspec
22
- Rake::GemPackageTask.new(eval(File.read("friendly_id.gemspec"))) { |pkg| }
16
+ task :yard do
17
+ %x{yard doc}
23
18
  end
24
19
 
20
+ task :doc => :yard
@@ -0,0 +1,71 @@
1
+ require File.expand_path("../test/helper", __FILE__)
2
+ require "ffaker"
3
+ require "friendly_id/migration"
4
+
5
+ N = 1000
6
+
7
+ migration do |m|
8
+ m.add_column :users, :slug, :string
9
+ m.add_index :users, :slug, :unique => true
10
+ end
11
+
12
+ migration do |m|
13
+ m.create_table :posts do |t|
14
+ t.string :name
15
+ t.string :slug
16
+ end
17
+ m.add_index :posts, :slug, :unique => true
18
+ end
19
+ CreateFriendlyIdSlugs.up
20
+
21
+
22
+ class Array
23
+ def rand
24
+ self[Kernel.rand(length)]
25
+ end
26
+ end
27
+
28
+ class User
29
+ include FriendlyId::Slugged
30
+ has_friendly_id :name
31
+ end
32
+
33
+ class Post < ActiveRecord::Base
34
+ include FriendlyId::History
35
+ has_friendly_id :name
36
+ end
37
+
38
+ USERS = []
39
+ BOOKS = []
40
+ POSTS = []
41
+
42
+ 100.times do
43
+ name = Faker::Name.name
44
+ USERS << (User.create! :name => name).friendly_id
45
+ POSTS << (Post.create! :name => name).friendly_id
46
+ BOOKS << (Book.create! :name => name).id
47
+ end
48
+
49
+ Benchmark.bmbm do |x|
50
+ x.report 'find (without FriendlyId)' do
51
+ N.times {Book.find BOOKS.rand}
52
+ end
53
+ x.report 'find (in-table slug)' do
54
+ N.times {User.find USERS.rand}
55
+ end
56
+ x.report 'find (external slug)' do
57
+ N.times {Post.find_by_friendly_id POSTS.rand}
58
+ end
59
+
60
+ x.report 'insert (without FriendlyId)' do
61
+ N.times {transaction {Book.create :name => Faker::Name.name}}
62
+ end
63
+
64
+ x.report 'insert (in-table-slug)' do
65
+ N.times {transaction {User.create :name => Faker::Name.name}}
66
+ end
67
+
68
+ x.report 'insert (external slug)' do
69
+ N.times {transaction {Post.create :name => Faker::Name.name}}
70
+ end
71
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "friendly_id/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "friendly_id4"
8
+ s.version = FriendlyId::Version::STRING
9
+ s.authors = ["Norman Clarke"]
10
+ s.email = ["norman@njclarke.com"]
11
+ s.homepage = "http://norman.github.com/friendly_id"
12
+ s.summary = "A comprehensive slugging and pretty-URL plugin."
13
+ s.rubyforge_project = "friendly_id"
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency "activerecord", "~> 3.0"
19
+ s.add_development_dependency "sqlite3", "~> 1.3"
20
+ s.add_development_dependency "cutest", "~> 1.1.2"
21
+ s.add_development_dependency "ffaker"
22
+ s.add_development_dependency "yard"
23
+
24
+ s.description = <<-EOM
25
+ FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins
26
+ for Ruby on Rails. It allows you to create pretty URL's and work with
27
+ human-friendly strings as if they were numeric ids for ActiveRecord models.
28
+ EOM
29
+ end
@@ -1,121 +1,14 @@
1
+ require "friendly_id/base"
2
+ require "friendly_id/model"
3
+ require "friendly_id/object_utils"
4
+ require "friendly_id/configuration"
5
+ require "friendly_id/finder_methods"
6
+
1
7
  # FriendlyId is a comprehensive Ruby library for ActiveRecord permalinks and
2
8
  # slugs.
3
9
  # @author Norman Clarke
4
10
  module FriendlyId
5
-
6
- autoload :Slugged, "friendly_id/slugged"
7
- autoload :Scoped, "friendly_id/scoped"
8
-
9
- # Class methods that will be added to ActiveRecord::Base.
10
- module Base
11
- extend self
12
-
13
- def has_friendly_id(*args)
14
- options = args.extract_options!
15
- base = args.shift
16
- friendly_id_config.set options.merge(:base => base)
17
- include Model
18
- # @NOTE: AR-specific code here
19
- validates_exclusion_of base, :in => Configuration::DEFAULTS[:reserved_words]
20
- before_save do |record|
21
- record.instance_eval {@_current_friendly_id = friendly_id}
22
- end
23
- end
24
-
25
- def friendly_id_config
26
- @friendly_id_config ||= Configuration.new(self)
27
- end
28
-
29
- def uses_friendly_id?
30
- !! @friendly_id_config
31
- end
32
- end
33
-
34
- # Instance methods that will be added to all classes using FriendlyId.
35
- module Model
36
-
37
- # Convenience method for accessing the class method of the same name.
38
- def friendly_id_config
39
- self.class.friendly_id_config
40
- end
41
-
42
- # Get the instance's friendly_id.
43
- def friendly_id
44
- send friendly_id_config.query_field
45
- end
46
-
47
- # Either the friendly_id, or the numeric id cast to a string.
48
- def to_param
49
- (friendly_id or id).to_s
50
- end
51
- end
52
-
53
- # The configuration paramters passed to +has_friendly_id+ will be stored
54
- # in this object.
55
- class Configuration
56
- attr_accessor :base
57
- attr_reader :klass
58
-
59
- DEFAULTS = {
60
- :config_error_message => 'FriendlyId has no such config option "%s"',
61
- :reserved_words => ["new", "edit"]
62
- }
63
-
64
- def initialize(klass, values = nil)
65
- @klass = klass
66
- set values
67
- end
68
-
69
- def method_missing(symbol, *args, &block)
70
- option = symbol.to_s.gsub(/=\z/, '')
71
- raise ArgumentError, DEFAULTS[:config_error_message] % option
72
- end
73
-
74
- def set(values)
75
- values and values.each {|name, value| self.send "#{name}=", value}
76
- end
77
-
78
- def query_field
79
- base
80
- end
81
- end
82
-
83
- # Utility methods that are in Object because it's impossible to predict what
84
- # kinds of objects get passed into FinderMethods#find_one and
85
- # Model#normalize_friendly_id.
86
- module ObjectUtils
87
-
88
- # True is the id is definitely friendly, false if definitely unfriendly,
89
- # else nil.
90
- def friendly_id?
91
- if kind_of?(Integer) or kind_of?(Symbol) or self.class.respond_to? :friendly_id_config
92
- false
93
- elsif to_i.to_s != to_s
94
- true
95
- end
96
- end
97
-
98
- # True if the id is definitely unfriendly, false if definitely friendly,
99
- # else nil.
100
- def unfriendly_id?
101
- val = friendly_id? ; !val unless val.nil?
102
- end
103
- end
104
-
105
- # These methods will override the finder methods in ActiveRecord::Relation.
106
- module FinderMethods
107
-
108
- protected
109
-
110
- # @NOTE AR-specific code here
111
- def find_one(id)
112
- return super if !@klass.uses_friendly_id? or id.unfriendly_id?
113
- where(@klass.friendly_id_config.query_field => id).first or super
114
- end
115
-
116
- end
11
+ autoload :Slugged, "friendly_id/slugged"
12
+ autoload :Scoped, "friendly_id/scoped"
13
+ autoload :History, "friendly_id/history"
117
14
  end
118
-
119
- ActiveRecord::Base.extend FriendlyId::Base
120
- ActiveRecord::Relation.send :include, FriendlyId::FinderMethods
121
- Object.send :include, FriendlyId::ObjectUtils
@@ -0,0 +1,29 @@
1
+ module FriendlyId
2
+ # Class methods that will be added to ActiveRecord::Base.
3
+ module Base
4
+ extend self
5
+
6
+ def has_friendly_id(*args)
7
+ options = args.extract_options!
8
+ base = args.shift
9
+ friendly_id_config.set options.merge(:base => base)
10
+ include Model
11
+ # @NOTE: AR-specific code here
12
+ validates_exclusion_of base, :in => Configuration::DEFAULTS[:reserved_words]
13
+ before_save do |record|
14
+ record.instance_eval {@current_friendly_id = friendly_id}
15
+ end
16
+ self
17
+ end
18
+
19
+ def friendly_id_config
20
+ @friendly_id_config ||= Configuration.new(self)
21
+ end
22
+
23
+ def uses_friendly_id?
24
+ defined? @friendly_id_config
25
+ end
26
+ end
27
+ end
28
+
29
+ ActiveRecord::Base.extend FriendlyId::Base
@@ -0,0 +1,31 @@
1
+ module FriendlyId
2
+ # The configuration paramters passed to +has_friendly_id+ will be stored
3
+ # in this object.
4
+ class Configuration
5
+ attr_accessor :base
6
+ attr_reader :klass
7
+
8
+ DEFAULTS = {
9
+ :config_error_message => 'FriendlyId has no such config option "%s"',
10
+ :reserved_words => ["new", "edit"]
11
+ }
12
+
13
+ def initialize(klass, values = nil)
14
+ @klass = klass
15
+ set values
16
+ end
17
+
18
+ def method_missing(symbol, *args, &block)
19
+ option = symbol.to_s.gsub(/=\z/, '')
20
+ raise ArgumentError, DEFAULTS[:config_error_message] % option
21
+ end
22
+
23
+ def set(values)
24
+ values and values.each {|name, value| self.send "#{name}=", value}
25
+ end
26
+
27
+ def query_field
28
+ base
29
+ end
30
+ end
31
+ end