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 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