cheba-dm-is-slug 0.9.13
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/AUTHORS +6 -0
- data/History.txt +1 -0
- data/LICENSE +20 -0
- data/Manifest.txt +12 -0
- data/README.txt +56 -0
- data/Rakefile +54 -0
- data/TODO +4 -0
- data/lib/dm-is-slug.rb +36 -0
- data/lib/dm-is-slug/is/slug.rb +191 -0
- data/lib/dm-is-slug/is/version.rb +7 -0
- data/spec/integration/slug_spec.rb +249 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +32 -0
- metadata +98 -0
data/AUTHORS
ADDED
data/History.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Aaron Qian
|
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/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
= dm-is-slug
|
2
|
+
|
3
|
+
DataMapper plugin for creating and slugs(permalinks).
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
NOTE: You no longer need to download dm-more source code in order to install
|
8
|
+
this.
|
9
|
+
|
10
|
+
All you need to do is:
|
11
|
+
|
12
|
+
$ sudo rake install
|
13
|
+
|
14
|
+
Remember to require it in your app's init.rb
|
15
|
+
|
16
|
+
dependency 'dm-is-slug'
|
17
|
+
|
18
|
+
== Getting started
|
19
|
+
|
20
|
+
Lets say we have a post-class, and we want to generate permalinks or slugs for all posts.
|
21
|
+
|
22
|
+
class Post
|
23
|
+
include DataMapper::Resource
|
24
|
+
|
25
|
+
property :id, Serial
|
26
|
+
property :title, String
|
27
|
+
property :content, String
|
28
|
+
|
29
|
+
# here we define that it should have a slug that uses title as the permalink
|
30
|
+
# it will generate an extra slug property of String type, with the same size as title
|
31
|
+
is :slug, :source => :title
|
32
|
+
end
|
33
|
+
|
34
|
+
Let's Say we need to define a permalink based on a method instead of a property.
|
35
|
+
|
36
|
+
class User
|
37
|
+
include DataMapper::Resource
|
38
|
+
|
39
|
+
property :id, Serial
|
40
|
+
property :email, String
|
41
|
+
property :password, String
|
42
|
+
|
43
|
+
# we only want to strip out the domain name
|
44
|
+
# and use only the email account name as the permalink
|
45
|
+
def slug_for_email
|
46
|
+
email.split("@").first
|
47
|
+
end
|
48
|
+
|
49
|
+
# here we define that it should have a slug that uses title as the permalink
|
50
|
+
# it will generate an extra slug property of String type, with the same size as title
|
51
|
+
is :slug, :source => :slug_for_email, :size => 255
|
52
|
+
end
|
53
|
+
|
54
|
+
You can now find objects by slug like this:
|
55
|
+
|
56
|
+
post = Post.first(:slug => "your_slug")
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
require 'pathname'
|
7
|
+
|
8
|
+
ROOT = Pathname(__FILE__).dirname.expand_path
|
9
|
+
require ROOT + 'lib/dm-is-slug/is/version'
|
10
|
+
|
11
|
+
AUTHOR = ['Aaron Qian', 'James Herdman', 'Nik Radford', 'Paul', 'Mike Frawley', 'Alex Makuta']
|
12
|
+
EMAIL = ['aaron [a] ekohe [d] com', 'james.herdman@gmail.com', 'nik [a] terminaldischarge [d] net', 'maverick.stoklosa@gmail.com', 'frawl021@gmail.com', 'cheba@pointlessone.org']
|
13
|
+
GEM_NAME = "dm-is-slug"
|
14
|
+
GEM_VERSION = DataMapper::Is::Slug::VERSION
|
15
|
+
GEM_DEPENDENCIES = [["dm-core", "~>0.9"]]
|
16
|
+
GEM_CLEAN = ["log", "pkg"]
|
17
|
+
GEM_EXTRAS = { :has_rdoc => false }
|
18
|
+
|
19
|
+
PROJECT_NAME = "dm-is-slug"
|
20
|
+
PROJECT_URL = "http://github.com/aq1018/dm-is-slug"
|
21
|
+
PROJECT_DESCRIPTION = PROJECT_SUMMARY = "DataMapper plugin that generates unique slugs"
|
22
|
+
|
23
|
+
require 'tasks/hoe'
|
24
|
+
|
25
|
+
task :default => [ :spec ]
|
26
|
+
|
27
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
28
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
29
|
+
|
30
|
+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
|
31
|
+
task :install => [ :package ] do
|
32
|
+
sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
|
36
|
+
task :uninstall => [ :clobber ] do
|
37
|
+
sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
|
38
|
+
end
|
39
|
+
|
40
|
+
desc 'Run specifications'
|
41
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
42
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
43
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
44
|
+
|
45
|
+
begin
|
46
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
47
|
+
t.rcov_opts << '--exclude' << 'spec'
|
48
|
+
t.rcov_opts << '--text-summary'
|
49
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
50
|
+
rescue Exception
|
51
|
+
puts 'rcov is not installed. Please install before continuing'
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
end
|
data/lib/dm-is-slug.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Needed to import datamapper and other gems
|
2
|
+
require 'rubygems'
|
3
|
+
require 'pathname'
|
4
|
+
require 'iconv'
|
5
|
+
|
6
|
+
# Add all external dependencies for the plugin here
|
7
|
+
gem 'dm-core', '~>0.9.9'
|
8
|
+
require 'dm-core'
|
9
|
+
|
10
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-slug' / 'is' / 'version.rb'
|
11
|
+
|
12
|
+
# Require plugin-files
|
13
|
+
require Pathname(__FILE__).dirname.expand_path / 'dm-is-slug' / 'is' / 'slug.rb'
|
14
|
+
|
15
|
+
# Include the plugin in Resource
|
16
|
+
module DataMapper
|
17
|
+
module Resource
|
18
|
+
module ClassMethods
|
19
|
+
include DataMapper::Is::Slug
|
20
|
+
end # module ClassMethods
|
21
|
+
end # module Resource
|
22
|
+
end # module DataMapper
|
23
|
+
|
24
|
+
# Include DataMapper::Model#get and DataMapper::Collection#get override
|
25
|
+
# So we do user.posts.get("my-shinny-new-post")
|
26
|
+
|
27
|
+
module DataMapper
|
28
|
+
module Model
|
29
|
+
include DataMapper::Is::Slug::AliasMethods
|
30
|
+
end
|
31
|
+
|
32
|
+
class Collection
|
33
|
+
include DataMapper::Is::Slug::AliasMethods
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Is
|
3
|
+
module Slug
|
4
|
+
class InvalidSlugSource < Exception
|
5
|
+
end
|
6
|
+
|
7
|
+
DEFAULT_SLUG_SIZE = 50
|
8
|
+
|
9
|
+
DEFAULT_SLUG_OPTIONS = {
|
10
|
+
:permanent_slug => true
|
11
|
+
}
|
12
|
+
|
13
|
+
# @param [String] str A string to escape for use as a slug
|
14
|
+
# @return [String] an URL-safe string
|
15
|
+
def self.escape(str)
|
16
|
+
s = Iconv.conv('ascii//translit//IGNORE', 'utf-8', str)
|
17
|
+
s.gsub!(/\W+/, ' ')
|
18
|
+
s.strip!
|
19
|
+
s.downcase!
|
20
|
+
s.gsub!(/\ +/, '-')
|
21
|
+
s
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Methods that should be included in DataMapper::Model.
|
26
|
+
# Normally this should just be your generator, so that the namespace
|
27
|
+
# does not get cluttered. ClassMethods and InstanceMethods gets added
|
28
|
+
# in the specific resources when you fire is :slug
|
29
|
+
##
|
30
|
+
|
31
|
+
# Defines a +slug+ property on your model with the same size as your
|
32
|
+
# source property. This property is Unicode escaped, and treated so as
|
33
|
+
# to be fit for use in URLs.
|
34
|
+
#
|
35
|
+
# ==== Example
|
36
|
+
# Suppose your source attribute was the following string: "Hot deals on
|
37
|
+
# Boxing Day". This string would be escaped to "hot-deals-on-boxing-day".
|
38
|
+
#
|
39
|
+
# Non-ASCII characters are attempted to be converted to their nearest
|
40
|
+
# approximate.
|
41
|
+
#
|
42
|
+
# ==== Parameters
|
43
|
+
# +permanent_slug+::
|
44
|
+
# Permanent slugs are not changed even if the source property has
|
45
|
+
# +source+::
|
46
|
+
# The property on the model to use as the source of the generated slug,
|
47
|
+
# or an instance method defined in the model, the method must return
|
48
|
+
# a string or nil.
|
49
|
+
# +size+::
|
50
|
+
# The length of the +slug+ property
|
51
|
+
#
|
52
|
+
# @param [Hash] provide options in a Hash. See *Parameters* for details
|
53
|
+
def is_slug(options)
|
54
|
+
extend DataMapper::Is::Slug::ClassMethods
|
55
|
+
include DataMapper::Is::Slug::InstanceMethods
|
56
|
+
|
57
|
+
@slug_options = DEFAULT_SLUG_OPTIONS.merge(options)
|
58
|
+
raise InvalidSlugSource('You must specify a :source to generate slug.') unless slug_source
|
59
|
+
|
60
|
+
slug_options[:size] ||= get_slug_size
|
61
|
+
property(:slug, String, :size => slug_options[:size], :unique => true) unless slug_property
|
62
|
+
if method_defined?(:valid?)
|
63
|
+
before :valid?, :generate_slug
|
64
|
+
else
|
65
|
+
before :slug, :generate_slug
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ClassMethods
|
70
|
+
attr_reader :slug_options
|
71
|
+
|
72
|
+
def permanent_slug?
|
73
|
+
slug_options[:permanent_slug]
|
74
|
+
end
|
75
|
+
|
76
|
+
def slug_source
|
77
|
+
slug_options[:source] ? slug_options[:source].to_sym : nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def slug_source_property
|
81
|
+
detect_slug_property_by_name(slug_source)
|
82
|
+
end
|
83
|
+
|
84
|
+
def slug_property
|
85
|
+
detect_slug_property_by_name(:slug)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def detect_slug_property_by_name(name)
|
91
|
+
properties.detect do |p|
|
92
|
+
p.name == name && p.type == String
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def get_slug_size
|
97
|
+
slug_source_property && slug_source_property.size || DataMapper::Is::Slug::DEFAULT_SLUG_SIZE
|
98
|
+
end
|
99
|
+
end # ClassMethods
|
100
|
+
|
101
|
+
module InstanceMethods
|
102
|
+
def to_param
|
103
|
+
[slug]
|
104
|
+
end
|
105
|
+
|
106
|
+
def permanent_slug?
|
107
|
+
self.class.permanent_slug?
|
108
|
+
end
|
109
|
+
|
110
|
+
def slug_source
|
111
|
+
self.class.slug_source
|
112
|
+
end
|
113
|
+
|
114
|
+
def slug_source_property
|
115
|
+
self.class.slug_source_property
|
116
|
+
end
|
117
|
+
|
118
|
+
def slug_property
|
119
|
+
self.class.slug_property
|
120
|
+
end
|
121
|
+
|
122
|
+
def slug_source_value
|
123
|
+
self.send(slug_source)
|
124
|
+
end
|
125
|
+
|
126
|
+
# The slug is not stale if
|
127
|
+
# 1. the slug is permanent, and slug column has something valid in it
|
128
|
+
# 2. the slug source value is nil or empty
|
129
|
+
def stale_slug?
|
130
|
+
!((permanent_slug? && slug && !slug.empty?) || (slug_source_value.nil? || slug_source_value.empty?))
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def generate_slug
|
136
|
+
return unless self.class.respond_to?(:slug_options) && self.class.slug_options
|
137
|
+
raise InvalidSlugSource('Invalid slug source.') unless slug_source_property || self.respond_to?(slug_source)
|
138
|
+
return unless stale_slug?
|
139
|
+
attribute_set :slug, unique_slug
|
140
|
+
end
|
141
|
+
|
142
|
+
def unique_slug
|
143
|
+
old_slug = self.slug
|
144
|
+
max_length = self.class.send(:get_slug_size)
|
145
|
+
base_slug = ::DataMapper::Is::Slug.escape(slug_source_value)[0, max_length]
|
146
|
+
i = 1
|
147
|
+
new_slug = base_slug
|
148
|
+
|
149
|
+
if old_slug != new_slug
|
150
|
+
lambda do
|
151
|
+
dupe = self.class.first(:slug => new_slug)
|
152
|
+
if dupe && dupe != self
|
153
|
+
i = i + 1
|
154
|
+
slug_length = max_length - i.to_s.length - 1
|
155
|
+
new_slug = "#{base_slug[0, slug_length]}-#{i}"
|
156
|
+
redo
|
157
|
+
end
|
158
|
+
end.call
|
159
|
+
new_slug
|
160
|
+
else
|
161
|
+
old_slug
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end # InstanceMethods
|
165
|
+
|
166
|
+
module AliasMethods
|
167
|
+
# override the old get method so that it looks for slugs first
|
168
|
+
# and call the old get if slug is not found
|
169
|
+
def get_with_slug(*key)
|
170
|
+
result = nil
|
171
|
+
if respond_to?(:slug_options) && slug_options
|
172
|
+
result = first(:slug => key[0])
|
173
|
+
end
|
174
|
+
|
175
|
+
if result.nil?
|
176
|
+
get_without_slug(*key)
|
177
|
+
else
|
178
|
+
result
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# fired when your plugin gets included into Resource
|
184
|
+
def self.included(base)
|
185
|
+
base.send :alias_method, :get_without_slug, :get
|
186
|
+
base.send :alias_method, :get, :get_with_slug
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end # Slug
|
190
|
+
end # Is
|
191
|
+
end # DataMapper
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
3
|
+
|
4
|
+
if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
5
|
+
describe 'DataMapper::Is::Slug' do
|
6
|
+
|
7
|
+
class User
|
8
|
+
include DataMapper::Resource
|
9
|
+
|
10
|
+
property :id, Serial
|
11
|
+
property :email, String
|
12
|
+
has n, :posts
|
13
|
+
has n, :todos
|
14
|
+
|
15
|
+
def slug_for_email
|
16
|
+
email.split("@").first
|
17
|
+
end
|
18
|
+
|
19
|
+
is :slug, :source => :slug_for_email, :size => 80, :permanent_slug => false
|
20
|
+
end
|
21
|
+
|
22
|
+
class Post
|
23
|
+
include DataMapper::Resource
|
24
|
+
|
25
|
+
property :id, Serial
|
26
|
+
property :title, String, :size => 30
|
27
|
+
property :content, Text
|
28
|
+
|
29
|
+
belongs_to :user
|
30
|
+
|
31
|
+
is :slug, :source => :title
|
32
|
+
end
|
33
|
+
|
34
|
+
class Todo
|
35
|
+
include DataMapper::Resource
|
36
|
+
property :id, Serial
|
37
|
+
property :title, String
|
38
|
+
|
39
|
+
belongs_to :user
|
40
|
+
end
|
41
|
+
|
42
|
+
class SlugKey
|
43
|
+
include DataMapper::Resource
|
44
|
+
property :title, String
|
45
|
+
property :slug, String, :key => true
|
46
|
+
|
47
|
+
is :slug, :source => :title
|
48
|
+
end
|
49
|
+
|
50
|
+
before :all do
|
51
|
+
User.auto_migrate!(:default)
|
52
|
+
Post.auto_migrate!(:default)
|
53
|
+
Todo.auto_migrate!(:default)
|
54
|
+
SlugKey.auto_migrate!(:default)
|
55
|
+
|
56
|
+
@u1 = User.create(:email => "john@ekohe.com")
|
57
|
+
@p1 = Post.create(:user => @u1, :title => "My first shinny blog post")
|
58
|
+
@p2 = Post.create(:user => @u1, :title => "My second shinny blog post")
|
59
|
+
@p3 = Post.create(:user => @u1, :title => "My third shinny blog post")
|
60
|
+
|
61
|
+
@u2 = User.create(:email => "john@someotherplace.com")
|
62
|
+
@p4 = Post.create(:user => @u2, :title => "My first Shinny blog post")
|
63
|
+
@p5 = Post.create(:user => @u2, :title => "i heart merb and dm")
|
64
|
+
@p6 = Post.create(:user => @u2, :title => "another productive day!!")
|
65
|
+
@p7 = Post.create(:user => @u2, :title => "another productive day!!")
|
66
|
+
@p8 = Post.create(:user => @u2, :title => "another productive day!!")
|
67
|
+
@p9 = Post.create(:user => @u2, :title => "another productive day!!")
|
68
|
+
@p10 = Post.create(:user => @u2, :title => "another productive day!!")
|
69
|
+
@p11 = Post.create(:user => @u2, :title => "another productive day!!")
|
70
|
+
@p12 = Post.create(:user => @u2, :title => "another productive day!!")
|
71
|
+
@p13 = Post.create(:user => @u2, :title => "another productive day!!")
|
72
|
+
@p14 = Post.create(:user => @u2, :title => "another productive day!!")
|
73
|
+
@p15 = Post.create(:user => @u2, :title => "another productive day!!")
|
74
|
+
@p16 = Post.create(:user => @u2, :title => "another productive day!!")
|
75
|
+
@p17 = Post.create(:user => @u2, :title => "A fancy café")
|
76
|
+
(1..20).each do |i|
|
77
|
+
instance_variable_set "@p_#{i}".to_sym, Post.create(:user => @u2, :title => "DM tricks")
|
78
|
+
end
|
79
|
+
|
80
|
+
@sk = SlugKey.create(:title => 'slug key')
|
81
|
+
|
82
|
+
@post1 = Post.create :title => 'a' * Post.slug_options[:size]
|
83
|
+
@post2 = Post.create :title => 'a' * Post.slug_options[:size]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should generate slugs" do
|
87
|
+
User.all.each do |u|
|
88
|
+
u.slug.should_not be_nil
|
89
|
+
end
|
90
|
+
|
91
|
+
Post.all.each do |p|
|
92
|
+
p.slug.should_not be_nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should generate unique slugs" do
|
97
|
+
@u1.slug.should_not == @u2.slug
|
98
|
+
@p1.slug.should_not == @p4.slug
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should generate correct slug for user" do
|
102
|
+
@u1.slug.should == "john"
|
103
|
+
@u2.slug.should == "john-2"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should generate correct slug for post" do
|
107
|
+
@p1.slug.should == "my-first-shinny-blog-post"
|
108
|
+
@p2.slug.should == "my-second-shinny-blog-post"
|
109
|
+
@p3.slug.should == "my-third-shinny-blog-post"
|
110
|
+
@p4.slug.should == "my-first-shinny-blog-post-2"
|
111
|
+
@p5.slug.should == "i-heart-merb-and-dm"
|
112
|
+
@p6.slug.should == "another-productive-day"
|
113
|
+
@p7.slug.should == "another-productive-day-2"
|
114
|
+
@p8.slug.should == "another-productive-day-3"
|
115
|
+
@p9.slug.should == "another-productive-day-4"
|
116
|
+
@p10.slug.should == "another-productive-day-5"
|
117
|
+
@p11.slug.should == "another-productive-day-6"
|
118
|
+
@p12.slug.should == "another-productive-day-7"
|
119
|
+
@p13.slug.should == "another-productive-day-8"
|
120
|
+
@p14.slug.should == "another-productive-day-9"
|
121
|
+
@p15.slug.should == "another-productive-day-10"
|
122
|
+
@p16.slug.should == "another-productive-day-11"
|
123
|
+
@p_1.slug.should == 'dm-tricks'
|
124
|
+
(2..20).each do |i|
|
125
|
+
instance_variable_get("@p_#{i}".to_sym).slug.should == "dm-tricks-#{i}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should update slug if :permanent_slug => :false is specified" do
|
130
|
+
user = User.create(:email => "a_person@ekohe.com")
|
131
|
+
user.slug.should == "a_person"
|
132
|
+
|
133
|
+
user.should_not be_permanent_slug
|
134
|
+
|
135
|
+
user.email = "changed@ekohe.com"
|
136
|
+
user.should be_dirty
|
137
|
+
|
138
|
+
user.save.should be_true
|
139
|
+
user.slug.should == "changed"
|
140
|
+
user.destroy
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should not update slug if :permanent_slug => :true or not specified" do
|
144
|
+
post = Post.create(:user => @u1, :title => "hello world!")
|
145
|
+
post.slug.should == "hello-world"
|
146
|
+
post.should be_permanent_slug
|
147
|
+
post.title = "hello universe!"
|
148
|
+
post.should be_dirty
|
149
|
+
post.save.should be_true
|
150
|
+
post.slug.should == "hello-world"
|
151
|
+
post.destroy
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should have the right size for properties" do
|
155
|
+
user_slug_property = User.properties.detect{|p| p.name == :slug && p.type == String}
|
156
|
+
user_slug_property.should_not be_nil
|
157
|
+
user_slug_property.size.should == 80
|
158
|
+
|
159
|
+
Post.properties.detect{|p| p.name == :title && p.type == String}.size.should == 30
|
160
|
+
post_slug_property = Post.properties.detect{|p| p.name == :slug && p.type == String}
|
161
|
+
post_slug_property.should_not be_nil
|
162
|
+
post_slug_property.size.should == 30
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should find model using get method with slug" do
|
166
|
+
u = User.get("john")
|
167
|
+
u.should_not be_nil
|
168
|
+
u.should == @u1
|
169
|
+
|
170
|
+
Post.get("my-first-shinny-blog-post").should == @p1
|
171
|
+
@u1.posts.get("my-first-shinny-blog-post").should == @p1
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should output slug with to_param method" do
|
175
|
+
@u1.to_param.should == ["john"]
|
176
|
+
@p1.to_param.should == ["my-first-shinny-blog-post"]
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should find model using get method using id" do
|
180
|
+
u = User.get(@u1.id)
|
181
|
+
u.should_not be_nil
|
182
|
+
u.should == @u1
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should find model using get method using id with non-slug models" do
|
186
|
+
todo = Todo.create(:user => @u1, :title => "blabla")
|
187
|
+
todo.should_not be_nil
|
188
|
+
|
189
|
+
Todo.get(todo.id).should == todo
|
190
|
+
@u1.todos.get(todo.id).should == todo
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should strip unicode characters from the slug' do
|
194
|
+
@p17.slug.should == 'a-fancy-caf'
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should have slug_property on instance' do
|
198
|
+
@p1.slug_property.should == @p1.class.properties.detect{|p| p.name == :slug}
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should properly increment slug suffix' do
|
202
|
+
@p_20.slug.should == 'dm-tricks-20'
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should work with key on slug and validations' do
|
206
|
+
@sk.slug.should == 'slug-key'
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should have slug no longer than slug_options[:size]' do
|
210
|
+
@post1.slug.length.should == @post1.class.slug_options[:size]
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should have suffixed slug no longer than slug_options[:size]' do
|
214
|
+
@post2.slug.length.should == @post2.class.slug_options[:size]
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'should generate right slug for long sources' do
|
218
|
+
@post2.slug.should == ('a' * (@post2.class.slug_options[:size] - 2) + '-2')
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'editing' do
|
222
|
+
class Post2
|
223
|
+
include DataMapper::Resource
|
224
|
+
property :id, Serial
|
225
|
+
property :title, String, :size => 30
|
226
|
+
property :content, Text
|
227
|
+
|
228
|
+
is :slug, :source => :title, :permanent_slug => false
|
229
|
+
end
|
230
|
+
|
231
|
+
Post2.auto_migrate!
|
232
|
+
|
233
|
+
before :each do
|
234
|
+
Post2.all.destroy!
|
235
|
+
@post = Post2.create :title => 'The Post', :content => 'The content.'
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should not change slug if source is not changed' do
|
239
|
+
@post.update_attributes :content => 'The other content.'
|
240
|
+
Post2.first.slug.should == 'the-post'
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should change slug if source is changed' do
|
244
|
+
@post.update_attributes :title => 'The Other Post'
|
245
|
+
Post2.first.slug.should == 'the-other-post'
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'rspec', '~>1.2.6'
|
3
|
+
require 'spec'
|
4
|
+
require 'pathname'
|
5
|
+
require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-is-slug'
|
6
|
+
|
7
|
+
def load_driver(name, default_uri)
|
8
|
+
return false if ENV['ADAPTER'] != name.to_s
|
9
|
+
|
10
|
+
lib = "do_#{name}"
|
11
|
+
|
12
|
+
begin
|
13
|
+
gem lib, '~>0.9.7'
|
14
|
+
require lib
|
15
|
+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
16
|
+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
17
|
+
true
|
18
|
+
rescue Gem::LoadError => e
|
19
|
+
warn "Could not load #{lib}: #{e}"
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ENV['ADAPTER'] ||= 'sqlite3'
|
25
|
+
|
26
|
+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
27
|
+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
|
28
|
+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
|
29
|
+
|
30
|
+
gem 'dm-validations'
|
31
|
+
require 'dm-validations'
|
32
|
+
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cheba-dm-is-slug
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.13
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aaron Qian
|
8
|
+
- James Herdman
|
9
|
+
- Nik Radford
|
10
|
+
- Paul
|
11
|
+
- Mike Frawley
|
12
|
+
- Alex Makuta
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2009-06-02 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: dm-core
|
22
|
+
type: :runtime
|
23
|
+
version_requirement:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: "0.9"
|
29
|
+
version:
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hoe
|
32
|
+
type: :development
|
33
|
+
version_requirement:
|
34
|
+
version_requirements: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 1.8.2
|
39
|
+
version:
|
40
|
+
description: DataMapper plugin that generates unique permalinks / slugs
|
41
|
+
email:
|
42
|
+
- aaron [a] ekohe [d] com
|
43
|
+
- james.herdman@gmail.com
|
44
|
+
- nik [a] terminaldischarge [d] net
|
45
|
+
- maverick.stoklosa@gmail.com
|
46
|
+
- frawl021@gmail.com
|
47
|
+
- cheba@pointlessone.org
|
48
|
+
executables: []
|
49
|
+
|
50
|
+
extensions: []
|
51
|
+
|
52
|
+
extra_rdoc_files:
|
53
|
+
- History.txt
|
54
|
+
- Manifest.txt
|
55
|
+
- README.txt
|
56
|
+
files:
|
57
|
+
- AUTHORS
|
58
|
+
- History.txt
|
59
|
+
- LICENSE
|
60
|
+
- Manifest.txt
|
61
|
+
- README.txt
|
62
|
+
- Rakefile
|
63
|
+
- TODO
|
64
|
+
- lib/dm-is-slug.rb
|
65
|
+
- lib/dm-is-slug/is/slug.rb
|
66
|
+
- lib/dm-is-slug/is/version.rb
|
67
|
+
- spec/integration/slug_spec.rb
|
68
|
+
- spec/spec.opts
|
69
|
+
- spec/spec_helper.rb
|
70
|
+
has_rdoc: false
|
71
|
+
homepage: http://github.com/aq1018/dm-is-slug
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options:
|
74
|
+
- --main
|
75
|
+
- README.txt
|
76
|
+
require_paths:
|
77
|
+
- lib
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project: dm-is-slug
|
93
|
+
rubygems_version: 1.2.0
|
94
|
+
signing_key:
|
95
|
+
specification_version: 2
|
96
|
+
summary: DataMapper plugin that generates unique permalinks / slugs
|
97
|
+
test_files: []
|
98
|
+
|