slugalicious 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.textile +16 -6
- data/lib/slugalicious.rb +10 -6
- data/slugalicious.gemspec +29 -33
- metadata +92 -74
- data/.document +0 -5
- data/.gitignore +0 -26
- data/.rspec +0 -1
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -107
- data/Rakefile +0 -37
- data/VERSION +0 -1
- data/spec/factories.rb +0 -14
- data/spec/slug_spec.rb +0 -65
- data/spec/slugalicious_spec.rb +0 -232
- data/spec/spec_helper.rb +0 -45
data/LICENSE
CHANGED
data/README.textile
CHANGED
@@ -3,7 +3,7 @@ h1. Slugalicious -- Easy and powerful URL slugging for Rails 3
|
|
3
3
|
_*(no monkey-patching required)*_
|
4
4
|
|
5
5
|
| *Author* | Tim Morgan |
|
6
|
-
| *Version* | 1.
|
6
|
+
| *Version* | 1.1 (Aug 13, 2011) |
|
7
7
|
| *License* | Released under the MIT license. |
|
8
8
|
|
9
9
|
h2. About
|
@@ -186,11 +186,21 @@ def find_product
|
|
186
186
|
end
|
187
187
|
</code></pre>
|
188
188
|
|
189
|
-
The old URL will remain indefinitely, but users who hit it will be redirected to
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
189
|
+
The old URL will remain indefinitely, but users who hit it will be redirected to
|
190
|
+
the new URL. Ideally, links to the old URL will be replaced over time with links
|
191
|
+
to the new URL.
|
192
|
+
|
193
|
+
The problem is that even though the old slug is inactive, it's still "taken." If
|
194
|
+
you create a product called "Keyboard", but then rename it to "Piano", the
|
195
|
+
product will claim both the "keyboard" and "piano" slugs. If you had renamed it
|
196
|
+
to make room for a different product called "Keyboard" (like a computer
|
197
|
+
keyboard), you'd find its slug is "keyboard;2" or similar.
|
198
|
+
|
199
|
+
To prevent the slug namespace from becoming more and more polluted over time,
|
200
|
+
websites generally expire inactive slugs after a period of time. To do this in
|
201
|
+
Slugalicious, write a task that periodically checks for and deletes old,
|
202
|
+
inactive @Slug@ records. Such a task could be invoked through a cron job, for
|
203
|
+
instance. An example:
|
194
204
|
|
195
205
|
<pre><code>
|
196
206
|
Slug.inactive.where([ "created_at < ?", 30.days.ago ]).delete_all
|
data/lib/slugalicious.rb
CHANGED
@@ -147,22 +147,26 @@ module Slugalicious
|
|
147
147
|
|
148
148
|
module InstanceMethods
|
149
149
|
|
150
|
+
def slug_object
|
151
|
+
slugs.loaded? ? slugs.detect(&:active) : slugs.active.first
|
152
|
+
end
|
153
|
+
private :slug_object
|
154
|
+
|
150
155
|
# @return [String, nil] The slug for this object, or @nil@ if none has been
|
151
156
|
# assigned.
|
152
157
|
|
153
158
|
def slug
|
154
|
-
|
159
|
+
Rails.cache.fetch("Slug/#{self.class.to_s}/#{id}/slug") do
|
160
|
+
slug_object.try(:slug)
|
161
|
+
end
|
155
162
|
end
|
156
163
|
|
157
164
|
# @return [String, nil] The full slug and path for this object, with scope
|
158
165
|
# included, or @nil@ if none has been assigned.
|
159
166
|
|
160
167
|
def slug_with_path
|
161
|
-
|
162
|
-
|
163
|
-
slug.scope.to_s + slug.slug
|
164
|
-
else
|
165
|
-
nil
|
168
|
+
Rails.cache.fetch("Slug/#{self.class.to_s}/#{id}/slug_with_path") do
|
169
|
+
slug_object ? (slug_object.scope.to_s + slug_object.slug) : nil
|
166
170
|
end
|
167
171
|
end
|
168
172
|
|
data/slugalicious.gemspec
CHANGED
@@ -1,68 +1,64 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{slugalicious}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{
|
11
|
+
s.authors = [%q{Tim Morgan}]
|
12
|
+
s.date = %q{2011-08-13}
|
13
13
|
s.description = %q{Slugalicious adds simple and powerful slugging to your ActiveRecord models.}
|
14
14
|
s.email = %q{git@timothymorgan.info}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
17
|
+
"README.textile"
|
18
18
|
]
|
19
19
|
s.files = [
|
20
|
-
"
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
"Rakefile",
|
28
|
-
"VERSION",
|
29
|
-
"lib/slugalicious.rb",
|
30
|
-
"lib/slugalicious_generator.rb",
|
31
|
-
"slugalicious.gemspec",
|
32
|
-
"spec/factories.rb",
|
33
|
-
"spec/slug_spec.rb",
|
34
|
-
"spec/slugalicious_spec.rb",
|
35
|
-
"spec/spec_helper.rb",
|
36
|
-
"templates/create_slugs.rb",
|
37
|
-
"templates/slug.rb"
|
20
|
+
"LICENSE",
|
21
|
+
"README.textile",
|
22
|
+
"lib/slugalicious.rb",
|
23
|
+
"lib/slugalicious_generator.rb",
|
24
|
+
"slugalicious.gemspec",
|
25
|
+
"templates/create_slugs.rb",
|
26
|
+
"templates/slug.rb"
|
38
27
|
]
|
39
28
|
s.homepage = %q{http://github.com/riscfuture/slugalicious}
|
40
|
-
s.
|
41
|
-
s.require_paths = ["lib"]
|
29
|
+
s.require_paths = [%q{lib}]
|
42
30
|
s.required_ruby_version = Gem::Requirement.new(">= 1.9")
|
43
|
-
s.rubygems_version = %q{1.
|
31
|
+
s.rubygems_version = %q{1.8.7}
|
44
32
|
s.summary = %q{Easy-to-use and powerful slugging for Rails 3}
|
45
|
-
s.test_files = [
|
46
|
-
"spec/factories.rb",
|
47
|
-
"spec/slug_spec.rb",
|
48
|
-
"spec/slugalicious_spec.rb",
|
49
|
-
"spec/spec_helper.rb"
|
50
|
-
]
|
51
33
|
|
52
34
|
if s.respond_to? :specification_version then
|
53
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
35
|
s.specification_version = 3
|
55
36
|
|
56
37
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
38
|
s.add_runtime_dependency(%q<rails>, [">= 3.0"])
|
58
39
|
s.add_runtime_dependency(%q<stringex>, [">= 0"])
|
40
|
+
s.add_runtime_dependency(%q<jeweler>, [">= 0"])
|
41
|
+
s.add_runtime_dependency(%q<yard>, [">= 0"])
|
42
|
+
s.add_runtime_dependency(%q<RedCloth>, [">= 0"])
|
43
|
+
s.add_runtime_dependency(%q<sqlite3>, [">= 0"])
|
44
|
+
s.add_runtime_dependency(%q<rspec>, [">= 0"])
|
59
45
|
else
|
60
46
|
s.add_dependency(%q<rails>, [">= 3.0"])
|
61
47
|
s.add_dependency(%q<stringex>, [">= 0"])
|
48
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
49
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
50
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
51
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
52
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
62
53
|
end
|
63
54
|
else
|
64
55
|
s.add_dependency(%q<rails>, [">= 3.0"])
|
65
56
|
s.add_dependency(%q<stringex>, [">= 0"])
|
57
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
58
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
59
|
+
s.add_dependency(%q<RedCloth>, [">= 0"])
|
60
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
66
62
|
end
|
67
63
|
end
|
68
64
|
|
metadata
CHANGED
@@ -1,112 +1,130 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: slugalicious
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 1
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
version: 1.0.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Tim Morgan
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-08-13 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: rails
|
22
|
-
requirement: &
|
16
|
+
requirement: &2162887120 !ruby/object:Gem::Requirement
|
23
17
|
none: false
|
24
|
-
requirements:
|
25
|
-
- -
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
- 3
|
29
|
-
- 0
|
30
|
-
version: "3.0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.0'
|
31
22
|
type: :runtime
|
32
23
|
prerelease: false
|
33
|
-
version_requirements: *
|
34
|
-
- !ruby/object:Gem::Dependency
|
24
|
+
version_requirements: *2162887120
|
25
|
+
- !ruby/object:Gem::Dependency
|
35
26
|
name: stringex
|
36
|
-
requirement: &
|
27
|
+
requirement: &2162886240 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2162886240
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: jeweler
|
38
|
+
requirement: &2162884900 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2162884900
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: yard
|
49
|
+
requirement: &2162884020 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2162884020
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: RedCloth
|
60
|
+
requirement: &2162883400 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *2162883400
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: &2162882740 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :runtime
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *2162882740
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: rspec
|
82
|
+
requirement: &2162882100 !ruby/object:Gem::Requirement
|
37
83
|
none: false
|
38
|
-
requirements:
|
39
|
-
- -
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
|
42
|
-
- 0
|
43
|
-
version: "0"
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
44
88
|
type: :runtime
|
45
89
|
prerelease: false
|
46
|
-
version_requirements: *
|
90
|
+
version_requirements: *2162882100
|
47
91
|
description: Slugalicious adds simple and powerful slugging to your ActiveRecord models.
|
48
92
|
email: git@timothymorgan.info
|
49
93
|
executables: []
|
50
|
-
|
51
94
|
extensions: []
|
52
|
-
|
53
|
-
extra_rdoc_files:
|
95
|
+
extra_rdoc_files:
|
54
96
|
- LICENSE
|
55
97
|
- README.textile
|
56
|
-
files:
|
57
|
-
- .document
|
58
|
-
- .gitignore
|
59
|
-
- .rspec
|
60
|
-
- Gemfile
|
61
|
-
- Gemfile.lock
|
98
|
+
files:
|
62
99
|
- LICENSE
|
63
100
|
- README.textile
|
64
|
-
- Rakefile
|
65
|
-
- VERSION
|
66
101
|
- lib/slugalicious.rb
|
67
102
|
- lib/slugalicious_generator.rb
|
68
103
|
- slugalicious.gemspec
|
69
|
-
- spec/factories.rb
|
70
|
-
- spec/slug_spec.rb
|
71
|
-
- spec/slugalicious_spec.rb
|
72
|
-
- spec/spec_helper.rb
|
73
104
|
- templates/create_slugs.rb
|
74
105
|
- templates/slug.rb
|
75
|
-
has_rdoc: true
|
76
106
|
homepage: http://github.com/riscfuture/slugalicious
|
77
107
|
licenses: []
|
78
|
-
|
79
108
|
post_install_message:
|
80
|
-
rdoc_options:
|
81
|
-
|
82
|
-
require_paths:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
83
111
|
- lib
|
84
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
113
|
none: false
|
86
|
-
requirements:
|
87
|
-
- -
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
|
90
|
-
|
91
|
-
- 9
|
92
|
-
version: "1.9"
|
93
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.9'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
119
|
none: false
|
95
|
-
requirements:
|
96
|
-
- -
|
97
|
-
- !ruby/object:Gem::Version
|
98
|
-
|
99
|
-
- 0
|
100
|
-
version: "0"
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
101
124
|
requirements: []
|
102
|
-
|
103
125
|
rubyforge_project:
|
104
|
-
rubygems_version: 1.
|
126
|
+
rubygems_version: 1.8.7
|
105
127
|
signing_key:
|
106
128
|
specification_version: 3
|
107
129
|
summary: Easy-to-use and powerful slugging for Rails 3
|
108
|
-
test_files:
|
109
|
-
- spec/factories.rb
|
110
|
-
- spec/slug_spec.rb
|
111
|
-
- spec/slugalicious_spec.rb
|
112
|
-
- spec/spec_helper.rb
|
130
|
+
test_files: []
|
data/.document
DELETED
data/.gitignore
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
## MAC OS
|
2
|
-
.DS_Store
|
3
|
-
|
4
|
-
## TEXTMATE
|
5
|
-
*.tmproj
|
6
|
-
tmtags
|
7
|
-
|
8
|
-
## EMACS
|
9
|
-
*~
|
10
|
-
\#*
|
11
|
-
.\#*
|
12
|
-
|
13
|
-
## VIM
|
14
|
-
*.swp
|
15
|
-
|
16
|
-
## PROJECT::GENERAL
|
17
|
-
coverage
|
18
|
-
rdoc
|
19
|
-
pkg
|
20
|
-
.bundle
|
21
|
-
.rvmrc
|
22
|
-
test.sqlite
|
23
|
-
|
24
|
-
## PROJECT::DOCUMENTATION
|
25
|
-
.yardoc
|
26
|
-
doc
|
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
-cfs
|
data/Gemfile
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
source :rubygems
|
2
|
-
|
3
|
-
# DEPENDENCIES
|
4
|
-
gem 'rails', '>= 3.0'
|
5
|
-
gem 'stringex'
|
6
|
-
|
7
|
-
# DEVELOPMENT
|
8
|
-
gem 'jeweler'
|
9
|
-
gem 'yard'
|
10
|
-
gem 'RedCloth', require: 'redcloth'
|
11
|
-
gem 'sqlite3'
|
12
|
-
|
13
|
-
# TEST
|
14
|
-
gem 'rspec'
|
15
|
-
group :test do
|
16
|
-
gem 'factory_girl'
|
17
|
-
end
|
data/Gemfile.lock
DELETED
@@ -1,107 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
RedCloth (4.2.3)
|
5
|
-
abstract (1.0.0)
|
6
|
-
actionmailer (3.0.1)
|
7
|
-
actionpack (= 3.0.1)
|
8
|
-
mail (~> 2.2.5)
|
9
|
-
actionpack (3.0.1)
|
10
|
-
activemodel (= 3.0.1)
|
11
|
-
activesupport (= 3.0.1)
|
12
|
-
builder (~> 2.1.2)
|
13
|
-
erubis (~> 2.6.6)
|
14
|
-
i18n (~> 0.4.1)
|
15
|
-
rack (~> 1.2.1)
|
16
|
-
rack-mount (~> 0.6.12)
|
17
|
-
rack-test (~> 0.5.4)
|
18
|
-
tzinfo (~> 0.3.23)
|
19
|
-
activemodel (3.0.1)
|
20
|
-
activesupport (= 3.0.1)
|
21
|
-
builder (~> 2.1.2)
|
22
|
-
i18n (~> 0.4.1)
|
23
|
-
activerecord (3.0.1)
|
24
|
-
activemodel (= 3.0.1)
|
25
|
-
activesupport (= 3.0.1)
|
26
|
-
arel (~> 1.0.0)
|
27
|
-
tzinfo (~> 0.3.23)
|
28
|
-
activeresource (3.0.1)
|
29
|
-
activemodel (= 3.0.1)
|
30
|
-
activesupport (= 3.0.1)
|
31
|
-
activesupport (3.0.1)
|
32
|
-
arel (1.0.1)
|
33
|
-
activesupport (~> 3.0.0)
|
34
|
-
builder (2.1.2)
|
35
|
-
diff-lcs (1.1.2)
|
36
|
-
erubis (2.6.6)
|
37
|
-
abstract (>= 1.0.0)
|
38
|
-
factory_girl (1.3.2)
|
39
|
-
ffi (0.6.3)
|
40
|
-
rake (>= 0.8.7)
|
41
|
-
gemcutter (0.6.1)
|
42
|
-
git (1.2.5)
|
43
|
-
i18n (0.4.2)
|
44
|
-
jeweler (1.4.0)
|
45
|
-
gemcutter (>= 0.1.0)
|
46
|
-
git (>= 1.2.5)
|
47
|
-
rubyforge (>= 2.0.0)
|
48
|
-
json_pure (1.4.6)
|
49
|
-
mail (2.2.9)
|
50
|
-
activesupport (>= 2.3.6)
|
51
|
-
i18n (~> 0.4.1)
|
52
|
-
mime-types (~> 1.16)
|
53
|
-
treetop (~> 1.4.8)
|
54
|
-
mime-types (1.16)
|
55
|
-
polyglot (0.3.1)
|
56
|
-
rack (1.2.1)
|
57
|
-
rack-mount (0.6.13)
|
58
|
-
rack (>= 1.0.0)
|
59
|
-
rack-test (0.5.6)
|
60
|
-
rack (>= 1.0)
|
61
|
-
rails (3.0.1)
|
62
|
-
actionmailer (= 3.0.1)
|
63
|
-
actionpack (= 3.0.1)
|
64
|
-
activerecord (= 3.0.1)
|
65
|
-
activeresource (= 3.0.1)
|
66
|
-
activesupport (= 3.0.1)
|
67
|
-
bundler (~> 1.0.0)
|
68
|
-
railties (= 3.0.1)
|
69
|
-
railties (3.0.1)
|
70
|
-
actionpack (= 3.0.1)
|
71
|
-
activesupport (= 3.0.1)
|
72
|
-
rake (>= 0.8.4)
|
73
|
-
thor (~> 0.14.0)
|
74
|
-
rake (0.8.7)
|
75
|
-
rspec (2.0.1)
|
76
|
-
rspec-core (~> 2.0.1)
|
77
|
-
rspec-expectations (~> 2.0.1)
|
78
|
-
rspec-mocks (~> 2.0.1)
|
79
|
-
rspec-core (2.0.1)
|
80
|
-
rspec-expectations (2.0.1)
|
81
|
-
diff-lcs (>= 1.1.2)
|
82
|
-
rspec-mocks (2.0.1)
|
83
|
-
rspec-core (~> 2.0.1)
|
84
|
-
rspec-expectations (~> 2.0.1)
|
85
|
-
rubyforge (2.0.4)
|
86
|
-
json_pure (>= 1.1.7)
|
87
|
-
sqlite3 (0.1.1)
|
88
|
-
ffi (>= 0.6.3)
|
89
|
-
stringex (1.2.0)
|
90
|
-
thor (0.14.3)
|
91
|
-
treetop (1.4.8)
|
92
|
-
polyglot (>= 0.3.1)
|
93
|
-
tzinfo (0.3.23)
|
94
|
-
yard (0.6.1)
|
95
|
-
|
96
|
-
PLATFORMS
|
97
|
-
ruby
|
98
|
-
|
99
|
-
DEPENDENCIES
|
100
|
-
RedCloth
|
101
|
-
factory_girl
|
102
|
-
jeweler
|
103
|
-
rails (>= 3.0)
|
104
|
-
rspec
|
105
|
-
sqlite3
|
106
|
-
stringex
|
107
|
-
yard
|
data/Rakefile
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'rake'
|
2
|
-
begin
|
3
|
-
require 'bundler'
|
4
|
-
rescue LoadError
|
5
|
-
puts "Bundler is not installed; install with `gem install bundler`."
|
6
|
-
exit 1
|
7
|
-
end
|
8
|
-
|
9
|
-
Bundler.require :default
|
10
|
-
|
11
|
-
Jeweler::Tasks.new do |gem|
|
12
|
-
gem.name = "slugalicious"
|
13
|
-
gem.summary = %Q{Easy-to-use and powerful slugging for Rails 3}
|
14
|
-
gem.description = %Q{Slugalicious adds simple and powerful slugging to your ActiveRecord models.}
|
15
|
-
gem.email = "git@timothymorgan.info"
|
16
|
-
gem.homepage = "http://github.com/riscfuture/slugalicious"
|
17
|
-
gem.authors = [ "Tim Morgan" ]
|
18
|
-
gem.required_ruby_version = '>= 1.9'
|
19
|
-
gem.add_dependency "rails", ">= 3.0"
|
20
|
-
gem.add_dependency 'stringex'
|
21
|
-
end
|
22
|
-
Jeweler::GemcutterTasks.new
|
23
|
-
|
24
|
-
require 'rspec/core/rake_task'
|
25
|
-
RSpec::Core::RakeTask.new
|
26
|
-
|
27
|
-
YARD::Rake::YardocTask.new('doc') do |doc|
|
28
|
-
doc.options << "-m" << "textile"
|
29
|
-
doc.options << "--protected"
|
30
|
-
doc.options << "-r" << "README.textile"
|
31
|
-
doc.options << "-o" << "doc"
|
32
|
-
doc.options << "--title" << "Slugalicious Documentation".inspect
|
33
|
-
|
34
|
-
doc.files = [ 'lib/**/*', 'README.textile', 'templates/slug.rb' ]
|
35
|
-
end
|
36
|
-
|
37
|
-
task(default: :spec)
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.0
|
data/spec/factories.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
Factory.define :slug do |f|
|
2
|
-
f.sequence(:slug) { |n| "slug-#{n}" }
|
3
|
-
f.active true
|
4
|
-
end
|
5
|
-
|
6
|
-
Factory.define :user do |f|
|
7
|
-
f.first_name "Doctor"
|
8
|
-
f.last_name "Spaceman"
|
9
|
-
end
|
10
|
-
|
11
|
-
Factory.define :abuser do |f|
|
12
|
-
f.first_name "Doctor"
|
13
|
-
f.last_name "Spaceman"
|
14
|
-
end
|
data/spec/slug_spec.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Slug do
|
4
|
-
before :each do
|
5
|
-
@record = Factory(:user)
|
6
|
-
Slug.delete_all
|
7
|
-
end
|
8
|
-
|
9
|
-
describe "validation" do
|
10
|
-
it "should not allow an active slug to be created if one already exists" do
|
11
|
-
Factory(:slug, sluggable: @record)
|
12
|
-
slug = Factory.build(:slug, sluggable: @record)
|
13
|
-
slug.should_not be_valid
|
14
|
-
slug.errors[:active].should_not be_empty
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should not allow a slug to be made active if one already exists" do
|
18
|
-
Factory(:slug, sluggable: @record)
|
19
|
-
slug = Factory(:slug, sluggable: @record, active: false)
|
20
|
-
slug.active = true
|
21
|
-
slug.should_not be_valid
|
22
|
-
slug.errors[:active].should_not be_empty
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should allow an active slug to be created if none exists" do
|
26
|
-
slug = Factory.build(:slug, sluggable: @record)
|
27
|
-
slug.should be_valid
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should allow a slug to be made active if none exists" do
|
31
|
-
slug = Factory(:slug, sluggable: @record, active: false)
|
32
|
-
slug.active = true
|
33
|
-
slug.should be_valid
|
34
|
-
end
|
35
|
-
|
36
|
-
it "should allow an inactive slug to be modified if the active field is not changing" do
|
37
|
-
Factory(:slug, sluggable: @record)
|
38
|
-
slug = Factory(:slug, sluggable: @record, active: false)
|
39
|
-
slug.scope = 'test'
|
40
|
-
slug.should be_valid
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "#activate!" do
|
45
|
-
it "should mark the slug as active" do
|
46
|
-
slug = Factory(:slug, sluggable: Factory(:user), active: false)
|
47
|
-
slug.activate!
|
48
|
-
slug.should be_active
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should deactivate all the record's other slugs" do
|
52
|
-
record = Factory(:user)
|
53
|
-
s1 = Slug.for(record).first
|
54
|
-
s2 = Factory(:slug, sluggable: record, active: false)
|
55
|
-
s3 = Factory(:slug, sluggable: record, active: false)
|
56
|
-
|
57
|
-
slug = Factory(:slug, sluggable: record, active: false)
|
58
|
-
slug.activate!
|
59
|
-
|
60
|
-
s1.reload.should_not be_active
|
61
|
-
s2.reload.should_not be_active
|
62
|
-
s3.reload.should_not be_active
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
data/spec/slugalicious_spec.rb
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
|
3
|
-
describe Slugalicious do
|
4
|
-
before :each do
|
5
|
-
@model = Class.new
|
6
|
-
@model.send :include, ActiveModel::Validations
|
7
|
-
@model.send :include, ActiveModel::Validations::Callbacks
|
8
|
-
@model.send :include, ActiveRecord::Callbacks
|
9
|
-
@model.send :include, ActiveRecord::Validations
|
10
|
-
@model.stub!(:has_many)
|
11
|
-
@model.send :include, Slugalicious
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "#slugged" do
|
15
|
-
it "should raise an error if no generators are given" do
|
16
|
-
expect { @model.send(:slugged, { options: 'here' }) }.to raise_error(ArgumentError)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "#to_param" do
|
21
|
-
it "should return the slug" do
|
22
|
-
user = Factory(:user)
|
23
|
-
user.slug.should_not be_nil
|
24
|
-
user.to_param.should eql(user.slug)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should return the full slug path when scoped" do
|
28
|
-
User._slug_procs.clear
|
29
|
-
User.send :slugged, :first_name, scope: :last_name
|
30
|
-
|
31
|
-
user = Factory(:user, last_name: 'test/')
|
32
|
-
user.to_param.should eql('test/doctor')
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context "slug generation" do
|
37
|
-
before :each do
|
38
|
-
User._slug_procs.clear
|
39
|
-
User._slug_blacklist.clear
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should set the slug according to the generator and apply the slugifier" do
|
43
|
-
User.send :slugged, :first_name
|
44
|
-
object = Factory(:user, first_name: "Sancho", last_name: "Sample")
|
45
|
-
object.slug.should eql("sancho")
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should generate from a proc as well as a symbol" do
|
49
|
-
User.send :slugged, ->(person) { "#{person.first_name} #{person.last_name}" }
|
50
|
-
object = Factory(:user, first_name: "Foo", last_name: "bar")
|
51
|
-
object.slug.should eql("foo-bar")
|
52
|
-
end
|
53
|
-
|
54
|
-
it "should give priority to the first non-nil slug" do
|
55
|
-
User.send :slugged, :gender, :last_name
|
56
|
-
object = Factory(:user, gender: nil, last_name: "Bar")
|
57
|
-
object.slug.should eql("bar")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "should not create a new slug if an existing one matches" do
|
61
|
-
User.send :slugged, :first_name, :last_name
|
62
|
-
object = Factory(:user, first_name: 'Foo', last_name: "Bar")
|
63
|
-
object.slug.should eql("foo")
|
64
|
-
other_slug = Factory(:slug, slug: 'bar', sluggable: object, active: false)
|
65
|
-
|
66
|
-
object.update_attribute :last_name, 'baz'
|
67
|
-
object.slug.should eql("foo")
|
68
|
-
other_slug.reload.active.should be_false
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should ignore slugs from other models" do
|
72
|
-
User.send :slugged, :first_name, :last_name
|
73
|
-
Factory(:abuser, last_name: 'Foo').slug.should eql('foo')
|
74
|
-
Factory(:user, first_name: 'Foo', last_name: 'Foo').slug.should eql('foo')
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should use the last-resort generator if nothing else is unique" do
|
78
|
-
User.send :slugged, :first_name, :last_name
|
79
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar').slug.should eql('foo')
|
80
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar').slug.should eql('bar')
|
81
|
-
user = Factory(:user, first_name: 'Foo', last_name: 'Bar')
|
82
|
-
user.slug.should eql("foo;#{user.id}")
|
83
|
-
end
|
84
|
-
|
85
|
-
it "should raise an error if all generators return nil" do
|
86
|
-
User.send :slugged, :gender, :birthdate
|
87
|
-
-> { Factory(:user, gender: nil, birthdate: nil) }.should raise_error
|
88
|
-
end
|
89
|
-
|
90
|
-
it "should use the first non-nil slug for the last-resort generator" do
|
91
|
-
User.send :slugged, :gender, :first_name, :last_name
|
92
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', gender: nil)
|
93
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', gender: nil)
|
94
|
-
user = Factory(:user, first_name: 'Foo', last_name: 'Bar', gender: nil)
|
95
|
-
user.slug.should eql("foo;#{user.id}")
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should use a custom id separator if given" do
|
99
|
-
User.send :slugged, :first_name, :last_name, id_separator: ':'
|
100
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar')
|
101
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar')
|
102
|
-
user = Factory(:user, first_name: 'Foo', last_name: 'Bar')
|
103
|
-
user.slug.should eql("foo:#{user.id}")
|
104
|
-
end
|
105
|
-
|
106
|
-
it "should avoid blacklisted slugs" do
|
107
|
-
User.send :slugged, :first_name, :last_name
|
108
|
-
Factory(:user, first_name: 'New', last_name: 'Bar').slug.should eql('bar')
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should use a custom blacklist" do
|
112
|
-
User.send :slugged, :first_name, :last_name, blacklist: %w( foo bar )
|
113
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz').slug.should eql('baz')
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should wrap the blacklist array" do
|
117
|
-
User.send :slugged, :first_name, :last_name, blacklist: 'foo'
|
118
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz').slug.should eql('baz')
|
119
|
-
end
|
120
|
-
|
121
|
-
it "should only search for available slugs inside the scope if given" do
|
122
|
-
User.send :slugged, :first_name, :last_name, scope: :callsign
|
123
|
-
|
124
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', callsign: 'One').slug.should eql('foo')
|
125
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz', callsign: 'One').slug.should eql('baz')
|
126
|
-
Factory(:user, first_name: 'Boo', last_name: 'Bar', callsign: 'One').slug.should eql('boo')
|
127
|
-
|
128
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', callsign: 'Two').slug.should eql('foo')
|
129
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz', callsign: 'Two').slug.should eql('baz')
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should accept a proc for a scope" do
|
133
|
-
User.send :slugged, :first_name, :last_name, scope: ->(object) { object.callsign[0] }
|
134
|
-
|
135
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', callsign: 'One').slug.should eql('foo')
|
136
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz', callsign: 'Only').slug.should eql('baz')
|
137
|
-
Factory(:user, first_name: 'Boo', last_name: 'Bar', callsign: 'Ocho').slug.should eql('boo')
|
138
|
-
|
139
|
-
Factory(:user, first_name: 'Foo', last_name: 'Bar', callsign: 'Two').slug.should eql('foo')
|
140
|
-
Factory(:user, first_name: 'Foo', last_name: 'Baz', callsign: 'Tres').slug.should eql('baz')
|
141
|
-
end
|
142
|
-
|
143
|
-
it "should enforce a maximum length of 126 characters" do
|
144
|
-
User.send :slugged, :first_name
|
145
|
-
|
146
|
-
user = Factory(:user)
|
147
|
-
user.first_name = 'A'*500
|
148
|
-
user.save(validate: false)
|
149
|
-
user.slug.should eql('a'*126)
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should shorten left of the ID separator" do
|
153
|
-
User.send :slugged, :first_name
|
154
|
-
|
155
|
-
user1 = Factory(:user)
|
156
|
-
user1.first_name = 'A'*500
|
157
|
-
user1.save(validate: false)
|
158
|
-
|
159
|
-
user2 = Factory(:user)
|
160
|
-
user2.first_name = 'A'*500
|
161
|
-
user2.save(validate: false)
|
162
|
-
|
163
|
-
user2.slug.size.should eql(126)
|
164
|
-
user2.slug.should match(/^a+;#{user2.id}$/)
|
165
|
-
end
|
166
|
-
|
167
|
-
it "should raise an error if no unique slugs are available" do
|
168
|
-
old_length = Slugalicious::MAX_SLUG_LENGTH
|
169
|
-
Slugalicious::MAX_SLUG_LENGTH = 1
|
170
|
-
|
171
|
-
User.send :slugged, ->(object) { 'f' }, blacklist: 'f'
|
172
|
-
-> { Factory(:user, first_name: 'Foo', last_name: 'Bar') }.should raise_error
|
173
|
-
|
174
|
-
Slugalicious::MAX_SLUG_LENGTH = old_length
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
describe "#find_from_slug" do
|
179
|
-
it "should return a Slug object for a slug" do
|
180
|
-
User.send :slugged, :first_name, :last_name
|
181
|
-
user1 = Factory(:user, first_name: "FN1", last_name: "LN1")
|
182
|
-
User.find_from_slug('fn1').should eql(user1)
|
183
|
-
end
|
184
|
-
|
185
|
-
it "should exclude slugs of other models" do
|
186
|
-
User.send :slugged, :first_name, :last_name
|
187
|
-
user1 = Factory(:user, first_name: "FN1", last_name: "LN1")
|
188
|
-
Factory(:abuser, first_name: 'FN1')
|
189
|
-
User.find_from_slug('fn1').should eql(user1)
|
190
|
-
end
|
191
|
-
|
192
|
-
it "should locate an object within a given scope" do
|
193
|
-
User.send :slugged, :first_name, scope: :last_name
|
194
|
-
user1 = Factory(:user, first_name: "FN1", last_name: "LN1")
|
195
|
-
user2 = Factory(:user, first_name: "FN1", last_name: "LN2")
|
196
|
-
User.find_from_slug('fn1', 'LN1').should eql(user1)
|
197
|
-
User.find_from_slug('fn1', 'LN2').should eql(user2)
|
198
|
-
end
|
199
|
-
|
200
|
-
it "should raise ActiveRecord::RecordNotFound if the slug does not exist" do
|
201
|
-
-> { User.find_from_slug('nonexist') }.should raise_error(ActiveRecord::RecordNotFound)
|
202
|
-
end
|
203
|
-
|
204
|
-
it "should raise ActiveRecord::RecordNotFound if the slug does not exist in scope" do
|
205
|
-
User.send :slugged, :first_name, scope: :last_name
|
206
|
-
Factory(:user, first_name: "FN1", last_name: "LN1")
|
207
|
-
Factory(:user, first_name: "FN2", last_name: "LN2")
|
208
|
-
-> { User.find_from_slug('fn2', 'ln1') }.should raise_error(ActiveRecord::RecordNotFound)
|
209
|
-
end
|
210
|
-
|
211
|
-
it "should find inactive slugs" do
|
212
|
-
User.send :slugged, :first_name
|
213
|
-
user = Factory(:user, first_name: 'New')
|
214
|
-
Factory(:slug, sluggable: user, slug: 'old', active: false)
|
215
|
-
User.find_from_slug('old').should eql(user)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
describe "#find_from_slug_path" do
|
220
|
-
it "should call #find_from_slug with the slug and no scope for unscoped models" do
|
221
|
-
User.send :slugged, :first_name
|
222
|
-
User.should_receive(:find_from_slug).once.with("test", '')
|
223
|
-
User.find_from_slug_path("test")
|
224
|
-
end
|
225
|
-
|
226
|
-
it "should call #find_from_slug with the slug and scope for scoped models" do
|
227
|
-
User.send :slugged, :first_name, scope: :last_name
|
228
|
-
User.should_receive(:find_from_slug).once.with("test", "path/to/")
|
229
|
-
User.find_from_slug_path("path/to/test")
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
Bundler.require :default, :test
|
2
|
-
require 'active_support'
|
3
|
-
require 'active_record'
|
4
|
-
|
5
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
-
|
8
|
-
require 'slugalicious'
|
9
|
-
|
10
|
-
ActiveRecord::Base.establish_connection(
|
11
|
-
adapter: 'sqlite3',
|
12
|
-
database: 'test.sqlite'
|
13
|
-
)
|
14
|
-
require "#{File.dirname __FILE__}/../templates/slug"
|
15
|
-
|
16
|
-
class User < ActiveRecord::Base
|
17
|
-
include Slugalicious
|
18
|
-
slugged :last_name, ->(user) { "#{user.first_name} #{user.last_name}" }
|
19
|
-
end
|
20
|
-
class Abuser < ActiveRecord::Base
|
21
|
-
include Slugalicious
|
22
|
-
slugged :last_name, ->(user) { "#{user.first_name} #{user.last_name}" }
|
23
|
-
end
|
24
|
-
|
25
|
-
require "#{File.dirname __FILE__}/factories"
|
26
|
-
|
27
|
-
RSpec.configure do |config|
|
28
|
-
config.before(:each) do
|
29
|
-
Slug.connection.execute "DROP TABLE IF EXISTS slugs"
|
30
|
-
Slug.connection.execute <<-SQL
|
31
|
-
CREATE TABLE slugs (
|
32
|
-
id INTEGER PRIMARY KEY ASC,
|
33
|
-
sluggable_type VARCHAR(126) NOT NULL,
|
34
|
-
sluggable_id INTEGER NOT NULL,
|
35
|
-
active BOOLEAN NOT NULL DEFAULT 1,
|
36
|
-
slug VARCHAR(126) NOT NULL,
|
37
|
-
scope VARCHAR(126)
|
38
|
-
)
|
39
|
-
SQL
|
40
|
-
User.connection.execute "DROP TABLE IF EXISTS users"
|
41
|
-
User.connection.execute "CREATE TABLE users (id INTEGER PRIMARY KEY ASC, first_name TEXT, last_name TEXT, callsign TEXT, gender VARCHAR(7))"
|
42
|
-
Abuser.connection.execute "DROP TABLE IF EXISTS abusers"
|
43
|
-
Abuser.connection.execute "CREATE TABLE abusers (id INTEGER PRIMARY KEY ASC, first_name TEXT, last_name TEXT, callsign TEXT, gender VARCHAR(7))"
|
44
|
-
end
|
45
|
-
end
|