friendly_id 4.0.0 → 4.0.1
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/.gitignore +1 -0
- data/.travis.yml +5 -5
- data/Changelog.md +11 -365
- data/Guide.rdoc +7 -6
- data/README.md +17 -11
- data/Rakefile +2 -2
- data/friendly_id.gemspec +11 -12
- data/gemfiles/Gemfile.rails-3.0.rb +15 -14
- data/gemfiles/Gemfile.rails-3.1.rb +16 -16
- data/gemfiles/Gemfile.rails-3.2.rb +16 -16
- data/lib/friendly_id.rb +2 -1
- data/lib/friendly_id/base.rb +1 -1
- data/lib/friendly_id/globalize.rb +102 -0
- data/lib/friendly_id/history.rb +24 -4
- data/lib/friendly_id/scoped.rb +13 -9
- data/lib/friendly_id/simple_i18n.rb +1 -1
- data/lib/friendly_id/slug_generator.rb +14 -6
- data/test/base_test.rb +4 -0
- data/test/configuration_test.rb +3 -1
- data/test/core_test.rb +3 -1
- data/test/generator_test.rb +0 -1
- data/test/helper.rb +0 -9
- data/test/history_test.rb +48 -3
- data/test/i18n_test.rb +1 -0
- data/test/object_utils_test.rb +4 -2
- data/test/schema.rb +3 -2
- data/test/scoped_test.rb +35 -9
- data/test/slugged_test.rb +7 -4
- data/test/sti_test.rb +11 -2
- metadata +43 -53
data/README.md
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
[](http://travis-ci.org/norman/friendly_id)
|
4
4
|
|
5
5
|
FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
|
6
|
-
Ruby on Rails. It allows you to create pretty
|
6
|
+
Ruby on Rails. It allows you to create pretty URLs and work with human-friendly
|
7
7
|
strings as if they were numeric ids for Active Record models.
|
8
8
|
|
9
|
-
Using FriendlyId, it's easy to make your application use
|
9
|
+
Using FriendlyId, it's easy to make your application use URLs like:
|
10
10
|
|
11
11
|
http://example.com/states/washington
|
12
12
|
|
@@ -18,9 +18,10 @@ instead of:
|
|
18
18
|
## FriendlyId Features
|
19
19
|
|
20
20
|
FriendlyId offers many advanced features, including: slug history and
|
21
|
-
versioning, i18n, scoped slugs, reserved words, and custom
|
21
|
+
versioning, i18n, Globalize support, scoped slugs, reserved words, and custom
|
22
|
+
slug generators.
|
22
23
|
|
23
|
-
FriendlyId is compatible with Active Record **3.0
|
24
|
+
FriendlyId is compatible with Active Record **3.0** and higher.
|
24
25
|
|
25
26
|
## Version 4.x
|
26
27
|
|
@@ -38,6 +39,8 @@ The best place to start is with the
|
|
38
39
|
[Guide](http://rubydoc.info/github/norman/friendly_id/master/file/Guide.rdoc),
|
39
40
|
which compiles the top-level RDocs into one outlined document.
|
40
41
|
|
42
|
+
You might also want to watch Ryan Bates's [Railscast on FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid).
|
43
|
+
|
41
44
|
## Rails Quickstart
|
42
45
|
|
43
46
|
gem install friendly_id
|
@@ -46,22 +49,22 @@ which compiles the top-level RDocs into one outlined document.
|
|
46
49
|
|
47
50
|
cd my_app
|
48
51
|
|
49
|
-
gem "friendly_id", "~> 4.0.
|
52
|
+
gem "friendly_id", "~> 4.0.1"
|
50
53
|
|
51
54
|
rails generate scaffold user name:string slug:string
|
52
55
|
|
53
56
|
# edit db/migrate/*_create_users.rb
|
54
|
-
add_index :users, :slug, :
|
57
|
+
add_index :users, :slug, unique: true
|
55
58
|
|
56
59
|
rake db:migrate
|
57
60
|
|
58
61
|
# edit app/models/user.rb
|
59
62
|
class User < ActiveRecord::Base
|
60
63
|
extend FriendlyId
|
61
|
-
friendly_id :name, :
|
64
|
+
friendly_id :name, use: :slugged
|
62
65
|
end
|
63
66
|
|
64
|
-
User.create! :
|
67
|
+
User.create! name: "Joe Schmoe"
|
65
68
|
|
66
69
|
rails server
|
67
70
|
|
@@ -87,13 +90,16 @@ tracker](http://github.com/norman/friendly_id/issues) for this project.
|
|
87
90
|
If you have a bug to report, please include the following information:
|
88
91
|
|
89
92
|
* **Version information for FriendlyId, Rails and Ruby.**
|
90
|
-
*
|
93
|
+
* Full stack trace and error message (if you have them).
|
91
94
|
* Any snippets of relevant model, view or controller code that shows how you
|
92
95
|
are using FriendlyId.
|
93
96
|
|
94
97
|
If you are able to, it helps even more if you can fork FriendlyId on Github,
|
95
98
|
and add a test that reproduces the error you are experiencing.
|
96
99
|
|
100
|
+
For more info on how to report bugs, please see [this
|
101
|
+
article](http://yourbugreportneedsmore.info/).
|
102
|
+
|
97
103
|
## Thanks and Credits
|
98
104
|
|
99
105
|
FriendlyId was originally created by Norman Clarke and Adrian Mugnolo, with
|
@@ -105,7 +111,7 @@ Part of the inspiration to rework FriendlyId came from Darcy Laycock's library
|
|
105
111
|
[Slugged](https://github.com/Sutto/slugged), which he was inspired to create
|
106
112
|
because of frustrations he experienced while using FriendlyId 3.x. Seeing a
|
107
113
|
smart programmer become frustrated with my code was enough of a kick in the
|
108
|
-
butt to make me want to improve this library
|
114
|
+
butt to make me want to significantly improve this library.
|
109
115
|
|
110
116
|
Many thanks to him for providing valid, real criticism while still being a cool
|
111
117
|
about it. I definitely recommend you check out his library if for some reason
|
@@ -122,7 +128,7 @@ should be!
|
|
122
128
|
|
123
129
|
## License
|
124
130
|
|
125
|
-
Copyright (c) 2008-
|
131
|
+
Copyright (c) 2008-2012 Norman Clarke, released under the MIT license.
|
126
132
|
|
127
133
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
128
134
|
this software and associated documentation files (the "Software"), to deal in
|
data/Rakefile
CHANGED
@@ -68,7 +68,7 @@ namespace :db do
|
|
68
68
|
driver = FriendlyId::Test::Database.driver
|
69
69
|
config = FriendlyId::Test::Database.config[driver]
|
70
70
|
commands = {
|
71
|
-
"mysql" => "mysql -e 'create database #{config["database"]};' >/dev/null",
|
71
|
+
"mysql" => "mysql -u #{config['username']} -e 'create database #{config["database"]};' >/dev/null",
|
72
72
|
"postgres" => "psql -c 'create database #{config['database']};' -U #{config['username']} >/dev/null"
|
73
73
|
}
|
74
74
|
%x{#{commands[driver] || true}}
|
@@ -80,7 +80,7 @@ namespace :db do
|
|
80
80
|
driver = FriendlyId::Test::Database.driver
|
81
81
|
config = FriendlyId::Test::Database.config[driver]
|
82
82
|
commands = {
|
83
|
-
"mysql" => "mysql -e 'drop database #{config["database"]};' >/dev/null",
|
83
|
+
"mysql" => "mysql -u #{config['username']} -e 'drop database #{config["database"]};' >/dev/null",
|
84
84
|
"postgres" => "psql -c 'drop database #{config['database']};' -U #{config['username']} >/dev/null"
|
85
85
|
}
|
86
86
|
%x{#{commands[driver] || true}}
|
data/friendly_id.gemspec
CHANGED
@@ -8,28 +8,27 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.version = FriendlyId::VERSION
|
9
9
|
s.authors = ["Norman Clarke"]
|
10
10
|
s.email = ["norman@njclarke.com"]
|
11
|
-
s.homepage = "http://
|
11
|
+
s.homepage = "http://github.com/norman/friendly_id"
|
12
12
|
s.summary = "A comprehensive slugging and pretty-URL plugin."
|
13
13
|
s.rubyforge_project = "friendly_id"
|
14
14
|
s.files = `git ls-files`.split("\n")
|
15
15
|
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
16
16
|
s.require_paths = ["lib"]
|
17
17
|
|
18
|
-
s.add_development_dependency "railties", "~> 3.
|
19
|
-
s.add_development_dependency "activerecord", "~> 3.
|
20
|
-
s.add_development_dependency "
|
21
|
-
s.add_development_dependency "
|
22
|
-
s.add_development_dependency "
|
23
|
-
s.add_development_dependency "
|
24
|
-
s.add_development_dependency "
|
25
|
-
s.add_development_dependency "
|
26
|
-
s.add_development_dependency "i18n", "~> 0.6.0"
|
18
|
+
s.add_development_dependency "railties", "~> 3.2.0"
|
19
|
+
s.add_development_dependency "activerecord", "~> 3.2.0"
|
20
|
+
s.add_development_dependency "minitest"
|
21
|
+
s.add_development_dependency "mocha"
|
22
|
+
s.add_development_dependency "maruku"
|
23
|
+
s.add_development_dependency "yard"
|
24
|
+
s.add_development_dependency "i18n"
|
25
|
+
s.add_development_dependency "ffaker"
|
27
26
|
s.add_development_dependency "simplecov"
|
28
27
|
|
29
28
|
s.description = <<-EOM
|
30
29
|
FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
|
31
|
-
Ruby on Rails. It allows you to create pretty
|
32
|
-
|
30
|
+
Ruby on Rails. It allows you to create pretty URLs and work with human-friendly
|
31
|
+
strings as if they were numeric ids for Active Record models.
|
33
32
|
EOM
|
34
33
|
|
35
34
|
s.post_install_message = <<-EOM
|
@@ -1,19 +1,20 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
platforms :jruby do
|
4
|
+
gem 'activerecord-jdbcsqlite3-adapter'
|
5
|
+
gem 'activerecord-jdbcmysql-adapter'
|
6
|
+
gem 'activerecord-jdbcpostgresql-adapter'
|
7
|
+
gem 'jruby-openssl'
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
gem
|
11
|
-
gem
|
12
|
-
gem
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2', '~> 0.2.0'
|
13
|
+
gem 'pg'
|
13
14
|
end
|
14
15
|
|
15
|
-
gem
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem
|
19
|
-
gem
|
16
|
+
gem 'activerecord', '~> 3.0.0'
|
17
|
+
gem 'railties', '~> 3.0.0'
|
18
|
+
gem 'minitest'
|
19
|
+
gem 'mocha'
|
20
|
+
gem 'rake'
|
@@ -1,21 +1,21 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
platforms :jruby do
|
4
|
+
gem 'activerecord-jdbcsqlite3-adapter'
|
5
|
+
gem 'activerecord-jdbcmysql-adapter'
|
6
|
+
gem 'activerecord-jdbcpostgresql-adapter'
|
7
|
+
gem 'jruby-openssl'
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2'
|
13
|
+
gem 'pg'
|
14
14
|
end
|
15
15
|
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem
|
19
|
-
gem
|
20
|
-
gem
|
21
|
-
gem
|
16
|
+
gem 'activerecord', '~> 3.1.0'
|
17
|
+
gem 'railties', '~> 3.1.3'
|
18
|
+
gem 'ffaker'
|
19
|
+
gem 'minitest'
|
20
|
+
gem 'mocha'
|
21
|
+
gem 'rake'
|
@@ -1,21 +1,21 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
platforms :jruby do
|
4
|
+
gem 'activerecord-jdbcsqlite3-adapter'
|
5
|
+
gem 'activerecord-jdbcmysql-adapter'
|
6
|
+
gem 'activerecord-jdbcpostgresql-adapter'
|
7
|
+
gem 'jruby-openssl'
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
gem
|
12
|
-
gem
|
13
|
-
gem
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2'
|
13
|
+
gem 'pg'
|
14
14
|
end
|
15
15
|
|
16
|
-
gem
|
17
|
-
gem
|
18
|
-
gem
|
19
|
-
gem
|
20
|
-
gem
|
21
|
-
gem
|
16
|
+
gem 'ffaker'
|
17
|
+
gem 'activerecord', '~> 3.2.0'
|
18
|
+
gem 'railties', '~> 3.2.0'
|
19
|
+
gem 'minitest'
|
20
|
+
gem 'mocha'
|
21
|
+
gem 'rake'
|
data/lib/friendly_id.rb
CHANGED
@@ -45,7 +45,7 @@ with numeric ids:
|
|
45
45
|
module FriendlyId
|
46
46
|
|
47
47
|
# The current version.
|
48
|
-
VERSION = "4.0.
|
48
|
+
VERSION = "4.0.1"
|
49
49
|
|
50
50
|
@mutex = Mutex.new
|
51
51
|
|
@@ -54,6 +54,7 @@ module FriendlyId
|
|
54
54
|
autoload :Reserved, "friendly_id/reserved"
|
55
55
|
autoload :Scoped, "friendly_id/scoped"
|
56
56
|
autoload :Slugged, "friendly_id/slugged"
|
57
|
+
autoload :Globalize, "friendly_id/globalize"
|
57
58
|
|
58
59
|
# FriendlyId takes advantage of `extended` to do basic model setup, primarily
|
59
60
|
# extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
|
data/lib/friendly_id/base.rb
CHANGED
@@ -9,7 +9,7 @@ method to configure your desired options:
|
|
9
9
|
|
10
10
|
class Foo < ActiveRecord::Base
|
11
11
|
extend FriendlyId
|
12
|
-
friendly_id bar, :use => [:slugged, :simple_i18n]
|
12
|
+
friendly_id :bar, :use => [:slugged, :simple_i18n]
|
13
13
|
end
|
14
14
|
|
15
15
|
The most important option is `:use`, which you use to tell FriendlyId which
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
|
5
|
+
=begin
|
6
|
+
|
7
|
+
== Translate slug db column using Globalize
|
8
|
+
|
9
|
+
The {FriendlyId::Globalize Globalize} module allow to use
|
10
|
+
Globalize (https://github.com/svenfuchs/globalize3) to translate slugs.
|
11
|
+
|
12
|
+
In order to use this module, your model must have a slug column and set the
|
13
|
+
field +slug+ translable with Globalize:
|
14
|
+
|
15
|
+
class Post < ActiveRecord::Base
|
16
|
+
translates :title, :slug
|
17
|
+
extend FriendlyId
|
18
|
+
friendly_id :title, :use => :globalize
|
19
|
+
end
|
20
|
+
|
21
|
+
=== Finds
|
22
|
+
|
23
|
+
Finds will take into consideration the current locale:
|
24
|
+
|
25
|
+
I18n.locale = :it
|
26
|
+
Post.find("guerre-stellari")
|
27
|
+
I18n.locale = :en
|
28
|
+
Post.find("star-wars")
|
29
|
+
|
30
|
+
To find a slug by an explicit locale, perform the find inside a block
|
31
|
+
passed to I18n's +with_locale+ method:
|
32
|
+
|
33
|
+
I18n.with_locale(:it) do
|
34
|
+
Post.find("guerre-stellari")
|
35
|
+
end
|
36
|
+
|
37
|
+
=== Creating Records
|
38
|
+
|
39
|
+
When new records are created, the slug is generated for the current locale only.
|
40
|
+
|
41
|
+
=== Translating Slugs
|
42
|
+
|
43
|
+
To translate an existing record's friendly_id, simply change locale and assign
|
44
|
+
+slug+ field:
|
45
|
+
|
46
|
+
I18n.with_locale(:it) do
|
47
|
+
post.slug = "guerre-stellari"
|
48
|
+
end
|
49
|
+
|
50
|
+
=end
|
51
|
+
module Globalize
|
52
|
+
|
53
|
+
def self.included(model_class)
|
54
|
+
model_class.instance_eval do
|
55
|
+
friendly_id_config.use :slugged
|
56
|
+
relation_class.send :include, FinderMethods
|
57
|
+
include Model
|
58
|
+
# Check if slug field is enabled to be translated with Globalize
|
59
|
+
if table_exists?
|
60
|
+
if columns.map(&:name).exclude?(friendly_id_config.query_field)
|
61
|
+
puts "\n[FriendlyId] Missing field '#{friendly_id_config.query_field}' in DB table '#{table_name}'. This is required for FriendlyId to properly function with the :globalize option.\n\n"
|
62
|
+
end
|
63
|
+
unless respond_to?('translated_attribute_names') || translated_attribute_names.exclude?(friendly_id_config.query_field.to_sym)
|
64
|
+
puts "\n[FriendlyId] You need to translate '#{friendly_id_config.query_field}' field with Globalize (add 'translates :#{friendly_id_config.query_field}' in your model '#{self.class.name}')\n\n"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module Model
|
71
|
+
def slug=(text)
|
72
|
+
set_slug(normalize_friendly_id(text))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
module FinderMethods
|
77
|
+
# FriendlyId overrides this method to make it possible to use friendly id's
|
78
|
+
# identically to numeric ids in finders.
|
79
|
+
#
|
80
|
+
# @example
|
81
|
+
# person = Person.find(123)
|
82
|
+
# person = Person.find("joe")
|
83
|
+
#
|
84
|
+
# @see FriendlyId::ObjectUtils
|
85
|
+
def find_one(id)
|
86
|
+
return super if id.unfriendly_id?
|
87
|
+
where(@klass.friendly_id_config.query_field => id).first or
|
88
|
+
joins(:translations).
|
89
|
+
where(translation_class.arel_table[:locale].eq(I18n.locale)).
|
90
|
+
where(translation_class.arel_table[@klass.friendly_id_config.query_field].eq(id)).first or
|
91
|
+
# if locale is not translated fallback to default locale
|
92
|
+
joins(:translations).
|
93
|
+
where(translation_class.arel_table[:locale].eq(I18n.default_locale)).
|
94
|
+
where(translation_class.arel_table[@klass.friendly_id_config.query_field].eq(id)).first or
|
95
|
+
super
|
96
|
+
end
|
97
|
+
|
98
|
+
protected :find_one
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/friendly_id/history.rb
CHANGED
@@ -65,20 +65,21 @@ method.
|
|
65
65
|
@friendly_id_config.use :slugged
|
66
66
|
has_many :slugs, :as => :sluggable, :dependent => :destroy,
|
67
67
|
:class_name => Slug.to_s, :order => "#{Slug.quoted_table_name}.id DESC"
|
68
|
-
|
68
|
+
after_save :create_slug
|
69
69
|
relation_class.send :include, FinderMethods
|
70
|
+
friendly_id_config.slug_generator_class.send :include, SlugGenerator
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
74
|
private
|
74
75
|
|
75
|
-
def
|
76
|
-
return
|
76
|
+
def create_slug
|
77
|
+
return if slugs.first.try(:slug) == friendly_id
|
77
78
|
# Allow reversion back to a previously used slug
|
78
79
|
relation = slugs.where(:slug => friendly_id)
|
79
80
|
result = relation.select("id").lock(true).all
|
80
81
|
relation.delete_all unless result.empty?
|
81
|
-
slugs.
|
82
|
+
slugs.create! :slug => friendly_id
|
82
83
|
end
|
83
84
|
|
84
85
|
# Adds a finder that explictly uses slugs from the slug table.
|
@@ -110,5 +111,24 @@ method.
|
|
110
111
|
yield sluggable_id if sluggable_id
|
111
112
|
end
|
112
113
|
end
|
114
|
+
|
115
|
+
# This module overrides {FriendlyId::SlugGenerator#conflicts} to consider
|
116
|
+
# all historic slugs for that model.
|
117
|
+
module SlugGenerator
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def conflicts
|
122
|
+
sluggable_class = friendly_id_config.model_class
|
123
|
+
pkey = sluggable_class.primary_key
|
124
|
+
value = sluggable.send pkey
|
125
|
+
# TODO this is a bit of a performance drain right now; optimize before next release.
|
126
|
+
scope = sluggable_class.unscoped.includes(:slugs).where("#{Slug.quoted_table_name}.slug = ? OR #{Slug.quoted_table_name}.slug LIKE ?", normalized, wildcard)
|
127
|
+
scope = scope.where(Slug.table_name => {:sluggable_type => sluggable_class.name})
|
128
|
+
scope = scope.where("#{sluggable_class.table_name}.#{pkey} <> ?", value) unless sluggable.new_record?
|
129
|
+
scope.order("LENGTH(#{Slug.quoted_table_name}.slug) DESC, #{Slug.quoted_table_name}.slug DESC")
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
113
133
|
end
|
114
134
|
end
|