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.
@@ -0,0 +1,4 @@
1
+ --files=*.md
2
+ --protected
3
+ --list-undoc
4
+ --exclude lib/friendly_id/migration
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
- human-friendly strings as if they were numeric ids for Active Record models.
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, 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).
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
- # add to Gemfile
50
- gem "friendly_id", "~> 4.0.0"
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
@@ -45,7 +45,7 @@ task :gem do
45
45
  end
46
46
 
47
47
  task :yard do
48
- puts %x{bundle exec yard doc --files=*.md}
48
+ puts %x{bundle exec yard}
49
49
  end
50
50
 
51
51
  task :bench do
@@ -1,11 +1,11 @@
1
1
  # encoding: utf-8
2
2
  $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
- require "friendly_id/version"
4
+ require "friendly_id"
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "friendly_id4"
8
- s.version = FriendlyId::Version::STRING
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"
@@ -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
- # FriendlyId is a comprehensive Ruby library for ActiveRecord permalinks and
8
- # slugs.
9
- #
10
- # @author Norman Clarke
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
- autoload :Slugged, "friendly_id/slugged"
13
- autoload :Scoped, "friendly_id/scoped"
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.friendly_id friendly_id}, the class
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
- end
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
@@ -1,31 +1,99 @@
1
1
  module FriendlyId
2
- # Class methods that will be added to ActiveRecord::Base.
2
+ # Class methods that will be added to model classes that extend {FriendlyId}.
3
3
  module Base
4
4
 
5
- # Configure FriendlyId for a model. Use this method to 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
- # @option options [Symbol] :use The name of an addon to use. By default, FriendlyId
14
- # provides {FriendlyId::Slugged :slugged}, {FriendlyId::History :history}
15
- # and {FriendlyId::Scoped :scoped}.
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
- attr_reader :base
28
+ attr_accessor :base
29
29
 
30
- # The model class that this configuration belongs to.
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
- @@defaults = {
39
- :reserved_words => ["new", "edit"]
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(klass, values = nil)
49
- @klass = klass
50
- @defaults = self.class.defaults.dup
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
- klass.send :include, FriendlyId.const_get(name.to_s.classify)
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
@@ -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