friendly_id4 4.0.0.beta4 → 4.0.0.beta5
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/.yardopts +4 -0
- data/README.md +21 -24
- data/Rakefile +1 -1
- data/friendly_id.gemspec +2 -2
- data/lib/friendly_id.rb +102 -8
- data/lib/friendly_id/base.rb +76 -8
- data/lib/friendly_id/configuration.rb +9 -27
- data/lib/friendly_id/finder_methods.rb +8 -0
- data/lib/friendly_id/history.rb +35 -0
- data/lib/friendly_id/migration.rb +1 -0
- data/lib/friendly_id/object_utils.rb +16 -6
- data/lib/friendly_id/reserved.rb +46 -0
- data/lib/friendly_id/scoped.rb +101 -19
- data/lib/friendly_id/slug.rb +3 -0
- data/lib/friendly_id/slug_sequencer.rb +3 -0
- data/lib/friendly_id/slugged.rb +180 -8
- data/lib/generators/friendly_id_generator.rb +3 -0
- data/test/base_test.rb +23 -0
- data/test/configuration_test.rb +6 -6
- data/test/core_test.rb +3 -11
- data/test/helper.rb +2 -2
- data/test/history_test.rb +9 -9
- data/test/object_utils_test.rb +3 -3
- data/test/reserved_test.rb +26 -0
- data/test/scoped_test.rb +8 -8
- data/test/shared.rb +15 -15
- data/test/slugged_test.rb +30 -20
- metadata +19 -18
- data/Guide.md +0 -363
- data/lib/friendly_id/version.rb +0 -9
data/.yardopts
ADDED
data/README.md
CHANGED
@@ -1,17 +1,8 @@
|
|
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).
|
5
|
-
|
6
|
-
For the current stable FriendlyId, please see:
|
7
|
-
|
8
|
-
[https://github.com/norman/friendly_id](https://github.com/norman/friendly_id_4)
|
9
|
-
<hr>
|
10
1
|
# FriendlyId
|
11
2
|
|
12
3
|
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
|
-
|
4
|
+
Ruby on Rails. It allows you to create pretty URL's and work with human-friendly
|
5
|
+
strings as if they were numeric ids for Active Record models.
|
15
6
|
|
16
7
|
Using FriendlyId, it's easy to make your application use URL's like:
|
17
8
|
|
@@ -24,20 +15,10 @@ instead of:
|
|
24
15
|
## FriendlyId Features
|
25
16
|
|
26
17
|
FriendlyId offers many advanced features, including: slug history and
|
27
|
-
versioning, scoped slugs, reserved words, custom slug generators
|
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).
|
18
|
+
versioning, scoped slugs, reserved words, and custom slug generators.
|
30
19
|
|
31
20
|
FriendlyId is compatible with Active Record **3.0** and **3.1**.
|
32
21
|
|
33
|
-
## Docs, Info and Support
|
34
|
-
|
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)
|
40
|
-
|
41
22
|
## Rails Quickstart
|
42
23
|
|
43
24
|
gem install friendly_id
|
@@ -46,8 +27,10 @@ FriendlyId is compatible with Active Record **3.0** and **3.1**.
|
|
46
27
|
|
47
28
|
cd my_app
|
48
29
|
|
49
|
-
#
|
50
|
-
|
30
|
+
# Add to Gemfile - this will change once version 4 is no longer
|
31
|
+
# in beta, but for now do this:
|
32
|
+
gem "friendly_id4", "4.0.0.beta4", :require => "friendly_id"
|
33
|
+
|
51
34
|
|
52
35
|
rails generate scaffold user name:string slug:string
|
53
36
|
|
@@ -68,6 +51,20 @@ FriendlyId is compatible with Active Record **3.0** and **3.1**.
|
|
68
51
|
|
69
52
|
GET http://localhost:3000/users/joe-schmoe
|
70
53
|
|
54
|
+
|
55
|
+
### Future Compatibility
|
56
|
+
|
57
|
+
FriendlyId will always remain compatible with the current release of Rails, and
|
58
|
+
at least one stable release behind. That means that support for 3.0.x will not be
|
59
|
+
dropped until a stable release of 3.2 is out, or possibly longer.
|
60
|
+
|
61
|
+
|
62
|
+
## Benchmarks
|
63
|
+
|
64
|
+
The latest benchmarks for FriendlyId are maintained
|
65
|
+
[here](https://gist.github.com/1129745).
|
66
|
+
|
67
|
+
|
71
68
|
## Bugs
|
72
69
|
|
73
70
|
Please report them on the [Github issue
|
data/Rakefile
CHANGED
data/friendly_id.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
3
|
|
4
|
-
require "friendly_id
|
4
|
+
require "friendly_id"
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "friendly_id4"
|
8
|
-
s.version = FriendlyId::
|
8
|
+
s.version = FriendlyId::VERSION
|
9
9
|
s.authors = ["Norman Clarke"]
|
10
10
|
s.email = ["norman@njclarke.com"]
|
11
11
|
s.homepage = "http://norman.github.com/friendly_id"
|
data/lib/friendly_id.rb
CHANGED
@@ -1,17 +1,94 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "thread"
|
1
3
|
require "friendly_id/base"
|
2
4
|
require "friendly_id/model"
|
3
5
|
require "friendly_id/object_utils"
|
4
6
|
require "friendly_id/configuration"
|
5
7
|
require "friendly_id/finder_methods"
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
=begin
|
10
|
+
|
11
|
+
== About FriendlyId
|
12
|
+
|
13
|
+
FriendlyId is an add-on to Ruby's Active Record that allows you to replace ids
|
14
|
+
in your URLs with strings:
|
15
|
+
|
16
|
+
# without FriendlyId
|
17
|
+
http://example.com/states/4323454
|
18
|
+
|
19
|
+
# with FriendlyId
|
20
|
+
http://example.com/states/washington
|
21
|
+
|
22
|
+
It requires few changes to your application code and offers flexibility,
|
23
|
+
performance and a well-documented codebase.
|
24
|
+
|
25
|
+
=== Concepts
|
26
|
+
|
27
|
+
Although FriendlyId helps with URLs, it does all of its work inside your models,
|
28
|
+
not your routes.
|
29
|
+
|
30
|
+
=== Simple Models
|
31
|
+
|
32
|
+
The simplest way to use FriendlyId is with a model that has a uniquely indexed
|
33
|
+
column with no spaces or special characters, and that is seldom or never
|
34
|
+
updated. The most common example of this is a user name:
|
35
|
+
|
36
|
+
class User < ActiveRecord::Base
|
37
|
+
extend FriendlyId
|
38
|
+
friendly_id :login
|
39
|
+
validates_format_of :login, :with => /\A[a-z0-9]+\z/i
|
40
|
+
end
|
41
|
+
|
42
|
+
@user = User.find "joe" # the old User.find(1) still works, too
|
43
|
+
@user.to_param # returns "joe"
|
44
|
+
redirect_to @user # the URL will be /users/joe
|
45
|
+
|
46
|
+
In this case, FriendlyId assumes you want to use the column as-is; it will never
|
47
|
+
modify the value of the column, and your application should ensure that the
|
48
|
+
value is admissible in a URL:
|
49
|
+
|
50
|
+
class City < ActiveRecord::Base
|
51
|
+
extend FriendlyId
|
52
|
+
friendly_id :name
|
53
|
+
end
|
54
|
+
|
55
|
+
@city.find "Viña del Mar"
|
56
|
+
redirect_to @city # the URL will be /cities/Viña%20del%20Mar
|
57
|
+
|
58
|
+
For this reason, it is often more convenient to use "slugs" rather than a single
|
59
|
+
column.
|
60
|
+
|
61
|
+
=== Slugged Models
|
62
|
+
|
63
|
+
FriendlyId can uses a separate column to store slugs for models which require
|
64
|
+
some processing of the friendly_id text. The most common example is a blog
|
65
|
+
post's title, which may have spaces, uppercase characters, or other attributes
|
66
|
+
you wish to modify to make them more suitable for use in URL's.
|
67
|
+
|
68
|
+
class Post < ActiveRecord::Base
|
69
|
+
extend FriendlyId
|
70
|
+
friendly_id :title, :use => :slugged
|
71
|
+
end
|
72
|
+
|
73
|
+
@post = Post.create(:title => "This is the first post!")
|
74
|
+
@post.friendly_id # returns "this-is-the-first-post"
|
75
|
+
redirect_to @post # the URL will be /posts/this-is-the-first-post
|
76
|
+
|
77
|
+
In general, use slugs by default unless you know for sure you don't need them.
|
78
|
+
|
79
|
+
@author Norman Clarke
|
80
|
+
=end
|
11
81
|
module FriendlyId
|
12
|
-
|
13
|
-
|
82
|
+
|
83
|
+
# The current version.
|
84
|
+
VERSION = "4.0.0.beta5"
|
85
|
+
|
86
|
+
@mutex = Mutex.new
|
87
|
+
|
14
88
|
autoload :History, "friendly_id/history"
|
89
|
+
autoload :Reserved, "friendly_id/reserved"
|
90
|
+
autoload :Scoped, "friendly_id/scoped"
|
91
|
+
autoload :Slugged, "friendly_id/slugged"
|
15
92
|
|
16
93
|
# FriendlyId takes advantage of `extended` to do basic model setup, primarily
|
17
94
|
# extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
|
@@ -20,7 +97,7 @@ module FriendlyId
|
|
20
97
|
# Previous versions of FriendlyId simply patched ActiveRecord::Base, but this
|
21
98
|
# version tries to be less invasive.
|
22
99
|
#
|
23
|
-
# In addition to adding {FriendlyId::Base
|
100
|
+
# In addition to adding {FriendlyId::Base#friendly_id friendly_id}, the class
|
24
101
|
# instance variable +@friendly_id_config+ is added. This variable is an
|
25
102
|
# instance of an anonymous subclass of {FriendlyId::Configuration}. This
|
26
103
|
# allows subsequently loaded modules like {FriendlyId::Slugged} and
|
@@ -39,7 +116,24 @@ module FriendlyId
|
|
39
116
|
base.instance_eval do
|
40
117
|
extend FriendlyId::Base
|
41
118
|
@friendly_id_config = Class.new(FriendlyId::Configuration).new(base)
|
119
|
+
if defaults = FriendlyId.defaults
|
120
|
+
defaults.yield @friendly_id_config
|
121
|
+
end
|
42
122
|
end
|
43
123
|
ActiveRecord::Relation.send :include, FriendlyId::FinderMethods
|
44
124
|
end
|
45
|
-
|
125
|
+
|
126
|
+
# Set global defaults for all models using FriendlyId.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
# FriendlyId.defaults do |config|
|
130
|
+
# config.base = :name
|
131
|
+
# config.use :slugged
|
132
|
+
# end
|
133
|
+
def self.defaults(&block)
|
134
|
+
@mutex.synchronize do
|
135
|
+
@defaults = block if block_given?
|
136
|
+
end
|
137
|
+
@defaults
|
138
|
+
end
|
139
|
+
end
|
data/lib/friendly_id/base.rb
CHANGED
@@ -1,31 +1,99 @@
|
|
1
1
|
module FriendlyId
|
2
|
-
# Class methods that will be added to
|
2
|
+
# Class methods that will be added to model classes that extend {FriendlyId}.
|
3
3
|
module Base
|
4
4
|
|
5
|
-
# Configure FriendlyId
|
6
|
-
# for your model.
|
5
|
+
# Configure FriendlyId's behavior in a model.
|
7
6
|
#
|
8
7
|
# class Post < ActiveRecord::Base
|
9
8
|
# extend FriendlyId
|
10
9
|
# friendly_id :title, :use => :slugged
|
11
10
|
# end
|
12
11
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# When given the optional block, this method will yield the class's instance
|
13
|
+
# of {FriendlyId::Configuration} to the block before evaluating other
|
14
|
+
# arguments, so configuration values set in the block may be overwritten by
|
15
|
+
# the arguments. This order was chosen to allow passing the same proc to
|
16
|
+
# multiple models, while being able to override the values it sets. Here
|
17
|
+
# is a contrived example:
|
18
|
+
#
|
19
|
+
# $friendly_id_config_proc = Proc.new do |config|
|
20
|
+
# config.base = :name
|
21
|
+
# config.use :slugged
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class Foo < ActiveRecord::Base
|
25
|
+
# extend FriendlyId
|
26
|
+
# friendly_id &$friendly_id_config_proc
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class Bar < ActiveRecord::Base
|
30
|
+
# extend FriendlyId
|
31
|
+
# friendly_id :title, &$friendly_id_config_proc
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# However, it's usually better to use {FriendlyId.defaults} for this:
|
35
|
+
#
|
36
|
+
# FriendlyId.defaults do |config|
|
37
|
+
# config.base = :name
|
38
|
+
# config.use :slugged
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# class Foo < ActiveRecord::Base
|
42
|
+
# extend FriendlyId
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# class Bar < ActiveRecord::Base
|
46
|
+
# extend FriendlyId
|
47
|
+
# friendly_id :title
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# In general you should use the block syntax either because of your personal
|
51
|
+
# aesthetic preference, or because you need to share some functionality
|
52
|
+
# between multiple models that can't be well encapsulated by
|
53
|
+
# {FriendlyId.defaults}.
|
54
|
+
#
|
55
|
+
# @option options [Symbol] :use The name of an addon to use. By default,
|
56
|
+
# FriendlyId provides {FriendlyId::Slugged :slugged},
|
57
|
+
# {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
|
58
|
+
# {FriendlyId::Scoped :scoped}.
|
59
|
+
#
|
60
|
+
# @option options [Array] :reserved_words Available when using +:reserved+,
|
61
|
+
# which is loaded by default. Sets an array of words banned for use as
|
62
|
+
# the basis of a friendly_id. By default this includes "edit" and "new".
|
63
|
+
#
|
64
|
+
# @option options [Symbol] :scope Available when using +:scoped+.
|
65
|
+
# Sets the relation or column used to scope generated friendly ids. This
|
66
|
+
# option has no default value.
|
67
|
+
#
|
68
|
+
# @option options [Symbol] :sequence_separator Available when using +:slugged+.
|
69
|
+
# Configures the sequence of characters used to separate a slug from a
|
70
|
+
# sequence. Defaults to +--+.
|
71
|
+
#
|
16
72
|
# @option options [Symbol] :slug_column Available when using +:slugged+.
|
17
73
|
# Configures the name of the column where FriendlyId will store the slug.
|
18
74
|
# Defaults to +:slug+.
|
75
|
+
#
|
76
|
+
# @option options [Symbol] :slug_sequencer_class Available when using +:slugged+.
|
77
|
+
# Sets the class used to generate unique slugs. You should not specify this
|
78
|
+
# unless you're doing some extensive hacking on FriendlyId. Defaults to
|
79
|
+
# {FriendlyId::SlugSequencer}.
|
80
|
+
#
|
81
|
+
# @yield Provides access to the model class's friendly_id_config, which
|
82
|
+
# allows an alternate configuration syntax, and conditional configuration
|
83
|
+
# logic.
|
84
|
+
#
|
85
|
+
# @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
|
19
86
|
def friendly_id(base = nil, options = {}, &block)
|
20
|
-
@friendly_id_config.use options.delete :use
|
21
|
-
@friendly_id_config.send :set, options.merge(:base => base)
|
22
87
|
yield @friendly_id_config if block_given?
|
88
|
+
@friendly_id_config.use options.delete :use
|
89
|
+
@friendly_id_config.send :set, base ? options.merge(:base => base) : options
|
23
90
|
before_save do |record|
|
24
91
|
record.instance_eval {@current_friendly_id = friendly_id}
|
25
92
|
end
|
26
93
|
include Model
|
27
94
|
end
|
28
95
|
|
96
|
+
# Returns the model class's {FriendlyId::Configuration friendly_id_config}.
|
29
97
|
def friendly_id_config
|
30
98
|
@friendly_id_config
|
31
99
|
end
|
@@ -25,39 +25,21 @@ module FriendlyId
|
|
25
25
|
# extend FriendlyId
|
26
26
|
# friendly_id :name
|
27
27
|
# end
|
28
|
-
|
28
|
+
attr_accessor :base
|
29
29
|
|
30
|
-
# The
|
31
|
-
# @return ActiveRecord::Base
|
32
|
-
attr_reader :klass
|
33
|
-
|
34
|
-
# The configuration parameters for the {#klass model class} using FriendlyId.
|
35
|
-
# @return Hash
|
30
|
+
# The default configuration options.
|
36
31
|
attr_reader :defaults
|
37
32
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# The default configuration parameters for models using FriendlyId.
|
43
|
-
# @return Hash
|
44
|
-
def self.defaults
|
45
|
-
@@defaults
|
46
|
-
end
|
33
|
+
# The model class that this configuration belongs to.
|
34
|
+
# @return ActiveRecord::Base
|
35
|
+
attr_reader :model_class
|
47
36
|
|
48
|
-
def initialize(
|
49
|
-
@
|
50
|
-
@defaults
|
37
|
+
def initialize(model_class, values = nil)
|
38
|
+
@model_class = model_class
|
39
|
+
@defaults = {}
|
51
40
|
set values
|
52
41
|
end
|
53
42
|
|
54
|
-
def base=(base)
|
55
|
-
@base = base
|
56
|
-
if @base.respond_to?(:to_s)
|
57
|
-
@klass.validates_exclusion_of @base, :in => defaults[:reserved_words]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
43
|
# Lets you specify the modules to use with FriendlyId.
|
62
44
|
#
|
63
45
|
# This method is invoked by {FriendlyId::Base#friendly_id friendly_id} when
|
@@ -74,7 +56,7 @@ module FriendlyId
|
|
74
56
|
# default FriendlyId provides +:slugged+, +:history+ and +:scoped+.
|
75
57
|
def use(*modules)
|
76
58
|
modules.to_a.flatten.compact.map do |name|
|
77
|
-
|
59
|
+
model_class.send :include, FriendlyId.const_get(name.to_s.classify)
|
78
60
|
end
|
79
61
|
end
|
80
62
|
|
@@ -4,6 +4,14 @@ module FriendlyId
|
|
4
4
|
|
5
5
|
protected
|
6
6
|
|
7
|
+
# FriendlyId overrides this method to make it possible to use friendly id's
|
8
|
+
# identically to numeric ids in finders.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# person = Person.find(123)
|
12
|
+
# person = Person.find("joe")
|
13
|
+
#
|
14
|
+
# @see FriendlyId::ObjectUtils
|
7
15
|
def find_one(id)
|
8
16
|
return super if !@klass.respond_to?(:friendly_id) || id.unfriendly_id?
|
9
17
|
where(@klass.friendly_id_config.query_field => id).first or super
|
data/lib/friendly_id/history.rb
CHANGED
@@ -1,8 +1,40 @@
|
|
1
1
|
require "friendly_id/slug"
|
2
2
|
|
3
3
|
module FriendlyId
|
4
|
+
|
5
|
+
=begin
|
6
|
+
FriendlyId can maintain a history of your record's older slugs, so if your
|
7
|
+
record's friendly_id changes, your URL's won't break.
|
8
|
+
|
9
|
+
class Post < ActiveRecord::Base
|
10
|
+
extend FriendlyId
|
11
|
+
friendly_id :title, :use => :history
|
12
|
+
end
|
13
|
+
|
14
|
+
class PostsController < ApplicationController
|
15
|
+
|
16
|
+
before_filter :find_post
|
17
|
+
|
18
|
+
...
|
19
|
+
def find_post
|
20
|
+
return unless params[:id]
|
21
|
+
@post = begin
|
22
|
+
Post.find params[:id]
|
23
|
+
rescue ActiveRecord::RecordNotFound
|
24
|
+
Post.find_by_friendly_id params[:id]
|
25
|
+
end
|
26
|
+
# If an old id or a numeric id was used to find the record, then
|
27
|
+
# the request path will not match the post_path, and we should do
|
28
|
+
# a 301 redirect that uses the current friendly_id
|
29
|
+
if request.path != post_path(@post)
|
30
|
+
return redirect_to @post, :status => :moved_permanently
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
=end
|
4
35
|
module History
|
5
36
|
|
37
|
+
# Configures the model instance to use the History add-on.
|
6
38
|
def self.included(klass)
|
7
39
|
klass.instance_eval do
|
8
40
|
raise "FriendlyId::History is incompatibe with FriendlyId::Scoped" if self < Scoped
|
@@ -21,7 +53,10 @@ module FriendlyId
|
|
21
53
|
end
|
22
54
|
end
|
23
55
|
|
56
|
+
# Adds a finder that explictly uses slugs from the slug table.
|
24
57
|
module Finder
|
58
|
+
|
59
|
+
# Search for a record in the slugs table using the specified slug.
|
25
60
|
def find_by_friendly_id(*args)
|
26
61
|
with_friendly_id(args.shift).first(*args)
|
27
62
|
end
|