friendly_id4 4.0.0.beta6 → 4.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +47 -68
- data/Rakefile +13 -108
- data/lib/friendly_id.rb +97 -117
- data/lib/friendly_id/scoped.rb +17 -116
- data/lib/friendly_id/slugged.rb +89 -183
- data/lib/friendly_id/test.rb +23 -0
- data/lib/friendly_id/test/generic.rb +84 -0
- data/lib/friendly_id/version.rb +9 -0
- data/test/core_test.rb +54 -16
- data/test/scoped_test.rb +39 -35
- data/test/slugged_test.rb +45 -64
- data/test/test_helper.rb +23 -0
- metadata +61 -125
- data/.gemtest +0 -0
- data/.gitignore +0 -11
- data/.yardopts +0 -4
- data/WhatsNew.md +0 -142
- data/bench.rb +0 -63
- data/friendly_id.gemspec +0 -31
- data/lib/friendly_id/base.rb +0 -134
- data/lib/friendly_id/configuration.rb +0 -78
- data/lib/friendly_id/finder_methods.rb +0 -20
- data/lib/friendly_id/history.rb +0 -64
- data/lib/friendly_id/migration.rb +0 -18
- data/lib/friendly_id/model.rb +0 -22
- data/lib/friendly_id/object_utils.rb +0 -40
- data/lib/friendly_id/reserved.rb +0 -46
- data/lib/friendly_id/slug.rb +0 -6
- data/lib/friendly_id/slug_sequencer.rb +0 -82
- data/lib/generators/friendly_id_generator.rb +0 -24
- data/test/base_test.rb +0 -54
- data/test/config/mysql.yml +0 -5
- data/test/config/mysql2.yml +0 -5
- data/test/config/postgres.yml +0 -6
- data/test/config/sqlite3.yml +0 -3
- data/test/configuration_test.rb +0 -27
- data/test/helper.rb +0 -90
- data/test/history_test.rb +0 -55
- data/test/object_utils_test.rb +0 -26
- data/test/reserved_test.rb +0 -26
- data/test/schema.rb +0 -56
- data/test/shared.rb +0 -118
- data/test/sti_test.rb +0 -48
data/friendly_id.gemspec
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
|
4
|
-
require "friendly_id"
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = "friendly_id4"
|
8
|
-
s.version = FriendlyId::VERSION
|
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 "maruku"
|
23
|
-
s.add_development_dependency "yard"
|
24
|
-
s.add_development_dependency "mocha"
|
25
|
-
|
26
|
-
s.description = <<-EOM
|
27
|
-
FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins
|
28
|
-
for Ruby on Rails. It allows you to create pretty URL's and work with
|
29
|
-
human-friendly strings as if they were numeric ids for ActiveRecord models.
|
30
|
-
EOM
|
31
|
-
end
|
data/lib/friendly_id/base.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# Class methods that will be added to model classes that extend {FriendlyId}.
|
3
|
-
module Base
|
4
|
-
|
5
|
-
# Configure FriendlyId's behavior in a model.
|
6
|
-
#
|
7
|
-
# class Post < ActiveRecord::Base
|
8
|
-
# extend FriendlyId
|
9
|
-
# friendly_id :title, :use => :slugged
|
10
|
-
# end
|
11
|
-
#
|
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 is
|
17
|
-
# 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
|
-
# === Order Method Calls in a Block vs Ordering Options
|
56
|
-
#
|
57
|
-
# When calling this method without a block, you may set the hash options in
|
58
|
-
# any order.
|
59
|
-
#
|
60
|
-
# However, when using block-style invocation, be sure to call
|
61
|
-
# FriendlyId::Configuration's {FriendlyId::Configuration#use use} method
|
62
|
-
# *prior* to the associated configuration options, because it will include
|
63
|
-
# modules into your class, and these modules in turn may add required
|
64
|
-
# configuration options to the +@friendly_id_configuraton+'s class:
|
65
|
-
#
|
66
|
-
# class Person < ActiveRecord::Base
|
67
|
-
# friendly_id do |config|
|
68
|
-
# # This will work
|
69
|
-
# config.use :slugged
|
70
|
-
# config.sequence_separator = ":"
|
71
|
-
# end
|
72
|
-
# end
|
73
|
-
#
|
74
|
-
# class Person < ActiveRecord::Base
|
75
|
-
# friendly_id do |config|
|
76
|
-
# # This will fail
|
77
|
-
# config.sequence_separator = ":"
|
78
|
-
# config.use :slugged
|
79
|
-
# end
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# @option options [Symbol] :use The name of an addon to use. By default,
|
83
|
-
# FriendlyId provides {FriendlyId::Slugged :slugged},
|
84
|
-
# {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
|
85
|
-
# {FriendlyId::Scoped :scoped}.
|
86
|
-
#
|
87
|
-
# @option options [Array] :reserved_words Available when using +:reserved+,
|
88
|
-
# which is loaded by default. Sets an array of words banned for use as
|
89
|
-
# the basis of a friendly_id. By default this includes "edit" and "new".
|
90
|
-
#
|
91
|
-
# @option options [Symbol] :scope Available when using +:scoped+.
|
92
|
-
# Sets the relation or column used to scope generated friendly ids. This
|
93
|
-
# option has no default value.
|
94
|
-
#
|
95
|
-
# @option options [Symbol] :sequence_separator Available when using +:slugged+.
|
96
|
-
# Configures the sequence of characters used to separate a slug from a
|
97
|
-
# sequence. Defaults to +--+.
|
98
|
-
#
|
99
|
-
# @option options [Symbol] :slug_column Available when using +:slugged+.
|
100
|
-
# Configures the name of the column where FriendlyId will store the slug.
|
101
|
-
# Defaults to +:slug+.
|
102
|
-
#
|
103
|
-
# @option options [Symbol] :slug_sequencer_class Available when using +:slugged+.
|
104
|
-
# Sets the class used to generate unique slugs. You should not specify this
|
105
|
-
# unless you're doing some extensive hacking on FriendlyId. Defaults to
|
106
|
-
# {FriendlyId::SlugSequencer}.
|
107
|
-
#
|
108
|
-
# @yield Provides access to the model class's friendly_id_config, which
|
109
|
-
# allows an alternate configuration syntax, and conditional configuration
|
110
|
-
# logic.
|
111
|
-
#
|
112
|
-
# @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
|
113
|
-
def friendly_id(base = nil, options = {}, &block)
|
114
|
-
yield @friendly_id_config if block_given?
|
115
|
-
@friendly_id_config.use options.delete :use
|
116
|
-
@friendly_id_config.send :set, base ? options.merge(:base => base) : options
|
117
|
-
before_save do |record|
|
118
|
-
record.instance_eval {@current_friendly_id = friendly_id}
|
119
|
-
end
|
120
|
-
include Model
|
121
|
-
end
|
122
|
-
|
123
|
-
# Returns the model class's {FriendlyId::Configuration friendly_id_config}.
|
124
|
-
# @note In the case of Single Table Inheritance (STI), this method will
|
125
|
-
# duplicate the parent class's FriendlyId::Configuration instance on first
|
126
|
-
# access. If you're concerned about thread safety, then be sure to invoke
|
127
|
-
# {#friendly_id} in your class for each model.
|
128
|
-
def friendly_id_config
|
129
|
-
@friendly_id_config or begin
|
130
|
-
@friendly_id_config = base_class.friendly_id_config.dup
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# The configuration paramters passed to +friendly_id+ will be stored in
|
3
|
-
# this object.
|
4
|
-
class Configuration
|
5
|
-
|
6
|
-
# The base column or method used by FriendlyId as the basis of a friendly id
|
7
|
-
# or slug.
|
8
|
-
#
|
9
|
-
# For models that don't use FriendlyId::Slugged, the base is the column that
|
10
|
-
# is used as the FriendlyId directly. For models using FriendlyId::Slugged,
|
11
|
-
# the base is a column or method whose value is used as the basis of the
|
12
|
-
# slug.
|
13
|
-
#
|
14
|
-
# For example, if you have a model representing blog posts and that uses
|
15
|
-
# slugs, you likely will want to use the "title" attribute as the base, and
|
16
|
-
# FriendlyId will take care of transforming the human-readable title into
|
17
|
-
# something suitable for use in a URL.
|
18
|
-
#
|
19
|
-
# @param [Symbol] A symbol referencing a column or method in the model. This
|
20
|
-
# value is usually set by passing it as the first argument to
|
21
|
-
# {FriendlyId::Base#friendly_id friendly_id}:
|
22
|
-
#
|
23
|
-
# @example
|
24
|
-
# class Book < ActiveRecord::Base
|
25
|
-
# extend FriendlyId
|
26
|
-
# friendly_id :name
|
27
|
-
# end
|
28
|
-
attr_accessor :base
|
29
|
-
|
30
|
-
# The default configuration options.
|
31
|
-
attr_reader :defaults
|
32
|
-
|
33
|
-
# The model class that this configuration belongs to.
|
34
|
-
# @return ActiveRecord::Base
|
35
|
-
attr_reader :model_class
|
36
|
-
|
37
|
-
def initialize(model_class, values = nil)
|
38
|
-
@model_class = model_class
|
39
|
-
@defaults = {}
|
40
|
-
set values
|
41
|
-
end
|
42
|
-
|
43
|
-
# Lets you specify the modules to use with FriendlyId.
|
44
|
-
#
|
45
|
-
# This method is invoked by {FriendlyId::Base#friendly_id friendly_id} when
|
46
|
-
# passing the +:use+ option, or when using {FriendlyId::Base#friendly_id
|
47
|
-
# friendly_id} with a block.
|
48
|
-
#
|
49
|
-
# @example
|
50
|
-
# class Book < ActiveRecord::Base
|
51
|
-
# extend FriendlyId
|
52
|
-
# friendly_id :name, :use => :slugged
|
53
|
-
# end
|
54
|
-
# @param [#to_s] *modules Arguments should be a symbols or strings that
|
55
|
-
# correspond with the name of a module inside the FriendlyId namespace. By
|
56
|
-
# default FriendlyId provides +:slugged+, +:history+ and +:scoped+.
|
57
|
-
def use(*modules)
|
58
|
-
modules.to_a.flatten.compact.map do |name|
|
59
|
-
model_class.send :include, FriendlyId.const_get(name.to_s.classify)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# The column that FriendlyId will use to find the record when querying by
|
64
|
-
# friendly id.
|
65
|
-
#
|
66
|
-
# This method is generally only used internally by FriendlyId.
|
67
|
-
# @return String
|
68
|
-
def query_field
|
69
|
-
base.to_s
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def set(values)
|
75
|
-
values and values.each {|name, value| self.send "#{name}=", value}
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# These methods will override the finder methods in ActiveRecord::Relation.
|
3
|
-
module FinderMethods
|
4
|
-
|
5
|
-
protected
|
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
|
15
|
-
def find_one(id)
|
16
|
-
return super if !@klass.respond_to?(:friendly_id) || id.unfriendly_id?
|
17
|
-
where(@klass.friendly_id_config.query_field => id).first or super
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/friendly_id/history.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require "friendly_id/slug"
|
2
|
-
|
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
|
35
|
-
module History
|
36
|
-
|
37
|
-
# Configures the model instance to use the History add-on.
|
38
|
-
def self.included(klass)
|
39
|
-
klass.instance_eval do
|
40
|
-
raise "FriendlyId::History is incompatibe with FriendlyId::Scoped" if self < Scoped
|
41
|
-
include Slugged unless self < Slugged
|
42
|
-
has_many :friendly_id_slugs, :as => :sluggable, :dependent => :destroy
|
43
|
-
before_save :build_friendly_id_slug, :if => lambda {|r| r.slug_sequencer.slug_changed?}
|
44
|
-
scope :with_friendly_id, lambda {|id| includes(:friendly_id_slugs).where("friendly_id_slugs.slug = ?", id)}
|
45
|
-
extend Finder
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def build_friendly_id_slug
|
52
|
-
self.friendly_id_slugs.build :slug => friendly_id
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
# Adds a finder that explictly uses slugs from the slug table.
|
57
|
-
module Finder
|
58
|
-
|
59
|
-
# Search for a record in the slugs table using the specified slug.
|
60
|
-
def find_by_friendly_id(*args)
|
61
|
-
with_friendly_id(args.shift).first(*args)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
class CreateFriendlyIdSlugs < ActiveRecord::Migration
|
2
|
-
|
3
|
-
def self.up
|
4
|
-
create_table :friendly_id_slugs do |t|
|
5
|
-
t.string :slug, :null => false
|
6
|
-
t.integer :sluggable_id, :null => false
|
7
|
-
t.string :sluggable_type, :limit => 40
|
8
|
-
t.datetime :created_at
|
9
|
-
end
|
10
|
-
add_index :friendly_id_slugs, :sluggable_id
|
11
|
-
add_index :friendly_id_slugs, [:slug, :sluggable_type], :unique => true
|
12
|
-
add_index :friendly_id_slugs, :sluggable_type
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.down
|
16
|
-
drop_table :friendly_id_slugs
|
17
|
-
end
|
18
|
-
end
|
data/lib/friendly_id/model.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# Instance methods that will be added to all classes using FriendlyId.
|
3
|
-
module Model
|
4
|
-
|
5
|
-
attr_reader :current_friendly_id
|
6
|
-
|
7
|
-
# Convenience method for accessing the class method of the same name.
|
8
|
-
def friendly_id_config
|
9
|
-
self.class.friendly_id_config
|
10
|
-
end
|
11
|
-
|
12
|
-
# Get the instance's friendly_id.
|
13
|
-
def friendly_id
|
14
|
-
send friendly_id_config.query_field
|
15
|
-
end
|
16
|
-
|
17
|
-
# Either the friendly_id, or the numeric id cast to a string.
|
18
|
-
def to_param
|
19
|
-
(friendly_id.present? ? friendly_id : id).to_s
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# Utility methods for determining whether any object is a friendly id.
|
3
|
-
#
|
4
|
-
# Monkey-patching Object is a somewhat extreme measure not to be taken lightly
|
5
|
-
# by libraries, but in this case I decided to do it because to me, it feels
|
6
|
-
# cleaner than adding a module method to {FriendlyId}. I've given the methods
|
7
|
-
# names that unambigously refer to the library of their origin, which should
|
8
|
-
# be sufficient to avoid conflicts with other libraries.
|
9
|
-
module ObjectUtils
|
10
|
-
|
11
|
-
# True is the id is definitely friendly, false if definitely unfriendly,
|
12
|
-
# else nil.
|
13
|
-
#
|
14
|
-
# An object is considired "definitely unfriendly" if its class is or
|
15
|
-
# inherits from Numeric, Symbol or ActiveRecord::Base.
|
16
|
-
#
|
17
|
-
# An object is considered "definitely friendly" if it responds to +to_i+,
|
18
|
-
# and its value when cast to an integer and then back to a string is
|
19
|
-
# different from its value when merely cast to a string:
|
20
|
-
#
|
21
|
-
# 123.friendly_id? #=> false
|
22
|
-
# "123".friendly_id? #=> nil
|
23
|
-
# "abc123".friendly_id? #=> true
|
24
|
-
def friendly_id?
|
25
|
-
if [Numeric, Symbol, ActiveRecord::Base].detect {|klass| self.class < klass}
|
26
|
-
false
|
27
|
-
elsif respond_to?(:to_i) && to_i.to_s != to_s
|
28
|
-
true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# True if the id is definitely unfriendly, false if definitely friendly,
|
33
|
-
# else nil.
|
34
|
-
def unfriendly_id?
|
35
|
-
val = friendly_id? ; !val unless val.nil?
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
Object.send :include, FriendlyId::ObjectUtils
|
data/lib/friendly_id/reserved.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
|
3
|
-
=begin
|
4
|
-
This module adds the ability to exlude a list of words from use as
|
5
|
-
FriendlyId slugs.
|
6
|
-
|
7
|
-
By default, FriendlyId reserves the words "new" and "edit" when this module
|
8
|
-
is included. You can configure this globally by using {FriendlyId.defaults FriendlyId.defaults}:
|
9
|
-
|
10
|
-
FriendlyId.defaults do |config|
|
11
|
-
config.use :reserved
|
12
|
-
# Reserve words for English and Spanish URLs
|
13
|
-
config.reserved_words = %w(new edit nueva nuevo editar)
|
14
|
-
end
|
15
|
-
=end
|
16
|
-
module Reserved
|
17
|
-
|
18
|
-
# When included, this module adds configuration options to the model class's
|
19
|
-
# friendly_id_config.
|
20
|
-
def self.included(model_class)
|
21
|
-
model_class.class_eval do
|
22
|
-
friendly_id_config.class.send :include, Reserved::Configuration
|
23
|
-
friendly_id_config.defaults[:reserved_words] ||= ["new", "edit"]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# This module adds the +:reserved_words+ configuration option to
|
28
|
-
# {FriendlyId::Configuration FriendlyId::Configuration}.
|
29
|
-
module Configuration
|
30
|
-
attr_writer :reserved_words
|
31
|
-
|
32
|
-
# Overrides {FriendlyId::Configuration#base} to add a validation to the
|
33
|
-
# model class.
|
34
|
-
def base=(base)
|
35
|
-
super
|
36
|
-
reserved_words = model_class.friendly_id_config.reserved_words
|
37
|
-
model_class.validates_exclusion_of base, :in => reserved_words
|
38
|
-
end
|
39
|
-
|
40
|
-
# An array of words forbidden as slugs.
|
41
|
-
def reserved_words
|
42
|
-
@reserved_words ||= @defaults[:reserved_words]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|