sluggi 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +1 -7
- data/lib/sluggi.rb +4 -0
- data/lib/sluggi/history.rb +6 -1
- data/lib/sluggi/model.rb +52 -0
- data/lib/sluggi/slugged.rb +4 -70
- data/lib/sluggi/validate_exclusion_of.rb +32 -0
- data/lib/sluggi/validate_presence.rb +9 -0
- data/lib/sluggi/validate_uniqueness.rb +9 -0
- data/lib/sluggi/version.rb +1 -1
- data/test/model_test.rb +73 -0
- data/test/slugged_test.rb +9 -83
- data/test/validate_presence_test.rb +12 -0
- data/test/validate_uniqueness_test.rb +24 -0
- data/test/validates_exclusion_of_test.rb +25 -0
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d47ac0cb584d36352b4ac43a1e2f7fc7cde0b85
|
4
|
+
data.tar.gz: a62c76432d186ab0024492c686fc45cb505f6768
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7584d82c191aa1c70e35c8ff16c1d0bc39c286340f9a01fb299e9e542b0fe6099bc89c9d55f3bc1147a45bfdf205b9d58d346b58ede60ef2e26e8c418a096ee0
|
7
|
+
data.tar.gz: f9740626f5c05f675848f399fecd486f296e957420fb52cb726346e47428915e3a185a71dd84261d7e8b834431ac394c3eb755ef802c9a1a1e40ccd4ce3e8a01
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -20,13 +20,7 @@ gem 'sluggi'
|
|
20
20
|
Add a string column named `slug` to any models you want to slug. You can generate a migration like so:
|
21
21
|
|
22
22
|
```sh
|
23
|
-
rails generate migration AddSlugToCats slug:string
|
24
|
-
```
|
25
|
-
```ruby
|
26
|
-
# edit the migration to add a unique index:
|
27
|
-
add_index :cats, :slug, unique: true
|
28
|
-
```
|
29
|
-
```sh
|
23
|
+
rails generate migration AddSlugToCats slug:string:uniq:index
|
30
24
|
rake db:migrate
|
31
25
|
```
|
32
26
|
|
data/lib/sluggi.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require "sluggi/version"
|
2
2
|
require "sluggi/slug"
|
3
3
|
require "sluggi/history"
|
4
|
+
require "sluggi/validate_exclusion_of"
|
5
|
+
require "sluggi/validate_presence"
|
6
|
+
require "sluggi/validate_uniqueness"
|
7
|
+
require "sluggi/model"
|
4
8
|
require "sluggi/slugged"
|
5
9
|
require "sluggi/generators/sluggi_generator"
|
data/lib/sluggi/history.rb
CHANGED
@@ -3,7 +3,12 @@ module Sluggi
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
included do
|
6
|
-
has_many :slugs,
|
6
|
+
has_many :slugs,
|
7
|
+
-> { order('slugs.id DESC') },
|
8
|
+
class_name: 'Sluggi::Slug',
|
9
|
+
as: :sluggable,
|
10
|
+
dependent: :destroy
|
11
|
+
|
7
12
|
after_save :create_slug
|
8
13
|
end
|
9
14
|
|
data/lib/sluggi/model.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Sluggi
|
2
|
+
module Model
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
NOT_IMPLEMENTED_MESSAGE = 'You must implement #slug_value_changed? '\
|
6
|
+
'and either #slug_value or #slug_candidates'.freeze
|
7
|
+
|
8
|
+
included do
|
9
|
+
before_validation :set_slug
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_param
|
13
|
+
slug_was
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def clean_slug(value)
|
19
|
+
value.try :parameterize
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_slug
|
23
|
+
return unless new_record? || slug_value_changed?
|
24
|
+
new_slug = clean_slug(slug_value)
|
25
|
+
return if new_slug.blank?
|
26
|
+
self.slug = new_slug
|
27
|
+
end
|
28
|
+
|
29
|
+
# these are generally good to override:
|
30
|
+
|
31
|
+
def slug_value
|
32
|
+
slug_candidates.each do |value|
|
33
|
+
next if value.blank?
|
34
|
+
candidate = clean_slug(value)
|
35
|
+
return candidate if candidate == slug
|
36
|
+
return candidate unless self.class.exists?(slug: candidate)
|
37
|
+
end
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
# return an array of candidate slug values
|
42
|
+
# Example:
|
43
|
+
# [first_name, full_name, id_and_full_name]
|
44
|
+
def slug_candidates
|
45
|
+
raise NotImplementedError, NOT_IMPLEMENTED_MESSAGE
|
46
|
+
end
|
47
|
+
|
48
|
+
def slug_value_changed?
|
49
|
+
raise NotImplementedError, NOT_IMPLEMENTED_MESSAGE
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/sluggi/slugged.rb
CHANGED
@@ -1,77 +1,11 @@
|
|
1
1
|
module Sluggi
|
2
2
|
module Slugged
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
RESERVED_SLUGS = %w(
|
6
|
-
create
|
7
|
-
edit
|
8
|
-
index
|
9
|
-
new
|
10
|
-
update
|
11
|
-
admin
|
12
|
-
assets
|
13
|
-
images
|
14
|
-
javascripts
|
15
|
-
login
|
16
|
-
logout
|
17
|
-
stylesheets
|
18
|
-
session
|
19
|
-
users
|
20
|
-
)
|
21
|
-
|
22
|
-
NOT_IMPLEMENTED_MESSAGE = 'You must implement #slug_value_changed? and either #slug_value or #slug_candidates'
|
23
|
-
|
24
4
|
included do
|
25
|
-
|
26
|
-
|
27
|
-
|
5
|
+
include Sluggi::ValidatePresence
|
6
|
+
include Sluggi::ValidateUniqueness
|
7
|
+
include Sluggi::ValidateExclusionOf
|
8
|
+
include Sluggi::Model
|
28
9
|
end
|
29
|
-
|
30
|
-
module ClassMethods
|
31
|
-
def reserved_slugs
|
32
|
-
RESERVED_SLUGS
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_param
|
37
|
-
slug_was
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def clean_slug(value)
|
43
|
-
value.try :parameterize
|
44
|
-
end
|
45
|
-
|
46
|
-
def set_slug
|
47
|
-
return unless new_record? || slug_value_changed?
|
48
|
-
new_slug = clean_slug(slug_value)
|
49
|
-
return if new_slug.blank?
|
50
|
-
self.slug = new_slug
|
51
|
-
end
|
52
|
-
|
53
|
-
# these are generally good to override:
|
54
|
-
|
55
|
-
def slug_value
|
56
|
-
slug_candidates.each do |value|
|
57
|
-
next if value.blank?
|
58
|
-
candidate = clean_slug(value)
|
59
|
-
return candidate if candidate == slug
|
60
|
-
return candidate unless self.class.exists?(slug: candidate)
|
61
|
-
end
|
62
|
-
nil
|
63
|
-
end
|
64
|
-
|
65
|
-
# return an array of candidate slug values
|
66
|
-
# Example:
|
67
|
-
# [first_name, full_name, id_and_full_name]
|
68
|
-
def slug_candidates
|
69
|
-
raise NotImplementedError.new(NOT_IMPLEMENTED_MESSAGE)
|
70
|
-
end
|
71
|
-
|
72
|
-
def slug_value_changed?
|
73
|
-
raise NotImplementedError.new(NOT_IMPLEMENTED_MESSAGE)
|
74
|
-
end
|
75
|
-
|
76
10
|
end
|
77
11
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sluggi
|
2
|
+
module ValidateExclusionOf
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
RESERVED_SLUGS = %w(
|
6
|
+
admin
|
7
|
+
assets
|
8
|
+
create
|
9
|
+
edit
|
10
|
+
images
|
11
|
+
index
|
12
|
+
javascripts
|
13
|
+
login
|
14
|
+
logout
|
15
|
+
new
|
16
|
+
session
|
17
|
+
stylesheets
|
18
|
+
update
|
19
|
+
users
|
20
|
+
)
|
21
|
+
|
22
|
+
included do
|
23
|
+
validates_exclusion_of :slug, in: ->(_) { reserved_slugs }
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
def reserved_slugs
|
28
|
+
RESERVED_SLUGS
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/sluggi/version.rb
CHANGED
data/test/model_test.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ModelTest < MiniTest::Spec
|
4
|
+
class IncompleteCat < ActiveRecord::Base
|
5
|
+
self.table_name = 'cats'
|
6
|
+
include Sluggi::Model
|
7
|
+
end
|
8
|
+
|
9
|
+
class Cat < ActiveRecord::Base
|
10
|
+
include Sluggi::Model
|
11
|
+
|
12
|
+
def slug_value
|
13
|
+
name
|
14
|
+
end
|
15
|
+
|
16
|
+
def slug_value_changed?
|
17
|
+
name_changed?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class CandidateCat < ActiveRecord::Base
|
22
|
+
self.table_name = 'cats'
|
23
|
+
include Sluggi::Model
|
24
|
+
|
25
|
+
def slug_candidates
|
26
|
+
[nil, name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def slug_value_changed?
|
30
|
+
name_changed?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises when slug_value is not implemented" do
|
35
|
+
assert_raises(NotImplementedError) do
|
36
|
+
IncompleteCat.new.valid?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#to_param" do
|
41
|
+
before do
|
42
|
+
Cat.delete_all
|
43
|
+
@cat = Cat.new(name: 'Tuxedo Stan')
|
44
|
+
end
|
45
|
+
|
46
|
+
it "is nil when new" do
|
47
|
+
assert_nil @cat.to_param
|
48
|
+
end
|
49
|
+
|
50
|
+
it "is the slug when persisted" do
|
51
|
+
@cat.save!
|
52
|
+
assert_equal 'tuxedo-stan', @cat.to_param
|
53
|
+
end
|
54
|
+
|
55
|
+
it "is the old slug when changed but not saved" do
|
56
|
+
@cat.save!
|
57
|
+
@cat.slug = 'ketzel'
|
58
|
+
assert_equal 'tuxedo-stan', @cat.to_param
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "sets slug from candidates on validation" do
|
63
|
+
cat = CandidateCat.new(name: 'Smokey')
|
64
|
+
assert cat.valid?
|
65
|
+
assert_equal 'smokey', cat.slug
|
66
|
+
end
|
67
|
+
|
68
|
+
it "sets slug on validation" do
|
69
|
+
cat = Cat.new(name: 'Prince Chunk')
|
70
|
+
assert cat.valid?
|
71
|
+
assert_equal 'prince-chunk', cat.slug
|
72
|
+
end
|
73
|
+
end
|
data/test/slugged_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class SluggedTest < MiniTest::Spec
|
4
|
-
class
|
4
|
+
class Cat < ActiveRecord::Base
|
5
5
|
include Sluggi::Slugged
|
6
6
|
|
7
7
|
def slug_value
|
@@ -13,93 +13,19 @@ class SluggedTest < MiniTest::Spec
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
include Sluggi::Slugged
|
19
|
-
|
20
|
-
def slug_candidates
|
21
|
-
[nil, name]
|
22
|
-
end
|
23
|
-
|
24
|
-
def slug_value_changed?
|
25
|
-
name_changed?
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class ::IncompleteCat < ActiveRecord::Base
|
30
|
-
self.table_name = 'cats'
|
31
|
-
include Sluggi::Slugged
|
32
|
-
end
|
33
|
-
|
34
|
-
it "raises when slug_value is not implemented" do
|
35
|
-
assert_raises(NotImplementedError) do
|
36
|
-
IncompleteCat.new.valid?
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "#to_param" do
|
41
|
-
before do
|
42
|
-
Cat.delete_all
|
43
|
-
@cat = Cat.new(name: 'Tuxedo Stan')
|
44
|
-
end
|
45
|
-
|
46
|
-
it "is nil when new" do
|
47
|
-
assert_nil @cat.to_param
|
48
|
-
end
|
49
|
-
|
50
|
-
it "is the slug when persisted" do
|
51
|
-
@cat.save!
|
52
|
-
assert_equal 'tuxedo-stan', @cat.to_param
|
53
|
-
end
|
54
|
-
|
55
|
-
it "is the old slug when changed but not saved" do
|
56
|
-
@cat.save!
|
57
|
-
@cat.slug = 'ketzel'
|
58
|
-
assert_equal 'tuxedo-stan', @cat.to_param
|
59
|
-
end
|
16
|
+
it "includes ValidatePresence" do
|
17
|
+
assert Cat.new.is_a? Sluggi::ValidatePresence
|
60
18
|
end
|
61
19
|
|
62
|
-
|
63
|
-
|
64
|
-
it "includes #{word}" do
|
65
|
-
assert_includes Cat.reserved_slugs, word
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
it "does not include valid slug" do
|
70
|
-
refute_includes Cat.reserved_slugs, 'something'
|
71
|
-
end
|
20
|
+
it "includes ValidateUniqueness" do
|
21
|
+
assert Cat.new.is_a? Sluggi::ValidateUniqueness
|
72
22
|
end
|
73
23
|
|
74
|
-
|
75
|
-
|
76
|
-
refute Cat.new(name: 'edit').valid?
|
77
|
-
end
|
78
|
-
|
79
|
-
it "blank slug is invalid" do
|
80
|
-
refute Cat.new(name: '').valid?
|
81
|
-
end
|
82
|
-
|
83
|
-
it "sets slug on validation" do
|
84
|
-
cat = Cat.new(name: 'Prince Chunk')
|
85
|
-
assert cat.valid?
|
86
|
-
assert_equal 'prince-chunk', cat.slug
|
87
|
-
end
|
88
|
-
|
89
|
-
it "is invalid when slug is taken" do
|
90
|
-
Cat.create(slug: 'mrs-chippy')
|
91
|
-
cat = Cat.new(name: 'Mrs. Chippy')
|
92
|
-
refute cat.valid?
|
93
|
-
assert_nil cat.to_param
|
94
|
-
end
|
24
|
+
it "includes ValidateExclusionOf" do
|
25
|
+
assert Cat.new.is_a? Sluggi::ValidateExclusionOf
|
95
26
|
end
|
96
27
|
|
97
|
-
|
98
|
-
|
99
|
-
cat = CandidateCat.new(name: 'Smokey')
|
100
|
-
assert cat.valid?
|
101
|
-
assert_equal 'smokey', cat.slug
|
102
|
-
end
|
28
|
+
it "includes Model" do
|
29
|
+
assert Cat.new.is_a? Sluggi::Model
|
103
30
|
end
|
104
|
-
|
105
31
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ValidatePresenceTest < MiniTest::Spec
|
4
|
+
class Cat < ActiveRecord::Base
|
5
|
+
include Sluggi::ValidatePresence
|
6
|
+
end
|
7
|
+
|
8
|
+
it "validates presence of slug" do
|
9
|
+
refute Cat.new(slug: '').valid?
|
10
|
+
assert Cat.new(slug: 'foo').valid?
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ValidateUniquenessTest < MiniTest::Spec
|
4
|
+
class Cat < ActiveRecord::Base
|
5
|
+
include Sluggi::Model
|
6
|
+
include Sluggi::ValidateUniqueness
|
7
|
+
|
8
|
+
def slug_value
|
9
|
+
name
|
10
|
+
end
|
11
|
+
|
12
|
+
def slug_value_changed?
|
13
|
+
name_changed?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it "validates slug is unique" do
|
18
|
+
Cat.create(slug: 'mrs-chippy')
|
19
|
+
cat = Cat.new(name: 'Mrs. Chippy')
|
20
|
+
refute cat.valid?
|
21
|
+
assert_nil cat.to_param
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ValidatesExclusionOfTest < MiniTest::Spec
|
4
|
+
class Cat < ActiveRecord::Base
|
5
|
+
include Sluggi::ValidateExclusionOf
|
6
|
+
end
|
7
|
+
|
8
|
+
describe ".reserved_slugs" do
|
9
|
+
%w(create login users).each do |word|
|
10
|
+
it "includes #{word}" do
|
11
|
+
assert_includes Cat.reserved_slugs, word
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it "does not include valid slug" do
|
16
|
+
refute_includes Cat.reserved_slugs, 'something'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "validation" do
|
21
|
+
it "ensures slug is not a reserved word" do
|
22
|
+
refute Cat.new(slug: 'edit').valid?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sluggi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tee Parham
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -97,15 +97,23 @@ files:
|
|
97
97
|
- lib/sluggi/generators/sluggi_generator.rb
|
98
98
|
- lib/sluggi/history.rb
|
99
99
|
- lib/sluggi/migrations/create_slugs.rb
|
100
|
+
- lib/sluggi/model.rb
|
100
101
|
- lib/sluggi/slug.rb
|
101
102
|
- lib/sluggi/slugged.rb
|
103
|
+
- lib/sluggi/validate_exclusion_of.rb
|
104
|
+
- lib/sluggi/validate_presence.rb
|
105
|
+
- lib/sluggi/validate_uniqueness.rb
|
102
106
|
- lib/sluggi/version.rb
|
103
107
|
- sluggi.gemspec
|
104
108
|
- test/generator_test.rb
|
105
109
|
- test/history_test.rb
|
110
|
+
- test/model_test.rb
|
106
111
|
- test/slug_test.rb
|
107
112
|
- test/slugged_test.rb
|
108
113
|
- test/test_helper.rb
|
114
|
+
- test/validate_presence_test.rb
|
115
|
+
- test/validate_uniqueness_test.rb
|
116
|
+
- test/validates_exclusion_of_test.rb
|
109
117
|
homepage: https://github.com/neighborland/sluggi
|
110
118
|
licenses:
|
111
119
|
- MIT
|
@@ -126,14 +134,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
134
|
version: '0'
|
127
135
|
requirements: []
|
128
136
|
rubyforge_project:
|
129
|
-
rubygems_version: 2.
|
137
|
+
rubygems_version: 2.4.4
|
130
138
|
signing_key:
|
131
139
|
specification_version: 4
|
132
140
|
summary: Rails Slug Generator
|
133
141
|
test_files:
|
134
142
|
- test/generator_test.rb
|
135
143
|
- test/history_test.rb
|
144
|
+
- test/model_test.rb
|
136
145
|
- test/slug_test.rb
|
137
146
|
- test/slugged_test.rb
|
138
147
|
- test/test_helper.rb
|
148
|
+
- test/validate_presence_test.rb
|
149
|
+
- test/validate_uniqueness_test.rb
|
150
|
+
- test/validates_exclusion_of_test.rb
|
139
151
|
has_rdoc:
|