slugger 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.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
- class_inheritable_accessor :slugger_options
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
- # :on_conflict can be :error or :concat_random_chars
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
- before_validation :permalize, :on => :create
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, :uniqueness => {:scope => slugger_options[:scope]}
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, :uniqueness => true
40
+ validates slugger_options[:slug_column].to_sym,
41
+ :uniqueness => true
35
42
  end
36
43
 
37
- send :define_method, :column_to_slug, lambda { self.send(slugger_options[:title_column]) }
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
- protected
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', self.send("#{self.slugger_options[:title_column]}")).to_s
64
+ s = Iconv.iconv('ascii//ignore//translit', 'utf-8',
65
+ self.send("#{self.slugger_options[:title_column]}")).to_s
56
66
  end
57
- s.gsub!(/\'/, '') # remove '
58
- s.gsub!(/\W+/, ' ') # all non-word chars to spaces
59
- s.strip! # ohh la la
60
- s.downcase! if slugger_options[:downcase]
61
- s.gsub!(/\ +/, slugger_options[:substitution_char].to_s) # spaces to dashes, preferred separator char everywhere
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
- existing = self.class.send(:where, "#{slugger_options[:slug_column]}".to_sym => self.send("#{self.slugger_options[:slug_column]}")).first
67
- if existing
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, validatino sets error
113
+ # no op, validation sets error
79
114
  end
80
115
 
81
- def strip_title
82
- self.send("#{self.slugger_options[:title_column]}").strip!
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
@@ -27,5 +27,4 @@ Gem::Specification.new do |s|
27
27
  s.add_dependency "activerecord", ">= 3.0.0"
28
28
  s.add_development_dependency "rspec", ">= 2.0.0"
29
29
  s.add_development_dependency "sqlite3", "~> 1.3.0"
30
- # s.add_runtime_dependency "rest-client"
31
30
  end
@@ -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"
@@ -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
- hash: 23
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
- date: 2011-08-28 00:00:00 Z
19
- dependencies:
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
- prerelease: false
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- requirement: &id003 !ruby/object:Gem::Requirement
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
- version_requirements: *id003
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
- hash: 3
108
- segments:
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
- hash: 3
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