permalink 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +78 -0
- data/Rakefile +31 -0
- data/init.rb +1 -0
- data/lib/permalink.rb +93 -0
- data/lib/permalink/string_ext.rb +11 -0
- data/lib/permalink/version.rb +10 -0
- data/test/models/beer.rb +3 -0
- data/test/models/donut.rb +3 -0
- data/test/models/post.rb +5 -0
- data/test/models/user.rb +3 -0
- data/test/permalink_test.rb +110 -0
- data/test/schema.rb +18 -0
- data/test/test_helper.rb +9 -0
- metadata +80 -0
data/README.markdown
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
has_permalink
|
2
|
+
=============
|
3
|
+
|
4
|
+
Instalation
|
5
|
+
-----------
|
6
|
+
|
7
|
+
You can use I18n-JS as plugin and gem. Choose what's best for you!
|
8
|
+
|
9
|
+
script/plugin install git://github.com/fnando/has_permalink.git
|
10
|
+
|
11
|
+
or
|
12
|
+
gem install permalink
|
13
|
+
|
14
|
+
Usage
|
15
|
+
-----
|
16
|
+
|
17
|
+
Add the method call `has_permalink` to your model. Your model should have a `permalink` attribute.
|
18
|
+
|
19
|
+
class Page < ActiveRecord::Base
|
20
|
+
has_permalink :title
|
21
|
+
end
|
22
|
+
|
23
|
+
You can specify the permalink field:
|
24
|
+
|
25
|
+
class page < ActiveRecord::Base
|
26
|
+
has_permalink :title, :to => :title_permalink
|
27
|
+
end
|
28
|
+
|
29
|
+
If you don't want to use `has_permalink`, you can call `'some text'.to_permalink` string method and
|
30
|
+
manage the permalink process by yourself.
|
31
|
+
|
32
|
+
Permalinks are not unique by default. `has_permalink` overrides `to_param` as following:
|
33
|
+
|
34
|
+
def to_param
|
35
|
+
"#{id}-#{permalink}"
|
36
|
+
end
|
37
|
+
|
38
|
+
You can define the `to_param` format:
|
39
|
+
|
40
|
+
class Page < ActiveRecord::Base
|
41
|
+
has_permalink :title, :to_param => %w(id permalink page)
|
42
|
+
end
|
43
|
+
|
44
|
+
The above settings will generate something link `100-some-title-page`. By overriding `to_param` method you don't have to change a thing on your app routes.
|
45
|
+
|
46
|
+
If you want to generate unique permalink, use the option `:unique`:
|
47
|
+
|
48
|
+
class Page < ActiveRecord::Base
|
49
|
+
has_permalink :title, :unique => true, :to_param => :permalink
|
50
|
+
end
|
51
|
+
|
52
|
+
The permalink is generated using `ActiveSupport::Multibyte::Chars` class; this means that characters will properly replaced from `áéíó` to `aeio`, for instance.
|
53
|
+
|
54
|
+
The permalink is created when `before_validation` callback is evaluated. This plugin also tries
|
55
|
+
to generate a permalink when `before_save` callback is evaluated and the instance has no permalink set.
|
56
|
+
|
57
|
+
## License
|
58
|
+
|
59
|
+
Copyright (c) 2008 Nando Vieira, released under the MIT license
|
60
|
+
|
61
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
62
|
+
a copy of this software and associated documentation files (the
|
63
|
+
"Software"), to deal in the Software without restriction, including
|
64
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
65
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
66
|
+
permit persons to whom the Software is furnished to do so, subject to
|
67
|
+
the following conditions:
|
68
|
+
|
69
|
+
The above copyright notice and this permission notice shall be
|
70
|
+
included in all copies or substantial portions of the Software.
|
71
|
+
|
72
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
73
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
74
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
75
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
76
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
77
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
78
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
require 'lib/permalink/version'
|
3
|
+
|
4
|
+
desc 'Default: run unit tests.'
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
desc 'Test the i18n-js plugin.'
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.libs << 'test'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'jeweler'
|
17
|
+
|
18
|
+
JEWEL = Jeweler::Tasks.new do |gem|
|
19
|
+
gem.name = "permalink"
|
20
|
+
gem.email = "fnando.vieira@gmail.com"
|
21
|
+
gem.homepage = "http://github.com/fnando/has_permalink"
|
22
|
+
gem.authors = ["Nando Vieira"]
|
23
|
+
gem.version = SimplesIdeias::Permalink::Version::STRING
|
24
|
+
gem.summary = "ActiveRecord plugin for automatically converting fields to permalinks."
|
25
|
+
gem.files = FileList["README.markdown", "init.rb", "{lib,test}/**/*", "Rakefile"]
|
26
|
+
end
|
27
|
+
|
28
|
+
Jeweler::GemcutterTasks.new
|
29
|
+
rescue LoadError => e
|
30
|
+
puts "[JEWELER] You can't build a gem until you install jeweler with `gem install jeweler`"
|
31
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "permalink"
|
data/lib/permalink.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require "permalink/string_ext"
|
2
|
+
|
3
|
+
module SimplesIdeias
|
4
|
+
module Permalink
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
|
8
|
+
class << base
|
9
|
+
attr_accessor :has_permalink_options
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# has_permalink :title
|
15
|
+
# has_permalink :title, :to => :custom_permalink_field
|
16
|
+
# has_permalink :title, :to => :permalink, :to_param => [:id, :permalink]
|
17
|
+
# has_permalink :title, :unique => true
|
18
|
+
def has_permalink(from, options={})
|
19
|
+
options = {
|
20
|
+
:to => :permalink,
|
21
|
+
:to_param => [:id, :permalink],
|
22
|
+
:unique => false
|
23
|
+
}.merge(options)
|
24
|
+
|
25
|
+
self.has_permalink_options = {
|
26
|
+
:from_column_name => from,
|
27
|
+
:to_column_name => options[:to],
|
28
|
+
:to_param => [options[:to_param]].flatten,
|
29
|
+
:unique => options[:unique]
|
30
|
+
}
|
31
|
+
|
32
|
+
include SimplesIdeias::Permalink::InstanceMethods
|
33
|
+
|
34
|
+
before_validation :create_permalink
|
35
|
+
before_save :create_permalink
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
def to_param
|
41
|
+
to_param_option = self.class.has_permalink_options[:to_param]
|
42
|
+
|
43
|
+
to_param_option.compact.collect do |name|
|
44
|
+
if respond_to?(name)
|
45
|
+
send(name).to_s
|
46
|
+
else
|
47
|
+
name.to_s
|
48
|
+
end
|
49
|
+
end.reject(&:blank?).join('-')
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def next_available_permalink(_permalink)
|
54
|
+
the_permalink = _permalink
|
55
|
+
|
56
|
+
if self.class.has_permalink_options[:unique]
|
57
|
+
suffix = 2
|
58
|
+
|
59
|
+
while self.class.first(:conditions => {to_permalink_name => the_permalink}, :select => self.class.primary_key)
|
60
|
+
the_permalink = "#{_permalink}-#{suffix}"
|
61
|
+
suffix += 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
the_permalink
|
66
|
+
end
|
67
|
+
|
68
|
+
def from_permalink_name
|
69
|
+
self.class.has_permalink_options[:from_column_name]
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_permalink_name
|
73
|
+
self.class.has_permalink_options[:to_column_name]
|
74
|
+
end
|
75
|
+
|
76
|
+
def from_permalink_value
|
77
|
+
read_attribute(from_permalink_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_permalink_value
|
81
|
+
read_attribute(to_permalink_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_permalink
|
85
|
+
unless from_permalink_value.blank? || !to_permalink_value.blank?
|
86
|
+
write_attribute(to_permalink_name, next_available_permalink(from_permalink_value.to_s.to_permalink))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
ActiveRecord::Base.send(:include, SimplesIdeias::Permalink)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class String
|
2
|
+
def to_permalink
|
3
|
+
str = ActiveSupport::Multibyte::Chars.new(self)
|
4
|
+
str = str.normalize(:kd).gsub(/[^\x00-\x7F]/,'').to_s
|
5
|
+
str.gsub!(/[^-\w\d]+/sim, "-")
|
6
|
+
str.gsub!(/-+/sm, "-")
|
7
|
+
str.gsub!(/^-?(.*?)-?$/, '\1')
|
8
|
+
str.downcase!
|
9
|
+
str
|
10
|
+
end
|
11
|
+
end
|
data/test/models/beer.rb
ADDED
data/test/models/post.rb
ADDED
data/test/models/user.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "test_helper"
|
3
|
+
|
4
|
+
class PermalinkTest < ActiveSupport::TestCase
|
5
|
+
SAMPLES = {
|
6
|
+
'This IS a Tripped out title!!.!1 (well/ not really)' => 'this-is-a-tripped-out-title-1-well-not-really',
|
7
|
+
'////// meph1sto r0x ! \\\\\\' => 'meph1sto-r0x',
|
8
|
+
'āčēģīķļņū' => 'acegiklnu',
|
9
|
+
'中文測試 chinese text' => 'chinese-text',
|
10
|
+
'some-)()()-ExtRa!/// .data==?> to \/\/test' => 'some-extra-data-to-test',
|
11
|
+
'http://simplesideias.com.br/tags/' => 'http-simplesideias-com-br-tags',
|
12
|
+
"Don't Repeat Yourself (DRY)" => 'don-t-repeat-yourself-dry',
|
13
|
+
"Text\nwith\nline\n\n\tbreaks" => 'text-with-line-breaks'
|
14
|
+
}
|
15
|
+
|
16
|
+
test "should create permalink using to_permalink" do
|
17
|
+
SAMPLES.each do |from, to|
|
18
|
+
assert_equal to, from.to_permalink
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
test "should create permalink" do
|
23
|
+
beer = create_beer
|
24
|
+
assert_equal 'duff', beer.permalink
|
25
|
+
end
|
26
|
+
|
27
|
+
test "should create permalink for custom field" do
|
28
|
+
donut = create_donut
|
29
|
+
assert_equal 'cream', donut.slug
|
30
|
+
end
|
31
|
+
|
32
|
+
test "should add permalink before_save" do
|
33
|
+
beer = Beer.new
|
34
|
+
assert_nil beer.permalink
|
35
|
+
beer.update_attribute(:name, 'Duff Premium')
|
36
|
+
beer.reload
|
37
|
+
assert_equal 'duff-premium', beer.permalink
|
38
|
+
end
|
39
|
+
|
40
|
+
test "should create unique permalinks" do
|
41
|
+
post1 = create_post(:title => 'Ruby is a beautiful language')
|
42
|
+
assert_equal "ruby-is-a-beautiful-language", post1.permalink
|
43
|
+
|
44
|
+
post2 = create_post(:title => 'Ruby is a beautiful language')
|
45
|
+
assert_equal "ruby-is-a-beautiful-language-2", post2.permalink
|
46
|
+
|
47
|
+
post3 = create_post(:title => 'Ruby is a beautiful language')
|
48
|
+
assert_equal "ruby-is-a-beautiful-language-3", post3.permalink
|
49
|
+
end
|
50
|
+
|
51
|
+
test "return to_param for unique permalink" do
|
52
|
+
post1 = create_post(:title => 'Ruby')
|
53
|
+
assert_equal post1.to_param, 'ruby'
|
54
|
+
|
55
|
+
post2 = create_post(:title => 'Ruby')
|
56
|
+
assert_equal post2.to_param, 'ruby-2'
|
57
|
+
end
|
58
|
+
|
59
|
+
test "should override to_param method" do
|
60
|
+
beer = create_beer
|
61
|
+
assert_equal "#{beer.id}-#{beer.permalink}", beer.to_param
|
62
|
+
end
|
63
|
+
|
64
|
+
test "should override to_param with custom fields" do
|
65
|
+
donut = create_donut
|
66
|
+
assert_equal "#{donut.slug}-#{donut.id}-permalink", donut.to_param
|
67
|
+
end
|
68
|
+
|
69
|
+
test "should ignore blank attributes from to_param" do
|
70
|
+
user = create_user
|
71
|
+
assert_equal "1-john-doe", user.to_param
|
72
|
+
end
|
73
|
+
|
74
|
+
test "should set permalink if permalink is blank" do
|
75
|
+
user = create_user(:permalink => " ")
|
76
|
+
user.reload
|
77
|
+
assert_equal "john-doe", user.permalink
|
78
|
+
end
|
79
|
+
|
80
|
+
test "should keep defined permalink" do
|
81
|
+
user = create_beer(:permalink => "jdoe")
|
82
|
+
user.reload
|
83
|
+
assert_equal "jdoe", user.permalink
|
84
|
+
end
|
85
|
+
|
86
|
+
test "should create unique permalinks for number-ended titles" do
|
87
|
+
post1 = create_post(:title => "Rails 3")
|
88
|
+
assert_equal "rails-3", post1.permalink
|
89
|
+
|
90
|
+
post2 = create_post(:title => "Rails 3")
|
91
|
+
assert_equal "rails-3-2", post2.permalink
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def create_beer(options={})
|
96
|
+
Beer.create({:name => 'Duff'}.merge(options))
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_donut(options={})
|
100
|
+
Donut.create({:flavor => 'Cream'}.merge(options))
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_user(options={})
|
104
|
+
User.create({:name => 'John Doe'}.merge(options))
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_post(options={})
|
108
|
+
Post.create({:title => 'Some nice post!'}.merge(options))
|
109
|
+
end
|
110
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :beers do |t|
|
3
|
+
t.string :name, :permalink
|
4
|
+
end
|
5
|
+
|
6
|
+
create_table :users do |t|
|
7
|
+
t.string :name, :permalink
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table :donuts do |t|
|
11
|
+
t.string :flavor, :slug
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :posts do |t|
|
15
|
+
t.string :title
|
16
|
+
t.string :permalink, :unique => true
|
17
|
+
end
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "test/unit"
|
3
|
+
require "active_record"
|
4
|
+
require "active_support/test_case"
|
5
|
+
require "permalink"
|
6
|
+
|
7
|
+
Dir.glob(File.dirname(__FILE__) + "/models/*.rb").each {|r| require r }
|
8
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ":memory:")
|
9
|
+
load('schema.rb')
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: permalink
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Nando Vieira
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-09 00:00:00 -03:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: fnando.vieira@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.markdown
|
29
|
+
files:
|
30
|
+
- README.markdown
|
31
|
+
- Rakefile
|
32
|
+
- init.rb
|
33
|
+
- lib/permalink.rb
|
34
|
+
- lib/permalink/string_ext.rb
|
35
|
+
- lib/permalink/version.rb
|
36
|
+
- test/models/beer.rb
|
37
|
+
- test/models/donut.rb
|
38
|
+
- test/models/post.rb
|
39
|
+
- test/models/user.rb
|
40
|
+
- test/permalink_test.rb
|
41
|
+
- test/schema.rb
|
42
|
+
- test/test_helper.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/fnando/has_permalink
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --charset=UTF-8
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.6
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: ActiveRecord plugin for automatically converting fields to permalinks.
|
73
|
+
test_files:
|
74
|
+
- test/models/beer.rb
|
75
|
+
- test/models/donut.rb
|
76
|
+
- test/models/post.rb
|
77
|
+
- test/models/user.rb
|
78
|
+
- test/permalink_test.rb
|
79
|
+
- test/schema.rb
|
80
|
+
- test/test_helper.rb
|