sluggy 0.1.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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Ary Djmal
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # Sluggy
2
+ ## Minimal slugging/permalink gem for ActiveRecord. Nothing fancy.
3
+
4
+ Built with rails 3.2 and ruby 1.9.2 in mind.
5
+
6
+ ### Features
7
+
8
+ * Slug generation with sequence support.
9
+ * Slug validation.
10
+ * **40 LOC** & fully tested.
11
+
12
+ ### Slug pattern
13
+
14
+ * downcase and strip
15
+ * remove non [a-z0-9-_]
16
+ * replace spaces with '-'
17
+ * if conflict add separator '--' with sequence number
18
+
19
+ ### Validations
20
+
21
+ * Presence
22
+ * Format with Sluggy::SLUG_REGEX
23
+ * Length within 1..100
24
+ * Uniqueness
25
+
26
+ ### Use like this
27
+
28
+ slug_for :title
29
+ slug_for :title, :column => :slug
30
+ slug_for :title, :column => :slug, :scope => :account_id
31
+
32
+ Defaults are `:column => :permalink, :scope => nil`
33
+
34
+ ### Install
35
+
36
+ gem 'sluggy'
37
+
38
+ Copyright (c) 2012 Ary Djmal, released under the MIT license.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ desc 'Default: run unit tests.'
5
+ task :default => :test
6
+
7
+ desc 'Test the sluggy gem.'
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << 'lib'
10
+ t.libs << 'test'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = false
13
+ end
data/lib/sluggy.rb ADDED
@@ -0,0 +1,44 @@
1
+ module Sluggy
2
+ SLUG_REGEX = %r{^[a-z0-9\-\_]+$}i
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def slug_for(base, options={})
10
+ options.reverse_merge!(:column => :permalink, :base => base, :scope => nil)
11
+ validates options[:column], :presence => true, :format => { :with => Sluggy::SLUG_REGEX }, :length => { :within => 1..100 }, :uniqueness => (options[:scope] ? {:scope => options[:scope]} : true)
12
+ before_validation :generate_slug
13
+ class_eval %{ def self.sluggy_options; #{options}; end }
14
+ include Sluggy::InstanceMethods
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ private
20
+ def slug_column; self.class.sluggy_options[:column]; end
21
+ def slug_scope; self.class.sluggy_options[:scope]; end
22
+ def slug_base; self.class.sluggy_options[:base]; end
23
+
24
+ def generate_slug
25
+ if send(slug_column).blank?
26
+ slug = send(slug_base).to_s.downcase.strip.gsub(/[^a-z0-9\s\-\_]/, '').gsub(/\s+/, '-')
27
+
28
+ exists = self.class.where("#{slug_column} = ? OR #{slug_column} LIKE ?", slug, "#{slug}--%")
29
+ exists = exists.where('id != ?', id) unless new_record?
30
+ exists = exists.where("#{slug_scope} = ?", send(slug_scope)) if slug_scope
31
+ exists = exists.order("LENGTH(#{slug_column}) DESC, #{slug_column} DESC")
32
+
33
+ if conflict = exists.first
34
+ last_number = conflict.send(slug_column).gsub(/^#{Regexp.quote(slug)}--/, '').to_i
35
+ slug << "--#{last_number+1}"
36
+ end
37
+
38
+ send("#{slug_column}=", slug)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ ActiveRecord::Base.send(:include, Sluggy)
data/sluggy.gemspec ADDED
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'sluggy'
3
+ s.version = '0.1.0'
4
+ s.authors = ['Ary Djmal']
5
+ s.email = ['arydjmal@gmail.com']
6
+ s.summary = 'Minimal slugging/permalink gem for ActiveRecord. Nothing fancy.'
7
+ s.homepage = 'http://github.com/arydjmal/sluggy'
8
+
9
+ s.add_dependency 'activerecord', '>= 3.0.0'
10
+
11
+ s.add_development_dependency 'rake'
12
+
13
+ s.files = Dir["#{File.dirname(__FILE__)}/**/*"]
14
+ end
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+
3
+ class SluggyDefaultsTest < Test::Unit::TestCase
4
+ def setup
5
+ Post.delete_all
6
+ Post.send(:slug_for, :title)
7
+ Post.create!(:title => 'First Post')
8
+ end
9
+
10
+ def test_with_new_title
11
+ post = Post.create!(:title => 'Second Post')
12
+ assert_equal('second-post', post.permalink)
13
+ end
14
+
15
+ def test_with_same_title
16
+ post = Post.create!(:title => 'First Post')
17
+ assert_equal('first-post--1', post.permalink)
18
+ end
19
+
20
+ def test_with_two_same_title
21
+ post_1 = Post.create!(:title => 'First Post')
22
+ post_2 = Post.create!(:title => 'First Post')
23
+ assert_equal('first-post--1', post_1.permalink)
24
+ assert_equal('first-post--2', post_2.permalink)
25
+ end
26
+
27
+ def test_slug_should_be_clean
28
+ post = Post.create!(:title => ' Third Post\'s! is-good_now!?? ')
29
+ assert_equal('third-posts-is-good_now', post.permalink)
30
+ end
31
+
32
+ def test_should_validate_when_updating_slug
33
+ post = Post.create!(:title => 'First Post')
34
+ assert_equal('first-post--1', post.permalink)
35
+ post.update_attributes(:permalink => '')
36
+ assert_equal('first-post--1', post.permalink)
37
+ post.update_attributes(:permalink => 'first-post!! iji@@')
38
+ assert_equal('first-post--1', post.reload.permalink)
39
+ post.update_attributes(:permalink => 'first-post')
40
+ assert_equal('first-post--1', post.reload.permalink)
41
+ end
42
+ end
43
+
44
+ class SluggyWithOptionsTest < Test::Unit::TestCase
45
+ def setup
46
+ User.delete_all
47
+ User.send(:slug_for, :name, {:column => :handle, :scope => :account_id})
48
+ User.create!(:name => 'Ary Djmal', :account_id => 1)
49
+ end
50
+
51
+ def test_with_new_title
52
+ user = User.create!(:name => 'Nati Djmal', :account_id => 1)
53
+ assert_equal('nati-djmal', user.handle)
54
+ end
55
+
56
+ def test_with_same_name
57
+ user = User.create!(:name => 'Ary Djmal', :account_id => 1)
58
+ assert_equal('ary-djmal--1', user.handle)
59
+ end
60
+
61
+ def test_with_two_same_title
62
+ user_1 = User.create!(:name => 'Ary Djmal', :account_id => 1)
63
+ user_2 = User.create!(:name => 'Ary Djmal', :account_id => 1)
64
+ assert_equal('ary-djmal--1', user_1.handle)
65
+ assert_equal('ary-djmal--2', user_2.handle)
66
+ end
67
+
68
+ def test_with_same_title_but_not_in_scope
69
+ user = User.create!(:name => 'Ary Djmal', :account_id => 2)
70
+ assert_equal('ary-djmal', user.handle)
71
+ end
72
+
73
+ def test_should_validate_when_updating_slug
74
+ user = User.create!(:name => 'Ary Djmal', :account_id => 1)
75
+ assert_equal('ary-djmal--1', user.handle)
76
+ user.update_attributes(:handle => '')
77
+ assert_equal('ary-djmal--1', user.handle)
78
+ user.update_attributes(:handle => 'ary-djmal!! iji@@')
79
+ assert_equal('ary-djmal--1', user.reload.handle)
80
+ user.update_attributes(:handle => 'ary-djmal')
81
+ assert_equal('ary-djmal--1', user.reload.handle)
82
+ end
83
+ end
@@ -0,0 +1,25 @@
1
+ require 'test/unit'
2
+ require 'active_record'
3
+ require 'sluggy'
4
+
5
+ ActiveRecord::Base.establish_connection({
6
+ :adapter => 'sqlite3',
7
+ :database => ':memory:'
8
+ })
9
+
10
+ ActiveRecord::Schema.define do
11
+ create_table :posts do |t|
12
+ t.string :title, :permalink
13
+ end
14
+
15
+ create_table :users do |t|
16
+ t.string :name, :handle
17
+ t.integer :account_id
18
+ end
19
+ end
20
+
21
+ class Post < ActiveRecord::Base
22
+ end
23
+
24
+ class User < ActiveRecord::Base
25
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sluggy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ary Djmal
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-02 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: &70257429974860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70257429974860
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70257429972740 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70257429972740
36
+ description:
37
+ email:
38
+ - arydjmal@gmail.com
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - ./lib/sluggy.rb
44
+ - ./MIT-LICENSE
45
+ - ./Rakefile
46
+ - ./README.md
47
+ - ./sluggy.gemspec
48
+ - ./test/sluggy_test.rb
49
+ - ./test/test_helper.rb
50
+ homepage: http://github.com/arydjmal/sluggy
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.10
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Minimal slugging/permalink gem for ActiveRecord. Nothing fancy.
74
+ test_files: []
75
+ has_rdoc: