slugger 0.2.0 → 0.3.0
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/VERSION +1 -1
- data/lib/slugger.rb +62 -24
- data/slugger.gemspec +0 -1
- data/spec/lib/post_spec.rb +5 -0
- data/spec/lib/user_spec.rb +7 -0
- data/spec/schema.rb +4 -4
- metadata +41 -73
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/lib/slugger.rb
CHANGED
@@ -8,7 +8,7 @@ module Slugger
|
|
8
8
|
end
|
9
9
|
module ClassMethods
|
10
10
|
def has_slug(title_column=nil,options={})
|
11
|
-
|
11
|
+
class_attribute :slugger_options
|
12
12
|
default_options = {
|
13
13
|
:title_column => 'title',
|
14
14
|
:slug_column => 'slug',
|
@@ -16,35 +16,44 @@ module Slugger
|
|
16
16
|
:downcase => true,
|
17
17
|
:on_conflict => :error
|
18
18
|
}
|
19
|
-
|
19
|
+
|
20
20
|
self.slugger_options = default_options.merge(options)
|
21
21
|
self.slugger_options[:title_column] = title_column unless title_column.nil?
|
22
|
-
|
23
|
-
# if columns_hash[slugger_options[:title_column].to_s].nil?
|
24
|
-
# raise ArgumentError, "#{self.name} is missing source column"
|
25
|
-
# end
|
26
|
-
raise ArgumentError, "#{self.name} is missing required #{slugger_options[:slug_column]} column" if columns_hash[slugger_options[:slug_column].to_s].nil?
|
27
22
|
|
28
|
-
|
23
|
+
if columns_hash[slugger_options[:slug_column].to_s].nil?
|
24
|
+
raise ArgumentError, "#{self.name} is missing required " +
|
25
|
+
"#{slugger_options[:slug_column]} column"
|
26
|
+
end
|
27
|
+
|
28
|
+
before_validation :permalize, :on => :create
|
29
|
+
before_validation :permalize_on_blank, :on => :update
|
30
|
+
|
31
|
+
# Used by +slug_conflict_resolution_append_id+
|
32
|
+
after_create :append_id_to_slug
|
29
33
|
|
30
34
|
validates slugger_options[:slug_column].to_sym, :presence => true
|
35
|
+
|
31
36
|
if slugger_options[:scope]
|
32
|
-
validates slugger_options[:slug_column].to_sym,
|
37
|
+
validates slugger_options[:slug_column].to_sym,
|
38
|
+
:uniqueness => { :scope => slugger_options[:scope] }
|
33
39
|
else
|
34
|
-
validates slugger_options[:slug_column].to_sym,
|
40
|
+
validates slugger_options[:slug_column].to_sym,
|
41
|
+
:uniqueness => true
|
35
42
|
end
|
36
43
|
|
37
|
-
send :define_method, :column_to_slug,
|
38
|
-
|
44
|
+
send :define_method, :column_to_slug,
|
45
|
+
lambda { self.send(slugger_options[:title_column]) }
|
46
|
+
|
39
47
|
include InstanceMethods
|
40
48
|
end
|
41
49
|
end
|
42
50
|
module InstanceMethods
|
43
51
|
|
44
|
-
|
52
|
+
private
|
45
53
|
|
46
54
|
def permalize
|
47
55
|
return unless self.send("#{self.slugger_options[:slug_column]}").blank?
|
56
|
+
|
48
57
|
if slugger_options[:title_column].is_a?(Array)
|
49
58
|
s = ""
|
50
59
|
self.slugger_options[:title_column].each do |m|
|
@@ -52,34 +61,63 @@ module Slugger
|
|
52
61
|
end
|
53
62
|
s = Iconv.iconv('ascii//ignore//translit', 'utf-8', s).to_s
|
54
63
|
else
|
55
|
-
s = Iconv.iconv('ascii//ignore//translit', 'utf-8',
|
64
|
+
s = Iconv.iconv('ascii//ignore//translit', 'utf-8',
|
65
|
+
self.send("#{self.slugger_options[:title_column]}")).to_s
|
56
66
|
end
|
57
|
-
|
58
|
-
|
59
|
-
s.
|
60
|
-
|
61
|
-
s.gsub!(/\
|
67
|
+
|
68
|
+
# Remove apostrophes
|
69
|
+
s.gsub!(/\'/, '')
|
70
|
+
# Replace all non-word chars to spaces
|
71
|
+
s.gsub!(/\W+/, ' ')
|
72
|
+
s.strip!
|
73
|
+
# Replace spaces with dashes or custom substitution character
|
74
|
+
s.gsub!(/\ +/, slugger_options[:substitution_char].to_s)
|
75
|
+
s.downcase! if slugger_options[:downcase]
|
76
|
+
s = s[0..(slugger_options[:max_length] - 1)] if slugger_options[:max_length]
|
77
|
+
|
62
78
|
self.send("#{self.slugger_options[:slug_column]}=", s)
|
79
|
+
|
63
80
|
slug_conflict_resolution
|
64
81
|
end
|
82
|
+
|
83
|
+
def permalize_on_blank
|
84
|
+
permalize if self[slugger_options[:slug_column]].blank?
|
85
|
+
end
|
86
|
+
|
65
87
|
def slug_conflict_resolution(append=nil)
|
66
|
-
|
67
|
-
|
88
|
+
slug_column = slugger_options[:slug_column]
|
89
|
+
|
90
|
+
# Check if there are any records which the generated slug will conflict with
|
91
|
+
if self.class.where(slug_column => read_attribute(slug_column)).any?
|
68
92
|
self.send("slug_conflict_resolution_#{self.slugger_options[:on_conflict]}", append)
|
69
93
|
end
|
70
94
|
end
|
95
|
+
|
71
96
|
def slug_conflict_resolution_concat_random_chars(append)
|
72
97
|
chars = ("a".."z").to_a + ("1".."9").to_a
|
73
98
|
random_chars = Array.new(3, '').collect{chars[rand(chars.size)]}.join
|
74
99
|
self.send("#{self.slugger_options[:slug_column]}=", "#{self.slugger_options[:slug_column]}#{self.slugger_options[:substitution_char]}#{random_chars}")
|
75
100
|
slug_conflict_resolution
|
76
101
|
end
|
102
|
+
|
103
|
+
def slug_conflict_resolution_append_id(append)
|
104
|
+
slug_column = slugger_options[:slug_column]
|
105
|
+
substitution_char = slugger_options[:substitution_char]
|
106
|
+
|
107
|
+
# Temporarily write the slug with '+ID+' appended, then update the slug
|
108
|
+
# after the record is saved to the database +appended_id_to_slug+
|
109
|
+
self[slug_column] = [self[slug_column], '+ID+'].join(substitution_char)
|
110
|
+
end
|
111
|
+
|
77
112
|
def slug_conflict_resolution_error(append)
|
78
|
-
# no op,
|
113
|
+
# no op, validation sets error
|
79
114
|
end
|
80
115
|
|
81
|
-
def
|
82
|
-
|
116
|
+
def append_id_to_slug
|
117
|
+
slug_column = slugger_options[:slug_column]
|
118
|
+
id_regex = /\+ID\+\z/
|
119
|
+
return unless self[slug_column][id_regex]
|
120
|
+
update_attribute(slug_column, self[slug_column].gsub(id_regex, id.to_s))
|
83
121
|
end
|
84
122
|
end
|
85
123
|
end
|
data/slugger.gemspec
CHANGED
data/spec/lib/post_spec.rb
CHANGED
@@ -13,6 +13,11 @@ describe Post do
|
|
13
13
|
p = Post.create(:title => "apostrop'hes", :slug => "apostrophes")
|
14
14
|
p.slug.should == "apostrophes"
|
15
15
|
end
|
16
|
+
it "should be limited to 20 characters" do
|
17
|
+
p = Post.create(
|
18
|
+
:title => "when you write really long titles slugs should be shortened")
|
19
|
+
p.slug.should == "when-you-write-reall"
|
20
|
+
end
|
16
21
|
it "should not be valid on duplicate" do
|
17
22
|
p = Post.create(:title => "hello")
|
18
23
|
p.slug.should == "hello"
|
data/spec/lib/user_spec.rb
CHANGED
@@ -9,4 +9,11 @@ describe User do
|
|
9
9
|
u = User.create(:first_name => "Tyler", :last_name => "Durden")
|
10
10
|
u.slug.should == "tyler-durden"
|
11
11
|
end
|
12
|
+
it "should append the database id if duplicates exist" do
|
13
|
+
first_user = User.create(:first_name => "Jordan", :last_name => "Byron")
|
14
|
+
first_user.slug.should == "jordan-byron"
|
15
|
+
|
16
|
+
second_user = User.create(:first_name => "Jordan", :last_name => "Byron")
|
17
|
+
second_user.slug.should == "jordan-byron-#{second_user.id}"
|
18
|
+
end
|
12
19
|
end
|
data/spec/schema.rb
CHANGED
@@ -13,14 +13,14 @@ class CreateSchema < ActiveRecord::Migration
|
|
13
13
|
t.string :slug
|
14
14
|
t.timestamps
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
create_table :users, :force => true do |t|
|
18
18
|
t.string :first_name
|
19
19
|
t.string :last_name
|
20
20
|
t.string :slug
|
21
21
|
t.timestamps
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
create_table :edges, :force => true do |t|
|
25
25
|
t.string :name
|
26
26
|
t.string :slug_name
|
@@ -33,11 +33,11 @@ CreateSchema.suppress_messages do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
class Post < ActiveRecord::Base
|
36
|
-
has_slug
|
36
|
+
has_slug 'title', :max_length => 20
|
37
37
|
end
|
38
38
|
|
39
39
|
class User < ActiveRecord::Base
|
40
|
-
has_slug [:first_name, :last_name]
|
40
|
+
has_slug [:first_name, :last_name], :on_conflict => :append_id
|
41
41
|
end
|
42
42
|
|
43
43
|
class Edge < ActiveRecord::Base
|
metadata
CHANGED
@@ -1,81 +1,58 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: slugger
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 0
|
10
|
-
version: 0.2.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Seth Faxon
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-01-11 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: activerecord
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &70106294659920 !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 7
|
29
|
-
segments:
|
30
|
-
- 3
|
31
|
-
- 0
|
32
|
-
- 0
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
33
21
|
version: 3.0.0
|
34
22
|
type: :runtime
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: rspec
|
38
23
|
prerelease: false
|
39
|
-
|
24
|
+
version_requirements: *70106294659920
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70106294659420 !ruby/object:Gem::Requirement
|
40
28
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
hash: 15
|
45
|
-
segments:
|
46
|
-
- 2
|
47
|
-
- 0
|
48
|
-
- 0
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
49
32
|
version: 2.0.0
|
50
33
|
type: :development
|
51
|
-
version_requirements: *id002
|
52
|
-
- !ruby/object:Gem::Dependency
|
53
|
-
name: sqlite3
|
54
34
|
prerelease: false
|
55
|
-
|
35
|
+
version_requirements: *70106294659420
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sqlite3
|
38
|
+
requirement: &70106294658960 !ruby/object:Gem::Requirement
|
56
39
|
none: false
|
57
|
-
requirements:
|
40
|
+
requirements:
|
58
41
|
- - ~>
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
hash: 27
|
61
|
-
segments:
|
62
|
-
- 1
|
63
|
-
- 3
|
64
|
-
- 0
|
42
|
+
- !ruby/object:Gem::Version
|
65
43
|
version: 1.3.0
|
66
44
|
type: :development
|
67
|
-
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70106294658960
|
68
47
|
description: Slugger is yet another slug generator.
|
69
|
-
email:
|
48
|
+
email:
|
70
49
|
- seth.faxon@gmail.com
|
71
50
|
executables: []
|
72
|
-
|
73
51
|
extensions: []
|
74
|
-
|
75
|
-
extra_rdoc_files:
|
52
|
+
extra_rdoc_files:
|
76
53
|
- MIT-LICENSE
|
77
54
|
- README.rdoc
|
78
|
-
files:
|
55
|
+
files:
|
79
56
|
- .gitignore
|
80
57
|
- Gemfile
|
81
58
|
- MIT-LICENSE
|
@@ -93,38 +70,29 @@ files:
|
|
93
70
|
- tasks/spec.rake
|
94
71
|
homepage: https://github.com/sfaxon/slugger
|
95
72
|
licenses: []
|
96
|
-
|
97
73
|
post_install_message:
|
98
74
|
rdoc_options: []
|
99
|
-
|
100
|
-
require_paths:
|
75
|
+
require_paths:
|
101
76
|
- lib
|
102
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
78
|
none: false
|
104
|
-
requirements:
|
105
|
-
- -
|
106
|
-
- !ruby/object:Gem::Version
|
107
|
-
|
108
|
-
|
109
|
-
- 0
|
110
|
-
version: "0"
|
111
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
84
|
none: false
|
113
|
-
requirements:
|
114
|
-
- -
|
115
|
-
- !ruby/object:Gem::Version
|
116
|
-
|
117
|
-
segments:
|
118
|
-
- 0
|
119
|
-
version: "0"
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
120
89
|
requirements: []
|
121
|
-
|
122
90
|
rubyforge_project: slugger
|
123
91
|
rubygems_version: 1.8.8
|
124
92
|
signing_key:
|
125
93
|
specification_version: 3
|
126
94
|
summary: Slugger is yet another slug generator.
|
127
|
-
test_files:
|
95
|
+
test_files:
|
128
96
|
- spec/lib/edge_spec.rb
|
129
97
|
- spec/lib/post_spec.rb
|
130
98
|
- spec/lib/user_spec.rb
|