dm-is-slug 1.0.1 → 1.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/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"
|