friendly_id4 4.0.0.pre → 4.0.0.pre3

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