dm-is-slug 0.10.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +142 -0
- data/README.rdoc +58 -0
- data/Rakefile +10 -8
- data/VERSION +1 -1
- data/dm-is-slug.gemspec +13 -11
- data/lib/dm-is-slug.rb +1 -7
- data/lib/dm-is-slug/is/slug.rb +49 -30
- data/spec/integration/slug_spec.rb +60 -54
- data/spec/spec_helper.rb +9 -34
- data/tasks/local_gemfile.rake +16 -0
- metadata +53 -22
- data/README.textile +0 -56
data/.gitignore
CHANGED
data/Gemfile
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# If you're working on more than one datamapper gem at a time, then it's
|
2
|
+
# recommended to create a local Gemfile and use this instead of the git
|
3
|
+
# sources. This will make sure that you are developing against your
|
4
|
+
# other local datamapper sources that you currently work on. Gemfile.local
|
5
|
+
# will behave identically to the standard Gemfile apart from the fact that
|
6
|
+
# it fetches the datamapper gems from local paths. This means that you can use
|
7
|
+
# the same environment variables, like ADAPTER(S) or PLUGIN(S) when running
|
8
|
+
# bundle commands. Gemfile.local is added to .gitignore, so you don't need to
|
9
|
+
# worry about accidentally checking local development paths into git.
|
10
|
+
# In order to create a local Gemfile, all you need to do is run:
|
11
|
+
#
|
12
|
+
# bundle exec rake local_gemfile
|
13
|
+
#
|
14
|
+
# This will give you a Gemfile.local file that points to your local clones of
|
15
|
+
# the various datamapper gems. It's assumed that all datamapper repo clones
|
16
|
+
# reside in the same directory. You can use the Gemfile.local like so for
|
17
|
+
# running any bundle command:
|
18
|
+
#
|
19
|
+
# BUNDLE_GEMFILE=Gemfile.local bundle foo
|
20
|
+
#
|
21
|
+
# You can also specify which adapter(s) should be part of the bundle by setting
|
22
|
+
# an environment variable. This of course also works when using the Gemfile.local
|
23
|
+
#
|
24
|
+
# bundle foo # dm-sqlite-adapter
|
25
|
+
# ADAPTER=mysql bundle foo # dm-mysql-adapter
|
26
|
+
# ADAPTERS=sqlite,mysql bundle foo # dm-sqlite-adapter and dm-mysql-adapter
|
27
|
+
#
|
28
|
+
# Of course you can also use the ADAPTER(S) variable when using the Gemfile.local
|
29
|
+
# and running specs against selected adapters.
|
30
|
+
#
|
31
|
+
# For easily working with adapters supported on your machine, it's recommended
|
32
|
+
# that you first install all adapters that you are planning to use or work on
|
33
|
+
# by doing something like
|
34
|
+
#
|
35
|
+
# ADAPTERS=sqlite,mysql,postgres bundle install
|
36
|
+
#
|
37
|
+
# This will clone the various repositories and make them available to bundler.
|
38
|
+
# Once you have them installed you can easily switch between adapters for the
|
39
|
+
# various development tasks. Running something like
|
40
|
+
#
|
41
|
+
# ADAPTER=mysql bundle exec rake spec
|
42
|
+
#
|
43
|
+
# will make sure that the dm-mysql-adapter is part of the bundle, and will be used
|
44
|
+
# when running the specs.
|
45
|
+
#
|
46
|
+
# You can also specify which plugin(s) should be part of the bundle by setting
|
47
|
+
# an environment variable. This also works when using the Gemfile.local
|
48
|
+
#
|
49
|
+
# bundle foo # dm-migrations
|
50
|
+
# PLUGINS=dm-validations bundle foo # dm-migrations and dm-validations
|
51
|
+
# PLUGINS=dm-validations,dm-types bundle foo # dm-migrations, dm-validations and dm-types
|
52
|
+
#
|
53
|
+
# Of course you can combine the PLUGIN(S) and ADAPTER(S) env vars to run specs
|
54
|
+
# for certain adapter/plugin combinations.
|
55
|
+
#
|
56
|
+
# Finally, to speed up running specs and other tasks, it's recommended to run
|
57
|
+
#
|
58
|
+
# bundle lock
|
59
|
+
#
|
60
|
+
# after running 'bundle install' for the first time. This will make 'bundle exec' run
|
61
|
+
# a lot faster compared to the unlocked version. With an unlocked bundle you would
|
62
|
+
# typically just run 'bundle install' from time to time to fetch the latest sources from
|
63
|
+
# upstream. When you locked your bundle, you need to run
|
64
|
+
#
|
65
|
+
# bundle install --relock
|
66
|
+
#
|
67
|
+
# to make sure to fetch the latest updates and then lock the bundle again. Gemfile.lock
|
68
|
+
# is added to the .gitignore file, so you don't need to worry about accidentally checking
|
69
|
+
# it into version control.
|
70
|
+
|
71
|
+
source 'http://rubygems.org'
|
72
|
+
|
73
|
+
DATAMAPPER = 'git://github.com/datamapper'
|
74
|
+
DM_VERSION = '~> 1.0.2'
|
75
|
+
|
76
|
+
group :runtime do # Runtime dependencies (as in the gemspec)
|
77
|
+
|
78
|
+
if ENV['EXTLIB']
|
79
|
+
gem 'extlib', '~> 0.9.15', :git => "#{DATAMAPPER}/extlib.git"
|
80
|
+
else
|
81
|
+
gem 'activesupport', '~> 3.0.0', :git => 'git://github.com/rails/rails.git', :branch => '3-0-stable', :require => nil
|
82
|
+
end
|
83
|
+
|
84
|
+
gem 'dm-core', DM_VERSION, :git => "#{DATAMAPPER}/dm-core.git"
|
85
|
+
|
86
|
+
gem 'unidecode', '~> 1.0.0'
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
group(:development) do # Development dependencies (as in the gemspec)
|
91
|
+
|
92
|
+
gem 'rake', '~> 0.8.7'
|
93
|
+
gem 'rspec', '~> 1.3', :git => 'git://github.com/snusnu/rspec', :branch => 'heckle_fix_plus_gemfile'
|
94
|
+
gem 'jeweler', '~> 1.4'
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
group :quality do # These gems contain rake tasks that check the quality of the source code
|
99
|
+
|
100
|
+
gem 'metric_fu', '~> 1.3'
|
101
|
+
gem 'rcov', '~> 0.9.8'
|
102
|
+
gem 'reek', '~> 1.2.8'
|
103
|
+
gem 'roodi', '~> 2.1'
|
104
|
+
gem 'yard', '~> 0.5'
|
105
|
+
gem 'yardstick', '~> 0.1'
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
group :datamapper do # We need this because we want to pin these dependencies to their git master sources
|
110
|
+
|
111
|
+
adapters = ENV['ADAPTER'] || ENV['ADAPTERS']
|
112
|
+
adapters = adapters.to_s.tr(',', ' ').split.uniq - %w[ in_memory ]
|
113
|
+
|
114
|
+
DO_VERSION = '~> 0.10.2'
|
115
|
+
DM_DO_ADAPTERS = %w[ sqlite postgres mysql oracle sqlserver ]
|
116
|
+
|
117
|
+
if (do_adapters = DM_DO_ADAPTERS & adapters).any?
|
118
|
+
options = {}
|
119
|
+
options[:git] = "#{DATAMAPPER}/do.git" if ENV['DO_GIT'] == 'true'
|
120
|
+
|
121
|
+
gem 'data_objects', DO_VERSION, options.dup
|
122
|
+
|
123
|
+
do_adapters.each do |adapter|
|
124
|
+
adapter = 'sqlite3' if adapter == 'sqlite'
|
125
|
+
gem "do_#{adapter}", DO_VERSION, options.dup
|
126
|
+
end
|
127
|
+
|
128
|
+
gem 'dm-do-adapter', DM_VERSION, :git => "#{DATAMAPPER}/dm-do-adapter.git"
|
129
|
+
end
|
130
|
+
|
131
|
+
adapters.each do |adapter|
|
132
|
+
gem "dm-#{adapter}-adapter", DM_VERSION, :git => "#{DATAMAPPER}/dm-#{adapter}-adapter.git"
|
133
|
+
end
|
134
|
+
|
135
|
+
plugins = ENV['PLUGINS'] || ENV['PLUGIN']
|
136
|
+
plugins = plugins.to_s.tr(',', ' ').split.push('dm-migrations').uniq
|
137
|
+
|
138
|
+
plugins.each do |plugin|
|
139
|
+
gem plugin, DM_VERSION, :git => "#{DATAMAPPER}/#{plugin}.git"
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
data/README.rdoc
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= dm-is-slug
|
2
|
+
|
3
|
+
DataMapper plugin for creating and slugs(permalinks).
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
> gem install dm-is-slug
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
=== Creating a slug from property
|
12
|
+
|
13
|
+
class Post
|
14
|
+
include DataMapper::Resource
|
15
|
+
|
16
|
+
property :id, Serial
|
17
|
+
property :title, String
|
18
|
+
property :content, String
|
19
|
+
|
20
|
+
# here we define that it should have a slug that uses title as the permalink
|
21
|
+
# it will generate an extra slug property of String type, with the same size as title
|
22
|
+
is :slug, :source => :title
|
23
|
+
end
|
24
|
+
|
25
|
+
=== Creating a slug from arbitrary methods
|
26
|
+
|
27
|
+
class User
|
28
|
+
include DataMapper::Resource
|
29
|
+
|
30
|
+
property :id, Serial
|
31
|
+
property :email, String
|
32
|
+
property :password, String
|
33
|
+
|
34
|
+
# we only want to strip out the domain name
|
35
|
+
# and use only the email account name as the permalink
|
36
|
+
def slug_for_email
|
37
|
+
email.split("@").first
|
38
|
+
end
|
39
|
+
|
40
|
+
# here we define that it should have a slug that uses title as the permalink
|
41
|
+
# it will generate an extra slug property of String type, with the same size as title
|
42
|
+
is :slug, :source => :slug_for_email, :size => 255
|
43
|
+
end
|
44
|
+
|
45
|
+
=== Finding objects by slug
|
46
|
+
|
47
|
+
post = Post.first(:slug => "your_slug")
|
48
|
+
|
49
|
+
== Development
|
50
|
+
|
51
|
+
It's recommended to use bundler in development.
|
52
|
+
|
53
|
+
gem install bundler
|
54
|
+
ADAPTERS='in_memory yaml sqlite postgres mysql' bundle install --without quality
|
55
|
+
|
56
|
+
This will install all dependencies so that you could run specs.
|
57
|
+
|
58
|
+
bundler exec rake spec ADAPTER=in_memory
|
data/Rakefile
CHANGED
@@ -4,7 +4,7 @@ require 'rake'
|
|
4
4
|
begin
|
5
5
|
gem 'jeweler', '~> 1.4'
|
6
6
|
require 'jeweler'
|
7
|
-
|
7
|
+
|
8
8
|
Jeweler::Tasks.new do |gem|
|
9
9
|
gem.name = "dm-is-slug"
|
10
10
|
gem.summary = "DataMapper plugin that generates unique slugs"
|
@@ -15,18 +15,20 @@ begin
|
|
15
15
|
'nik [a] terminaldischarge [d] net',
|
16
16
|
'maverick.stoklosa@gmail.com',
|
17
17
|
'frawl021@gmail.com',
|
18
|
-
'cheba@pointlessone.org'
|
18
|
+
'cheba+github@pointlessone.org'
|
19
19
|
]
|
20
20
|
gem.homepage = "http://github.com/aq1018/dm-is-slug"
|
21
|
-
gem.authors = ['Aaron Qian', 'James Herdman', 'Nik Radford', 'Paul', 'Mike Frawley', '
|
22
|
-
|
23
|
-
gem.add_dependency "
|
21
|
+
gem.authors = ['Aaron Qian', 'James Herdman', 'Nik Radford', 'Paul', 'Mike Frawley', 'Alexander Mankuta']
|
22
|
+
|
23
|
+
gem.add_dependency "dm-core", "~> 1.0.2"
|
24
|
+
gem.add_dependency "unidecode", "~> 1.0.0"
|
25
|
+
|
24
26
|
gem.add_development_dependency 'rspec', '~> 1.3'
|
25
27
|
end
|
26
|
-
|
28
|
+
|
27
29
|
Jeweler::GemcutterTasks.new
|
28
|
-
|
30
|
+
|
29
31
|
FileList['tasks/**/*.rake'].each { |task| import task }
|
30
32
|
rescue LoadError
|
31
33
|
puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
|
32
|
-
end
|
34
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/dm-is-slug.gemspec
CHANGED
@@ -5,22 +5,23 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dm-is-slug}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Aaron Qian", "James Herdman", "Nik Radford", "Paul", "Mike Frawley", "
|
12
|
-
s.date = %q{2010-
|
11
|
+
s.authors = ["Aaron Qian", "James Herdman", "Nik Radford", "Paul", "Mike Frawley", "Alexander Mankuta"]
|
12
|
+
s.date = %q{2010-10-17}
|
13
13
|
s.description = %q{DataMapper plugin that generates unique slugs}
|
14
|
-
s.email = ["aq1018@gmail.com", "james.herdman@gmail.com", "nik [a] terminaldischarge [d] net", "maverick.stoklosa@gmail.com", "frawl021@gmail.com", "cheba@pointlessone.org"]
|
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 = [
|
16
16
|
"LICENSE",
|
17
|
-
"README.
|
17
|
+
"README.rdoc",
|
18
18
|
"TODO"
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".gitignore",
|
22
|
+
"Gemfile",
|
22
23
|
"LICENSE",
|
23
|
-
"README.
|
24
|
+
"README.rdoc",
|
24
25
|
"Rakefile",
|
25
26
|
"TODO",
|
26
27
|
"VERSION",
|
@@ -32,6 +33,7 @@ Gem::Specification.new do |s|
|
|
32
33
|
"spec/spec.opts",
|
33
34
|
"spec/spec_helper.rb",
|
34
35
|
"tasks/ci.rake",
|
36
|
+
"tasks/local_gemfile.rake",
|
35
37
|
"tasks/metrics.rake",
|
36
38
|
"tasks/spec.rake",
|
37
39
|
"tasks/yard.rake",
|
@@ -40,7 +42,7 @@ Gem::Specification.new do |s|
|
|
40
42
|
s.homepage = %q{http://github.com/aq1018/dm-is-slug}
|
41
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
42
44
|
s.require_paths = ["lib"]
|
43
|
-
s.rubygems_version = %q{1.3.
|
45
|
+
s.rubygems_version = %q{1.3.7}
|
44
46
|
s.summary = %q{DataMapper plugin that generates unique slugs}
|
45
47
|
s.test_files = [
|
46
48
|
"spec/integration/slug_spec.rb",
|
@@ -51,17 +53,17 @@ Gem::Specification.new do |s|
|
|
51
53
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
52
54
|
s.specification_version = 3
|
53
55
|
|
54
|
-
if Gem::Version.new(Gem::
|
55
|
-
s.add_runtime_dependency(%q<dm-core>, ["~> 0.
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_runtime_dependency(%q<dm-core>, ["~> 1.0.2"])
|
56
58
|
s.add_runtime_dependency(%q<unidecode>, ["~> 1.0.0"])
|
57
59
|
s.add_development_dependency(%q<rspec>, ["~> 1.3"])
|
58
60
|
else
|
59
|
-
s.add_dependency(%q<dm-core>, ["~> 0.
|
61
|
+
s.add_dependency(%q<dm-core>, ["~> 1.0.2"])
|
60
62
|
s.add_dependency(%q<unidecode>, ["~> 1.0.0"])
|
61
63
|
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
62
64
|
end
|
63
65
|
else
|
64
|
-
s.add_dependency(%q<dm-core>, ["~> 0.
|
66
|
+
s.add_dependency(%q<dm-core>, ["~> 1.0.2"])
|
65
67
|
s.add_dependency(%q<unidecode>, ["~> 1.0.0"])
|
66
68
|
s.add_dependency(%q<rspec>, ["~> 1.3"])
|
67
69
|
end
|
data/lib/dm-is-slug.rb
CHANGED
data/lib/dm-is-slug/is/slug.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
require 'unidecode'
|
2
|
+
require 'dm-core'
|
2
3
|
require 'dm-core/support/chainable'
|
3
4
|
|
4
5
|
module DataMapper
|
5
6
|
module Is
|
6
7
|
module Slug
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
end
|
11
|
+
|
7
12
|
class InvalidSlugSourceError < StandardError; end
|
8
13
|
|
9
14
|
# @param [String] str A string to escape for use as a slug
|
@@ -66,9 +71,12 @@ module DataMapper
|
|
66
71
|
|
67
72
|
|
68
73
|
options[:length] ||= get_slug_length
|
69
|
-
|
74
|
+
if slug_property && slug_property.class >= DataMapper::Property::String
|
75
|
+
options.merge! slug_property.options
|
76
|
+
end
|
77
|
+
property :slug, String, options.merge(:unique => true)
|
70
78
|
|
71
|
-
before :valid
|
79
|
+
before respond_to?(:valid?) ? :valid? : :save, :generate_slug
|
72
80
|
before :save, :generate_slug
|
73
81
|
end
|
74
82
|
|
@@ -95,11 +103,11 @@ module DataMapper
|
|
95
103
|
|
96
104
|
def detect_slug_property_by_name(name)
|
97
105
|
p = properties[name]
|
98
|
-
!p.nil? && p.
|
106
|
+
!p.nil? && DataMapper::Property::String >= p.class ? p : nil
|
99
107
|
end
|
100
108
|
|
101
109
|
def get_slug_length
|
102
|
-
slug_property.nil? ? (slug_source_property.nil? ? DataMapper::Property::DEFAULT_LENGTH : slug_source_property.length) : slug_property.length
|
110
|
+
slug_property.nil? ? (slug_source_property.nil? ? DataMapper::Property::String::DEFAULT_LENGTH : slug_source_property.length) : slug_property.length
|
103
111
|
end
|
104
112
|
end # ClassMethods
|
105
113
|
|
@@ -132,13 +140,12 @@ module DataMapper
|
|
132
140
|
# 1. the slug is permanent, and slug column has something valid in it
|
133
141
|
# 2. the slug source value is nil or empty
|
134
142
|
def stale_slug?
|
135
|
-
!((permanent_slug? &&
|
143
|
+
!((permanent_slug? && !slug.blank?) || slug_source_value.blank?)
|
136
144
|
end
|
137
145
|
|
138
146
|
private
|
139
147
|
|
140
148
|
def generate_slug
|
141
|
-
#puts "\nGenerating slug for #{self.class.name}: #{self.key.inspect}\n"
|
142
149
|
return unless self.class.respond_to?(:slug_options) && self.class.slug_options
|
143
150
|
raise InvalidSlugSourceError, 'Invalid slug source.' unless slug_source_property || self.respond_to?(slug_source)
|
144
151
|
return unless stale_slug?
|
@@ -146,40 +153,52 @@ module DataMapper
|
|
146
153
|
end
|
147
154
|
|
148
155
|
def unique_slug
|
149
|
-
old_slug = self.slug
|
150
156
|
max_length = self.class.send(:get_slug_length)
|
151
157
|
base_slug = ::DataMapper::Is::Slug.escape(slug_source_value)[0, max_length]
|
152
|
-
|
158
|
+
# Assuming that 5 digits is more than enought
|
159
|
+
index_length = 5
|
153
160
|
new_slug = base_slug
|
154
161
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
+
variations = max_length - base_slug.length - 1
|
163
|
+
|
164
|
+
slugs = if variations > index_length + 1
|
165
|
+
[base_slug]
|
166
|
+
else
|
167
|
+
((variations - 1)..index_length).map do |n|
|
168
|
+
base_slug[0, max_length - n - 1]
|
169
|
+
end.uniq
|
170
|
+
end
|
171
|
+
|
172
|
+
not_self_conditions = {}
|
173
|
+
unless new?
|
174
|
+
self.model.key.each do |property|
|
175
|
+
not_self_conditions.merge!(property.name.not => self.send(property.name))
|
162
176
|
end
|
177
|
+
end
|
178
|
+
|
179
|
+
max_index = slugs.map do |s|
|
180
|
+
self.class.all(not_self_conditions.merge :slug.like => "#{s}-%")
|
181
|
+
end.flatten.map do |r|
|
182
|
+
index = r.slug.gsub /^(#{slugs.join '|'})-/, ''
|
183
|
+
index =~ /\d+/ ? index.to_i : nil
|
184
|
+
end.compact.max
|
163
185
|
|
164
|
-
|
165
|
-
|
166
|
-
dupe = self.class.first(not_self_conditions.merge(:slug => new_slug))
|
167
|
-
if dupe
|
168
|
-
#puts "Got dupe: #{dupe.inspect}"
|
169
|
-
i = i + 1
|
170
|
-
slug_length = max_length - i.to_s.length - 1
|
171
|
-
new_slug = "#{base_slug[0, slug_length]}-#{i}"
|
172
|
-
#puts "New slug: #{new_slug}"
|
173
|
-
redo
|
174
|
-
end
|
175
|
-
end.call
|
176
|
-
#puts "Found new slug: #{new_slug}"
|
177
|
-
new_slug
|
186
|
+
new_index = if max_index.nil?
|
187
|
+
self.class.first(not_self_conditions.merge :slug => base_slug).present? ? 2 : 1
|
178
188
|
else
|
179
|
-
|
189
|
+
max_index + 1
|
190
|
+
end
|
191
|
+
|
192
|
+
if new_index > 1
|
193
|
+
slug_length = max_length - new_index.to_s.length - 1
|
194
|
+
new_slug = "#{base_slug[0, slug_length]}-#{new_index}"
|
180
195
|
end
|
196
|
+
|
197
|
+
new_slug
|
181
198
|
end
|
182
199
|
end # InstanceMethods
|
200
|
+
|
201
|
+
Model.send(:include, self)
|
183
202
|
end # Slug
|
184
203
|
end # Is
|
185
204
|
end # DataMapper
|
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
|
1
|
+
# encoding: utf-8
|
3
2
|
|
4
|
-
|
5
|
-
describe 'DataMapper::Is::Slug' do
|
3
|
+
require 'spec_helper'
|
6
4
|
|
7
|
-
|
5
|
+
|
6
|
+
describe DataMapper::Is::Slug do
|
7
|
+
|
8
|
+
before :all do
|
9
|
+
class ::User
|
8
10
|
include DataMapper::Resource
|
9
11
|
|
10
12
|
property :id, Serial
|
@@ -19,7 +21,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
19
21
|
is :slug, :source => :slug_for_email, :length => 80, :permanent_slug => false
|
20
22
|
end
|
21
23
|
|
22
|
-
class Post
|
24
|
+
class ::Post
|
23
25
|
include DataMapper::Resource
|
24
26
|
|
25
27
|
property :id, Serial
|
@@ -31,7 +33,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
31
33
|
is :slug, :source => :title
|
32
34
|
end
|
33
35
|
|
34
|
-
class Todo
|
36
|
+
class ::Todo
|
35
37
|
include DataMapper::Resource
|
36
38
|
property :id, Serial
|
37
39
|
property :title, String
|
@@ -39,43 +41,53 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
39
41
|
belongs_to :user
|
40
42
|
end
|
41
43
|
|
42
|
-
class SlugKey
|
44
|
+
class ::SlugKey
|
43
45
|
include DataMapper::Resource
|
44
46
|
property :title, String
|
45
47
|
|
46
48
|
is :slug, :source => :title, :key => true
|
47
49
|
end
|
48
50
|
|
51
|
+
class ::Post2
|
52
|
+
include DataMapper::Resource
|
53
|
+
|
54
|
+
property :id, Serial
|
55
|
+
property :title, String, :length => 30
|
56
|
+
property :content, Text
|
57
|
+
|
58
|
+
is :slug, :source => :title, :permanent_slug => false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
supported_by :sqlite, :mysql, :postgres do
|
63
|
+
|
49
64
|
before :all do
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
(1..10).each do |i|
|
70
|
-
instance_variable_set "@p2_#{i}".to_sym, Post.create(:user => @u2, :title => "DM tricks")
|
71
|
-
end
|
65
|
+
DataMapper.repository do
|
66
|
+
@u1 = User.create(:email => "john@ekohe.com")
|
67
|
+
@p1 = Post.create(:user => @u1, :title => "My first shinny blog post")
|
68
|
+
@p2 = Post.create(:user => @u1, :title => "My second shinny blog post")
|
69
|
+
@p3 = Post.create(:user => @u1, :title => "My third shinny blog post")
|
70
|
+
|
71
|
+
@u2 = User.create(:email => "john@someotherplace.com")
|
72
|
+
@p4 = Post.create(:user => @u2, :title => "My first Shinny blog post")
|
73
|
+
@p5 = Post.create(:user => @u2, :title => "i heart merb and dm")
|
74
|
+
@p6 = Post.create(:user => @u2, :title => "A fancy café")
|
75
|
+
@p7 = Post.create(:user => @u2, :title => "你好")
|
76
|
+
|
77
|
+
(1..10).each do |i|
|
78
|
+
instance_variable_set "@p1_#{i}".to_sym, Post.create(:user => @u2, :title => "another productive day!!")
|
79
|
+
end
|
80
|
+
(1..10).each do |i|
|
81
|
+
instance_variable_set "@p2_#{i}".to_sym, Post.create(:user => @u2, :title => "DM tricks")
|
82
|
+
end
|
72
83
|
|
73
|
-
|
84
|
+
@sk = SlugKey.create(:title => 'slug key')
|
74
85
|
|
75
|
-
|
76
|
-
|
86
|
+
@post1 = Post.create :user => @u1, :title => 'a' * Post.slug_property.length
|
87
|
+
@post2 = Post.create :user => @u1, :title => 'a' * Post.slug_property.length
|
88
|
+
end
|
77
89
|
end
|
78
|
-
|
90
|
+
|
79
91
|
it "should raise error if :source option is not specified" do
|
80
92
|
lambda {
|
81
93
|
class BadUsage
|
@@ -89,20 +101,24 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
89
101
|
end
|
90
102
|
|
91
103
|
it "should display obsolete warning if :size option is used" do
|
92
|
-
|
104
|
+
module M
|
105
|
+
class Thingy
|
106
|
+
end
|
93
107
|
end
|
94
|
-
Thingy.stub!(:warn)
|
95
|
-
Thingy.should_receive(:warn).with("Slug with :size option is deprecated, use :length instead")
|
108
|
+
M::Thingy.stub!(:warn)
|
109
|
+
M::Thingy.should_receive(:warn).with("Slug with :size option is deprecated, use :length instead")
|
96
110
|
|
97
111
|
lambda {
|
98
|
-
class Thingy
|
112
|
+
class M::Thingy
|
99
113
|
include DataMapper::Resource
|
114
|
+
property :id, Serial
|
100
115
|
property :title, String
|
101
116
|
|
102
117
|
is :slug, :source => :title, :size => 20
|
103
118
|
end
|
104
119
|
}.should_not raise_error
|
105
120
|
|
121
|
+
M.const_set 'Thingy', nil
|
106
122
|
end
|
107
123
|
|
108
124
|
it "should generate slugs" do
|
@@ -171,15 +187,15 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
171
187
|
it "should have the right size for properties" do
|
172
188
|
user_slug_property = User.properties[:slug]
|
173
189
|
user_slug_property.should_not be_nil
|
174
|
-
user_slug_property.
|
190
|
+
user_slug_property.should be_an_instance_of(DataMapper::Property::String)
|
175
191
|
user_slug_property.length.should == 80
|
176
192
|
|
177
193
|
post_title_property = Post.properties[:title]
|
178
|
-
post_title_property.
|
194
|
+
post_title_property.should be_an_instance_of(DataMapper::Property::String)
|
179
195
|
post_title_property.length.should == 30
|
180
196
|
|
181
197
|
post_slug_property = Post.properties[:slug]
|
182
|
-
post_slug_property.
|
198
|
+
post_slug_property.should be_an_instance_of(DataMapper::Property::String)
|
183
199
|
post_slug_property.should_not be_nil
|
184
200
|
post_slug_property.length.should == 30
|
185
201
|
end
|
@@ -238,25 +254,15 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
|
|
238
254
|
end
|
239
255
|
|
240
256
|
describe 'editing' do
|
241
|
-
class Post2
|
242
|
-
include DataMapper::Resource
|
243
|
-
property :id, Serial
|
244
|
-
property :title, String, :length => 30
|
245
|
-
property :content, Text
|
246
|
-
|
247
|
-
is :slug, :source => :title, :permanent_slug => false
|
248
|
-
end
|
249
|
-
|
250
|
-
Post2.auto_migrate!
|
251
|
-
|
252
257
|
before :each do
|
253
258
|
Post2.all.destroy!
|
254
259
|
@post = Post2.create :title => 'The Post', :content => 'The content.'
|
255
260
|
end
|
256
261
|
|
257
262
|
it 'should not change slug if source is not changed' do
|
258
|
-
@post.update
|
259
|
-
|
263
|
+
@post.update(:content => 'The other content.').should be_true
|
264
|
+
@post.reload
|
265
|
+
@post.slug.should == 'the-post'
|
260
266
|
end
|
261
267
|
|
262
268
|
it 'should change slug if source is changed' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,38 +1,13 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require 'dm-core'
|
7
|
-
|
8
|
-
# use local dm-adjust if running from a typical dev checkout.
|
9
|
-
lib = File.join('..', 'dm-adjust', 'lib')
|
10
|
-
$LOAD_PATH.unshift(lib) if File.directory?(lib)
|
11
|
-
require 'dm-adjust'
|
12
|
-
|
13
|
-
# Support running specs with 'rake spec' and 'spec'
|
14
|
-
$LOAD_PATH.unshift('lib') unless $LOAD_PATH.include?('lib')
|
15
|
-
|
16
|
-
require 'dm-validations'
|
2
|
+
|
3
|
+
require 'dm-core/spec/setup'
|
4
|
+
require 'dm-core/spec/lib/adapter_helpers'
|
5
|
+
|
17
6
|
require 'dm-is-slug'
|
18
|
-
|
19
|
-
def load_driver(name, default_uri)
|
20
|
-
return false if ENV['ADAPTER'] != name.to_s
|
21
|
-
|
22
|
-
begin
|
23
|
-
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
|
24
|
-
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
|
25
|
-
true
|
26
|
-
rescue LoadError => e
|
27
|
-
warn "Could not load do_#{name}: #{e}"
|
28
|
-
false
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
ENV['ADAPTER'] ||= 'sqlite3'
|
33
|
-
|
34
|
-
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
|
35
|
-
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
|
36
|
-
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
|
7
|
+
require 'dm-migrations'
|
37
8
|
|
9
|
+
DataMapper::Spec.setup!
|
38
10
|
|
11
|
+
Spec::Runner.configure do |config|
|
12
|
+
config.extend(DataMapper::Spec::Adapters::Helpers)
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
desc "Support bundling from local source code (allows BUNDLE_GEMFILE=Gemfile.local bundle foo)"
|
2
|
+
task :local_gemfile do |t|
|
3
|
+
|
4
|
+
root = Pathname(__FILE__).dirname.parent
|
5
|
+
datamapper = root.parent
|
6
|
+
|
7
|
+
root.join('Gemfile.local').open('w') do |f|
|
8
|
+
root.join('Gemfile').open.each do |line|
|
9
|
+
line.sub!(/DATAMAPPER = 'git:\/\/github.com\/datamapper'/, "DATAMAPPER = '#{datamapper}'")
|
10
|
+
line.sub!(/:git => \"#\{DATAMAPPER\}\/(.+?)(?:\.git)?\"/, ':path => "#{DATAMAPPER}/\1"')
|
11
|
+
line.sub!(/do_options\[:git\] = \"#\{DATAMAPPER\}\/(.+?)(?:\.git)?\"/, 'do_options[:path] = "#{DATAMAPPER}/\1"')
|
12
|
+
f.puts line
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-is-slug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Aaron Qian
|
@@ -9,44 +15,61 @@ authors:
|
|
9
15
|
- Nik Radford
|
10
16
|
- Paul
|
11
17
|
- Mike Frawley
|
12
|
-
-
|
18
|
+
- Alexander Mankuta
|
13
19
|
autorequire:
|
14
20
|
bindir: bin
|
15
21
|
cert_chain: []
|
16
22
|
|
17
|
-
date: 2010-
|
23
|
+
date: 2010-10-17 00:00:00 -07:00
|
18
24
|
default_executable:
|
19
25
|
dependencies:
|
20
26
|
- !ruby/object:Gem::Dependency
|
21
27
|
name: dm-core
|
22
|
-
|
23
|
-
|
24
|
-
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
25
31
|
requirements:
|
26
32
|
- - ~>
|
27
33
|
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
|
34
|
+
hash: 19
|
35
|
+
segments:
|
36
|
+
- 1
|
37
|
+
- 0
|
38
|
+
- 2
|
39
|
+
version: 1.0.2
|
40
|
+
type: :runtime
|
41
|
+
version_requirements: *id001
|
30
42
|
- !ruby/object:Gem::Dependency
|
31
43
|
name: unidecode
|
32
|
-
|
33
|
-
|
34
|
-
|
44
|
+
prerelease: false
|
45
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
35
47
|
requirements:
|
36
48
|
- - ~>
|
37
49
|
- !ruby/object:Gem::Version
|
50
|
+
hash: 23
|
51
|
+
segments:
|
52
|
+
- 1
|
53
|
+
- 0
|
54
|
+
- 0
|
38
55
|
version: 1.0.0
|
39
|
-
|
56
|
+
type: :runtime
|
57
|
+
version_requirements: *id002
|
40
58
|
- !ruby/object:Gem::Dependency
|
41
59
|
name: rspec
|
42
|
-
|
43
|
-
|
44
|
-
|
60
|
+
prerelease: false
|
61
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
45
63
|
requirements:
|
46
64
|
- - ~>
|
47
65
|
- !ruby/object:Gem::Version
|
66
|
+
hash: 9
|
67
|
+
segments:
|
68
|
+
- 1
|
69
|
+
- 3
|
48
70
|
version: "1.3"
|
49
|
-
|
71
|
+
type: :development
|
72
|
+
version_requirements: *id003
|
50
73
|
description: DataMapper plugin that generates unique slugs
|
51
74
|
email:
|
52
75
|
- aq1018@gmail.com
|
@@ -54,19 +77,20 @@ email:
|
|
54
77
|
- nik [a] terminaldischarge [d] net
|
55
78
|
- maverick.stoklosa@gmail.com
|
56
79
|
- frawl021@gmail.com
|
57
|
-
- cheba@pointlessone.org
|
80
|
+
- cheba+github@pointlessone.org
|
58
81
|
executables: []
|
59
82
|
|
60
83
|
extensions: []
|
61
84
|
|
62
85
|
extra_rdoc_files:
|
63
86
|
- LICENSE
|
64
|
-
- README.
|
87
|
+
- README.rdoc
|
65
88
|
- TODO
|
66
89
|
files:
|
67
90
|
- .gitignore
|
91
|
+
- Gemfile
|
68
92
|
- LICENSE
|
69
|
-
- README.
|
93
|
+
- README.rdoc
|
70
94
|
- Rakefile
|
71
95
|
- TODO
|
72
96
|
- VERSION
|
@@ -78,6 +102,7 @@ files:
|
|
78
102
|
- spec/spec.opts
|
79
103
|
- spec/spec_helper.rb
|
80
104
|
- tasks/ci.rake
|
105
|
+
- tasks/local_gemfile.rake
|
81
106
|
- tasks/metrics.rake
|
82
107
|
- tasks/spec.rake
|
83
108
|
- tasks/yard.rake
|
@@ -92,21 +117,27 @@ rdoc_options:
|
|
92
117
|
require_paths:
|
93
118
|
- lib
|
94
119
|
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
95
121
|
requirements:
|
96
122
|
- - ">="
|
97
123
|
- !ruby/object:Gem::Version
|
124
|
+
hash: 3
|
125
|
+
segments:
|
126
|
+
- 0
|
98
127
|
version: "0"
|
99
|
-
version:
|
100
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
101
130
|
requirements:
|
102
131
|
- - ">="
|
103
132
|
- !ruby/object:Gem::Version
|
133
|
+
hash: 3
|
134
|
+
segments:
|
135
|
+
- 0
|
104
136
|
version: "0"
|
105
|
-
version:
|
106
137
|
requirements: []
|
107
138
|
|
108
139
|
rubyforge_project:
|
109
|
-
rubygems_version: 1.3.
|
140
|
+
rubygems_version: 1.3.7
|
110
141
|
signing_key:
|
111
142
|
specification_version: 3
|
112
143
|
summary: DataMapper plugin that generates unique slugs
|
data/README.textile
DELETED
@@ -1,56 +0,0 @@
|
|
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")
|