dm-is-slug 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/README.rdoc +15 -1
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/dm-is-slug.gemspec +5 -2
- data/lib/dm-is-slug/is/slug.rb +33 -6
- data/spec/integration/slug_spec.rb +63 -4
- metadata +22 -13
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -42,9 +42,23 @@ DataMapper plugin for creating and slugs(permalinks).
|
|
42
42
|
is :slug, :source => :slug_for_email, :size => 255
|
43
43
|
end
|
44
44
|
|
45
|
+
=== Scoped slugs
|
46
|
+
|
47
|
+
class Post
|
48
|
+
include DataMapper::Resource
|
49
|
+
|
50
|
+
property :id, Serial
|
51
|
+
property :title, String
|
52
|
+
property :content, String
|
53
|
+
property :category, String
|
54
|
+
|
55
|
+
# Same as above but slugs will be unique only within their category.
|
56
|
+
is :slug, :source => :title, :slug => :category
|
57
|
+
end
|
58
|
+
|
45
59
|
=== Finding objects by slug
|
46
60
|
|
47
|
-
|
61
|
+
post = Post.first(:slug => "your_slug")
|
48
62
|
|
49
63
|
== Development
|
50
64
|
|
data/Rakefile
CHANGED
@@ -21,6 +21,7 @@ begin
|
|
21
21
|
gem.authors = ['Aaron Qian', 'James Herdman', 'Nik Radford', 'Paul', 'Mike Frawley', 'Alexander Mankuta']
|
22
22
|
|
23
23
|
gem.add_dependency "dm-core", "~> 1.0.2"
|
24
|
+
gem.add_dependency "dm-validations", "~> 1.0.2"
|
24
25
|
gem.add_dependency "unidecode", "~> 1.0.0"
|
25
26
|
|
26
27
|
gem.add_development_dependency 'rspec', '~> 1.3'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
1
|
+
1.1.0
|
data/dm-is-slug.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dm-is-slug}
|
8
|
-
s.version = "1.0
|
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
11
|
s.authors = ["Aaron Qian", "James Herdman", "Nik Radford", "Paul", "Mike Frawley", "Alexander Mankuta"]
|
12
|
-
s.date = %q{2010-10-
|
12
|
+
s.date = %q{2010-10-28}
|
13
13
|
s.description = %q{DataMapper plugin that generates unique slugs}
|
14
14
|
s.email = ["aq1018@gmail.com", "james.herdman@gmail.com", "nik [a] terminaldischarge [d] net", "maverick.stoklosa@gmail.com", "frawl021@gmail.com", "cheba+github@pointlessone.org"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -55,15 +55,18 @@ Gem::Specification.new do |s|
|
|
55
55
|
|
56
56
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
57
|
s.add_runtime_dependency(%q<dm-core>, ["~> 1.0.2"])
|
58
|
+
s.add_runtime_dependency(%q<dm-validations>, ["~> 1.0.2"])
|
58
59
|
s.add_runtime_dependency(%q<unidecode>, ["~> 1.0.0"])
|
59
60
|
s.add_development_dependency(%q<rspec>, ["~> 1.3"])
|
60
61
|
else
|
61
62
|
s.add_dependency(%q<dm-core>, ["~> 1.0.2"])
|
63
|
+
s.add_dependency(%q<dm-validations>, ["~> 1.0.2"])
|
62
64
|
s.add_dependency(%q<unidecode>, ["~> 1.0.0"])
|
63
65
|
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
64
66
|
end
|
65
67
|
else
|
66
68
|
s.add_dependency(%q<dm-core>, ["~> 1.0.2"])
|
69
|
+
s.add_dependency(%q<dm-validations>, ["~> 1.0.2"])
|
67
70
|
s.add_dependency(%q<unidecode>, ["~> 1.0.0"])
|
68
71
|
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
69
72
|
end
|
data/lib/dm-is-slug/is/slug.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'unidecode'
|
2
2
|
require 'dm-core'
|
3
3
|
require 'dm-core/support/chainable'
|
4
|
+
require 'dm-validations'
|
4
5
|
|
5
6
|
module DataMapper
|
6
7
|
module Is
|
@@ -66,6 +67,12 @@ module DataMapper
|
|
66
67
|
@slug_options[:permanent_slug] = options.delete(:permanent_slug)
|
67
68
|
@slug_options[:permanent_slug] = true if @slug_options[:permanent_slug].nil?
|
68
69
|
|
70
|
+
if options.has_key? :scope
|
71
|
+
@slug_options[:scope] = [options.delete(:scope)].flatten
|
72
|
+
end
|
73
|
+
|
74
|
+
@slug_options[:unique] = options.delete(:unique) || false
|
75
|
+
|
69
76
|
@slug_options[:source] = options.delete(:source)
|
70
77
|
raise InvalidSlugSourceError, 'You must specify a :source to generate slug.' unless slug_source
|
71
78
|
|
@@ -74,10 +81,16 @@ module DataMapper
|
|
74
81
|
if slug_property && slug_property.class >= DataMapper::Property::String
|
75
82
|
options.merge! slug_property.options
|
76
83
|
end
|
77
|
-
property :slug, String, options
|
84
|
+
property :slug, String, options
|
78
85
|
|
79
|
-
|
80
|
-
|
86
|
+
if @slug_options[:unique]
|
87
|
+
scope_options = @slug_options[:scope] && @slug_options[:scope].any? ?
|
88
|
+
{:scope => @slug_options[:scope]} : {}
|
89
|
+
|
90
|
+
validates_uniqueness_of :slug, scope_options
|
91
|
+
end
|
92
|
+
|
93
|
+
before :valid?, :generate_slug
|
81
94
|
end
|
82
95
|
|
83
96
|
module ClassMethods
|
@@ -139,8 +152,15 @@ module DataMapper
|
|
139
152
|
# The slug is not stale if
|
140
153
|
# 1. the slug is permanent, and slug column has something valid in it
|
141
154
|
# 2. the slug source value is nil or empty
|
155
|
+
# 3. scope is not changed
|
142
156
|
def stale_slug?
|
143
|
-
!(
|
157
|
+
!(
|
158
|
+
(permanent_slug? && !slug.blank?) ||
|
159
|
+
slug_source_value.blank?
|
160
|
+
) ||
|
161
|
+
!(!new? && (dirty_attributes.keys.map(&:name) &
|
162
|
+
(self.class.slug_options[:scope] || [])).compact.blank?
|
163
|
+
)
|
144
164
|
end
|
145
165
|
|
146
166
|
private
|
@@ -176,15 +196,22 @@ module DataMapper
|
|
176
196
|
end
|
177
197
|
end
|
178
198
|
|
199
|
+
scope_conditions = {}
|
200
|
+
if self.class.slug_options[:scope]
|
201
|
+
self.class.slug_options[:scope].each do |subject|
|
202
|
+
scope_conditions[subject] = self.__send__(subject)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
179
206
|
max_index = slugs.map do |s|
|
180
|
-
self.class.all(not_self_conditions.merge :slug.like => "#{s}-%")
|
207
|
+
self.class.all(not_self_conditions.merge(scope_conditions).merge :slug.like => "#{s}-%")
|
181
208
|
end.flatten.map do |r|
|
182
209
|
index = r.slug.gsub /^(#{slugs.join '|'})-/, ''
|
183
210
|
index =~ /\d+/ ? index.to_i : nil
|
184
211
|
end.compact.max
|
185
212
|
|
186
213
|
new_index = if max_index.nil?
|
187
|
-
self.class.first(not_self_conditions.merge :slug => base_slug).present? ? 2 : 1
|
214
|
+
self.class.first(not_self_conditions.merge(scope_conditions).merge :slug => base_slug).present? ? 2 : 1
|
188
215
|
else
|
189
216
|
max_index + 1
|
190
217
|
end
|
@@ -41,6 +41,16 @@ describe DataMapper::Is::Slug do
|
|
41
41
|
belongs_to :user
|
42
42
|
end
|
43
43
|
|
44
|
+
class ::Task
|
45
|
+
include DataMapper::Resource
|
46
|
+
property :id, Serial
|
47
|
+
property :title, String
|
48
|
+
property :description, String
|
49
|
+
property :category, String
|
50
|
+
|
51
|
+
is :slug, :source => :title, :scope => :category
|
52
|
+
end
|
53
|
+
|
44
54
|
class ::SlugKey
|
45
55
|
include DataMapper::Resource
|
46
56
|
property :title, String
|
@@ -63,6 +73,9 @@ describe DataMapper::Is::Slug do
|
|
63
73
|
|
64
74
|
before :all do
|
65
75
|
DataMapper.repository do
|
76
|
+
puts ">>> #{DataMapper.repository.adapter.inspect}"
|
77
|
+
DataMapper.logger.set_log STDOUT, :debug, ' ~ ', true
|
78
|
+
DataMapper.logger << 'boo!'
|
66
79
|
@u1 = User.create(:email => "john@ekohe.com")
|
67
80
|
@p1 = Post.create(:user => @u1, :title => "My first shinny blog post")
|
68
81
|
@p2 = Post.create(:user => @u1, :title => "My second shinny blog post")
|
@@ -96,10 +109,10 @@ describe DataMapper::Is::Slug do
|
|
96
109
|
property :id, Serial
|
97
110
|
|
98
111
|
is :slug, {}
|
99
|
-
end
|
112
|
+
end
|
100
113
|
}.should raise_error(DataMapper::Is::Slug::InvalidSlugSourceError)
|
101
114
|
end
|
102
|
-
|
115
|
+
|
103
116
|
it "should display obsolete warning if :size option is used" do
|
104
117
|
module M
|
105
118
|
class Thingy
|
@@ -222,11 +235,11 @@ describe DataMapper::Is::Slug do
|
|
222
235
|
it 'should unidecode latin characters from the slug' do
|
223
236
|
@p6.slug.should == 'a-fancy-cafe'
|
224
237
|
end
|
225
|
-
|
238
|
+
|
226
239
|
it 'should unidecode chinese characters from the slug' do
|
227
240
|
@p7.slug.should == 'ni-hao'
|
228
241
|
end
|
229
|
-
|
242
|
+
|
230
243
|
it 'should have slug_property on instance' do
|
231
244
|
@p1.slug_property.should == @p1.class.properties.detect{|p| p.name == :slug}
|
232
245
|
end
|
@@ -271,5 +284,51 @@ describe DataMapper::Is::Slug do
|
|
271
284
|
post.slug.should == 'the-other-post'
|
272
285
|
end
|
273
286
|
end
|
287
|
+
|
288
|
+
describe 'scoping' do
|
289
|
+
before :each do
|
290
|
+
Task.all.destroy!
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should allow duplicate slugs within different scopes' do
|
294
|
+
t1 = Task.create :category => 'programming', :title => 'fix'
|
295
|
+
t1.slug.should == 'fix'
|
296
|
+
|
297
|
+
t2 = Task.create :category => 'plumbing', :title => 'fix'
|
298
|
+
t2.slug.should == 'fix'
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'should not allow dupliate slugs within same scope' do
|
302
|
+
t1 = Task.create :category => 'programming', :title => 'fix'
|
303
|
+
t1.slug.should == 'fix'
|
304
|
+
|
305
|
+
t2 = Task.create :category => 'programming', :title => 'fix'
|
306
|
+
t2.slug.should == 'fix-2'
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should recalculate slug upon change of scope' do
|
310
|
+
t1 = Task.create :category => 'programming', :title => 'fix'
|
311
|
+
t1.slug.should == 'fix'
|
312
|
+
|
313
|
+
t2 = Task.create :category => 'plumbing', :title => 'fix'
|
314
|
+
t2.slug.should == 'fix'
|
315
|
+
|
316
|
+
t2.category = 'programming'
|
317
|
+
t2.save
|
318
|
+
t2.slug.should == 'fix-2'
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'should keep slug if scope did not change' do
|
322
|
+
t1 = Task.create :category => 'programming', :title => 'fix'
|
323
|
+
t1.slug.should == 'fix'
|
324
|
+
|
325
|
+
t2 = Task.create :category => 'plumbing', :title => 'fix'
|
326
|
+
t2.slug.should == 'fix'
|
327
|
+
|
328
|
+
t2.description = 'copper pipes are good'
|
329
|
+
t2.save
|
330
|
+
t2.slug.should == 'fix'
|
331
|
+
end
|
332
|
+
end
|
274
333
|
end
|
275
334
|
end
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-is-slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 21
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 1
|
8
|
-
- 0
|
9
7
|
- 1
|
10
|
-
|
8
|
+
- 0
|
9
|
+
version: 1.1.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Aaron Qian
|
@@ -20,7 +19,7 @@ autorequire:
|
|
20
19
|
bindir: bin
|
21
20
|
cert_chain: []
|
22
21
|
|
23
|
-
date: 2010-10-
|
22
|
+
date: 2010-10-28 00:00:00 -07:00
|
24
23
|
default_executable:
|
25
24
|
dependencies:
|
26
25
|
- !ruby/object:Gem::Dependency
|
@@ -31,7 +30,6 @@ dependencies:
|
|
31
30
|
requirements:
|
32
31
|
- - ~>
|
33
32
|
- !ruby/object:Gem::Version
|
34
|
-
hash: 19
|
35
33
|
segments:
|
36
34
|
- 1
|
37
35
|
- 0
|
@@ -40,36 +38,49 @@ dependencies:
|
|
40
38
|
type: :runtime
|
41
39
|
version_requirements: *id001
|
42
40
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
41
|
+
name: dm-validations
|
44
42
|
prerelease: false
|
45
43
|
requirement: &id002 !ruby/object:Gem::Requirement
|
46
44
|
none: false
|
47
45
|
requirements:
|
48
46
|
- - ~>
|
49
47
|
- !ruby/object:Gem::Version
|
50
|
-
|
48
|
+
segments:
|
49
|
+
- 1
|
50
|
+
- 0
|
51
|
+
- 2
|
52
|
+
version: 1.0.2
|
53
|
+
type: :runtime
|
54
|
+
version_requirements: *id002
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: unidecode
|
57
|
+
prerelease: false
|
58
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
51
63
|
segments:
|
52
64
|
- 1
|
53
65
|
- 0
|
54
66
|
- 0
|
55
67
|
version: 1.0.0
|
56
68
|
type: :runtime
|
57
|
-
version_requirements: *
|
69
|
+
version_requirements: *id003
|
58
70
|
- !ruby/object:Gem::Dependency
|
59
71
|
name: rspec
|
60
72
|
prerelease: false
|
61
|
-
requirement: &
|
73
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
62
74
|
none: false
|
63
75
|
requirements:
|
64
76
|
- - ~>
|
65
77
|
- !ruby/object:Gem::Version
|
66
|
-
hash: 9
|
67
78
|
segments:
|
68
79
|
- 1
|
69
80
|
- 3
|
70
81
|
version: "1.3"
|
71
82
|
type: :development
|
72
|
-
version_requirements: *
|
83
|
+
version_requirements: *id004
|
73
84
|
description: DataMapper plugin that generates unique slugs
|
74
85
|
email:
|
75
86
|
- aq1018@gmail.com
|
@@ -121,7 +132,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
121
132
|
requirements:
|
122
133
|
- - ">="
|
123
134
|
- !ruby/object:Gem::Version
|
124
|
-
hash: 3
|
125
135
|
segments:
|
126
136
|
- 0
|
127
137
|
version: "0"
|
@@ -130,7 +140,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
140
|
requirements:
|
131
141
|
- - ">="
|
132
142
|
- !ruby/object:Gem::Version
|
133
|
-
hash: 3
|
134
143
|
segments:
|
135
144
|
- 0
|
136
145
|
version: "0"
|