dm-is-slug 0.10.2 → 1.0.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/.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")
|