sluggy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: