slugged 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +14 -0
- data/.rvmrc +1 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +20 -0
- data/README.md +125 -0
- data/Rakefile +110 -0
- data/lib/generators/slugged/slug_migration/slug_migration_generator.rb +24 -0
- data/lib/generators/slugged/slug_migration/templates/migration.erb +12 -0
- data/lib/generators/slugged/slugs/slugs_generator.rb +24 -0
- data/lib/generators/slugged/slugs/templates/migration.erb +20 -0
- data/lib/slugged.rb +80 -0
- data/lib/slugged/active_record_methods.rb +112 -0
- data/lib/slugged/caching.rb +87 -0
- data/lib/slugged/finders.rb +19 -0
- data/lib/slugged/memory_cache.rb +29 -0
- data/lib/slugged/railtie.rb +9 -0
- data/lib/slugged/scopes.rb +13 -0
- data/lib/slugged/slug.rb +36 -0
- data/lib/slugged/slug_history.rb +41 -0
- data/lib/slugged/version.rb +8 -0
- data/test/caching_test.rb +77 -0
- data/test/helper.rb +44 -0
- data/test/is_sluggable_test.rb +155 -0
- data/test/model_definitions.rb +19 -0
- data/test/slug_history_test.rb +86 -0
- data/test/slugged_test.rb +27 -0
- metadata +174 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use ree@slugged --create
|
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
source :gemcutter
|
2
|
+
|
3
|
+
# Setup gems
|
4
|
+
gem "activerecord", "= 3.0.0"
|
5
|
+
gem "reversible_data"
|
6
|
+
gem "uuid"
|
7
|
+
gem "sqlite3-ruby", :require => "sqlite3"
|
8
|
+
|
9
|
+
gem "shoulda", :require => nil
|
10
|
+
gem "redgreen", :require => nil if RUBY_VERSION < "1.9"
|
11
|
+
gem "rcov", :require => nil
|
12
|
+
gem "reek", :require => nil
|
13
|
+
gem "roodi", :require => nil
|
14
|
+
gem "flay", :require => nil
|
15
|
+
gem "flog", :require => nil
|
16
|
+
gem "rake", :require => nil
|
17
|
+
gem "Saikuro", :require => nil
|
18
|
+
gem "jeweler", :require => nil
|
19
|
+
|
20
|
+
gem 'ruby-debug', :require => nil
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
Saikuro (1.1.0)
|
5
|
+
activemodel (3.0.0)
|
6
|
+
activesupport (= 3.0.0)
|
7
|
+
builder (~> 2.1.2)
|
8
|
+
i18n (~> 0.4.1)
|
9
|
+
activerecord (3.0.0)
|
10
|
+
activemodel (= 3.0.0)
|
11
|
+
activesupport (= 3.0.0)
|
12
|
+
arel (~> 1.0.0)
|
13
|
+
tzinfo (~> 0.3.23)
|
14
|
+
activesupport (3.0.0)
|
15
|
+
arel (1.0.1)
|
16
|
+
activesupport (~> 3.0.0)
|
17
|
+
builder (2.1.2)
|
18
|
+
columnize (0.3.1)
|
19
|
+
flay (1.4.1)
|
20
|
+
ruby_parser (~> 2.0)
|
21
|
+
sexp_processor (~> 3.0)
|
22
|
+
flog (2.5.0)
|
23
|
+
ruby_parser (~> 2.0)
|
24
|
+
sexp_processor (~> 3.0)
|
25
|
+
gemcutter (0.6.1)
|
26
|
+
git (1.2.5)
|
27
|
+
i18n (0.4.1)
|
28
|
+
jeweler (1.4.0)
|
29
|
+
gemcutter (>= 0.1.0)
|
30
|
+
git (>= 1.2.5)
|
31
|
+
rubyforge (>= 2.0.0)
|
32
|
+
json_pure (1.4.6)
|
33
|
+
linecache (0.43)
|
34
|
+
macaddr (1.0.0)
|
35
|
+
rake (0.8.7)
|
36
|
+
rcov (0.9.9)
|
37
|
+
redgreen (1.2.2)
|
38
|
+
reek (1.2.8)
|
39
|
+
ruby2ruby (~> 1.2)
|
40
|
+
ruby_parser (~> 2.0)
|
41
|
+
sexp_processor (~> 3.0)
|
42
|
+
reversible_data (0.1.0)
|
43
|
+
roodi (2.1.0)
|
44
|
+
ruby_parser
|
45
|
+
ruby-debug (0.10.3)
|
46
|
+
columnize (>= 0.1)
|
47
|
+
ruby-debug-base (~> 0.10.3.0)
|
48
|
+
ruby-debug-base (0.10.3)
|
49
|
+
linecache (>= 0.3)
|
50
|
+
ruby2ruby (1.2.5)
|
51
|
+
ruby_parser (~> 2.0)
|
52
|
+
sexp_processor (~> 3.0)
|
53
|
+
ruby_parser (2.0.5)
|
54
|
+
sexp_processor (~> 3.0)
|
55
|
+
rubyforge (2.0.4)
|
56
|
+
json_pure (>= 1.1.7)
|
57
|
+
sexp_processor (3.0.5)
|
58
|
+
shoulda (2.11.3)
|
59
|
+
sqlite3-ruby (1.3.1)
|
60
|
+
tzinfo (0.3.23)
|
61
|
+
uuid (2.3.1)
|
62
|
+
macaddr (~> 1.0)
|
63
|
+
|
64
|
+
PLATFORMS
|
65
|
+
ruby
|
66
|
+
|
67
|
+
DEPENDENCIES
|
68
|
+
Saikuro
|
69
|
+
activerecord (= 3.0.0)
|
70
|
+
flay
|
71
|
+
flog
|
72
|
+
jeweler
|
73
|
+
rake
|
74
|
+
rcov
|
75
|
+
redgreen
|
76
|
+
reek
|
77
|
+
reversible_data
|
78
|
+
roodi
|
79
|
+
ruby-debug
|
80
|
+
shoulda
|
81
|
+
sqlite3-ruby
|
82
|
+
uuid
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Darcy Laycock
|
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/README.md
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Slugged #
|
2
|
+
|
3
|
+
## About ###
|
4
|
+
|
5
|
+
Slugged is a simple slug library for ActiveRecord 3.0+.
|
6
|
+
|
7
|
+
It's main features are:
|
8
|
+
|
9
|
+
1. A very simple and tested codebase
|
10
|
+
2. Support for slug history (e.g. if a users slug changes, it will record the old slug)
|
11
|
+
3. Simple defaulting for slugs to UUID's (to avoid showing ID's.)
|
12
|
+
4. Built on ActiveRecord 3.0
|
13
|
+
5. If stringex is installed, uses stringex's transliteration stuff
|
14
|
+
|
15
|
+
|
16
|
+
Slugged used to be called Pseudocephalopod - a name inspired by the Jason Wander series of
|
17
|
+
books which I just happened to be reading when I had the need for this that focuses on a
|
18
|
+
war with slug-like creatures.
|
19
|
+
|
20
|
+
### Why? ###
|
21
|
+
|
22
|
+
I love the idea of friendly\_id, and most of the implementation but it felt bloated
|
23
|
+
to me and my experiences on getting it to work correctly with Rails 3 left a base taste
|
24
|
+
in my mouth / was altogether hacky.
|
25
|
+
|
26
|
+
Slugged is very much inspired by friendly id but with a much simpler codebase
|
27
|
+
and built to work on Rails 3 from the start.
|
28
|
+
|
29
|
+
## Usage ##
|
30
|
+
|
31
|
+
Using Slugged is simple. In Rails, simply drop this in your Gemfile:
|
32
|
+
|
33
|
+
gem 'slugged'
|
34
|
+
|
35
|
+
Optionally restricting the version.
|
36
|
+
|
37
|
+
Next, if you wish to use slug history run:
|
38
|
+
|
39
|
+
$ rails generate slugged:slugs
|
40
|
+
|
41
|
+
Otherwise, when calling is\_sluggable make sure to include :history => false
|
42
|
+
|
43
|
+
Next, you need to add a cached slug column to your model and add an index. In your migration,
|
44
|
+
you'd usually want something like:
|
45
|
+
|
46
|
+
add_column :users, :cached_slug, :string
|
47
|
+
add_index :users, :cached_slug
|
48
|
+
|
49
|
+
Or, using our build in generator:
|
50
|
+
|
51
|
+
$ rails generate slugged:slug_migration Model
|
52
|
+
|
53
|
+
Lastly, in your model, call is\_sluggable:
|
54
|
+
|
55
|
+
class User
|
56
|
+
is_sluggable :name
|
57
|
+
end
|
58
|
+
|
59
|
+
is\_sluggable accepts the source method name as a symbol, and an optional has of options including:
|
60
|
+
|
61
|
+
* _:sync_ - when source column changes, save the result. Defaults to true.
|
62
|
+
* _:convertor_ - a symbol (for a method) or block for how to generate the base slug. Defaults to :to\_url if available, parameterize otherwise.
|
63
|
+
* _:history_ - use slug history (e.g. if the name changes, it records the previous version in a slugs table). Defaults to true
|
64
|
+
* _:uuid_ - If the slug is blank, uses a generated uuid instead. Defaults to true
|
65
|
+
* _:slug\_column_ - the column in which to store the slug. Defaults to _:cached\_slug_
|
66
|
+
* _:to\_param_ - if true (by default), overrides to_param to use the slug
|
67
|
+
* _:use\_cache_ - uses Slugged.cache if available to cache any lookups e.g. in memcache.
|
68
|
+
|
69
|
+
Once installed, it provides the following methods:
|
70
|
+
|
71
|
+
### User.find\_using\_slug "some-slug" ###
|
72
|
+
|
73
|
+
Finds a user from a slug (which can be the record's id, it's cached slug or, if enabled, slug history)
|
74
|
+
|
75
|
+
### User.other\_than(record) ###
|
76
|
+
|
77
|
+
Returns a relationship which returns records other than the given.
|
78
|
+
|
79
|
+
### User.with\_cached\_slug(record) ###
|
80
|
+
|
81
|
+
Returns a relationship which returns records with the given cached slug.
|
82
|
+
|
83
|
+
### User#generate\_slug ###
|
84
|
+
|
85
|
+
Forces the generation of a current slug
|
86
|
+
|
87
|
+
### User#generate\_slug! ###
|
88
|
+
|
89
|
+
Forces the generation of a current slug and saves it
|
90
|
+
|
91
|
+
### User#autogenerate\_slug ###
|
92
|
+
|
93
|
+
Generates a slug if not already present.
|
94
|
+
|
95
|
+
### User#has\_better\_slug? ###
|
96
|
+
|
97
|
+
When found via Model.find\_using\_slug, it will return try
|
98
|
+
if there is a better slug available. Intended for use in redirects etc.
|
99
|
+
|
100
|
+
## Working on Slugged ##
|
101
|
+
|
102
|
+
To run tests, simply do the following:
|
103
|
+
|
104
|
+
bundle install
|
105
|
+
rake
|
106
|
+
|
107
|
+
And it's ready!
|
108
|
+
|
109
|
+
## Contributors ##
|
110
|
+
|
111
|
+
Thanks to the following who contributed functionality / bug fixes:
|
112
|
+
|
113
|
+
* [Matt Pruitt](http://github.com/guitsaru)
|
114
|
+
|
115
|
+
## Note on Patches/Pull Requests ##
|
116
|
+
|
117
|
+
* Fork the project.
|
118
|
+
* Make your feature addition or bug fix.
|
119
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
120
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
121
|
+
* Send me a pull request. Bonus points for topic branches.
|
122
|
+
|
123
|
+
## Copyright ##
|
124
|
+
|
125
|
+
Copyright (c) 2010 Darcy Laycock. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup
|
4
|
+
Bundler.require
|
5
|
+
|
6
|
+
require 'rake'
|
7
|
+
|
8
|
+
require File.expand_path('../lib/slugged/version', __FILE__)
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'jeweler'
|
12
|
+
Jeweler::Tasks.new do |gem|
|
13
|
+
gem.version = Slugged::Version::STRING
|
14
|
+
gem.name = "slugged"
|
15
|
+
gem.summary = %Q{Super simple slugs for ActiveRecord 3.0 and higher, with support for slug history}
|
16
|
+
gem.description = %Q{Super simple slugs for ActiveRecord 3.0 and higher, with support for slug history}
|
17
|
+
gem.email = "sutto@sutto.net"
|
18
|
+
gem.homepage = "http://github.com/Sutto/slugged"
|
19
|
+
gem.authors = ["Darcy Laycock"]
|
20
|
+
gem.add_dependency "activerecord", "~> 3.0.0"
|
21
|
+
gem.add_dependency "activesupport", "~> 3.0.0"
|
22
|
+
gem.add_dependency "uuid"
|
23
|
+
gem.add_development_dependency "shoulda", ">= 0"
|
24
|
+
gem.add_development_dependency "reversible_data"
|
25
|
+
end
|
26
|
+
Jeweler::GemcutterTasks.new
|
27
|
+
rescue LoadError
|
28
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'rake/testtask'
|
32
|
+
Rake::TestTask.new(:test) do |test|
|
33
|
+
test.libs << 'lib' << 'test'
|
34
|
+
test.pattern = 'test/**/*_test.rb'
|
35
|
+
test.verbose = true
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rcov/rcovtask'
|
40
|
+
Rcov::RcovTask.new do |test|
|
41
|
+
test.libs << 'test'
|
42
|
+
test.pattern = 'test/**/*_test.rb'
|
43
|
+
test.verbose = true
|
44
|
+
test.rcov_opts << '--exclude /gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-'
|
45
|
+
test.output_dir = "metrics/coverage"
|
46
|
+
end
|
47
|
+
rescue LoadError
|
48
|
+
task :rcov do
|
49
|
+
abort "Rcov isn't installed, please run via bundle exec after bundle installing"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
task :metrics => [:rcov, :saikuro, :reek, :flay, :flog, :roodi]
|
54
|
+
|
55
|
+
task :test => :check_dependencies
|
56
|
+
|
57
|
+
task :flog do
|
58
|
+
system "flog lib"
|
59
|
+
end
|
60
|
+
|
61
|
+
task :saikuro do
|
62
|
+
system "rm -rf metrics/saikuro && mkdir -p metrics/saikuro && saikuro -c -t -i lib/ -y 0 -w 11 -e 16 -o metrics/saikuro/"
|
63
|
+
end
|
64
|
+
|
65
|
+
begin
|
66
|
+
require 'flay'
|
67
|
+
require 'flay_task'
|
68
|
+
FlayTask.new
|
69
|
+
rescue LoadError
|
70
|
+
task :flay do
|
71
|
+
abort "Flay isn't installed, please run via bundle exec after bundle installing"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
begin
|
76
|
+
require 'reek/rake/task'
|
77
|
+
Reek::Rake::Task.new do |t|
|
78
|
+
t.fail_on_error = true
|
79
|
+
t.verbose = false
|
80
|
+
t.source_files = 'lib/**/*.rb'
|
81
|
+
end
|
82
|
+
rescue LoadError
|
83
|
+
task :reek do
|
84
|
+
abort "Reek isn't installed, please run via bundle exec after bundle installing"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
begin
|
89
|
+
require 'roodi'
|
90
|
+
require 'roodi_task'
|
91
|
+
RoodiTask.new do |t|
|
92
|
+
t.verbose = false
|
93
|
+
end
|
94
|
+
rescue LoadError
|
95
|
+
task :roodi do
|
96
|
+
abort "Roodi isn't installed, please run via bundle exec after bundle installing"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
task :default => :test
|
101
|
+
|
102
|
+
require 'rake/rdoctask'
|
103
|
+
Rake::RDocTask.new do |rdoc|
|
104
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
105
|
+
|
106
|
+
rdoc.rdoc_dir = 'rdoc'
|
107
|
+
rdoc.title = "slugged #{version}"
|
108
|
+
rdoc.rdoc_files.include('README*')
|
109
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
110
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Slugged
|
2
|
+
module Generators
|
3
|
+
class SlugMigrationGenerator < Rails::Generators::NamedBase
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
def self.source_root
|
7
|
+
@_ps_source_root ||= File.expand_path("templates", File.dirname(__FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.next_migration_number(dirname) #:nodoc:
|
11
|
+
if ActiveRecord::Base.timestamped_migrations
|
12
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
else
|
14
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_migration_file
|
19
|
+
migration_template "migration.erb", "db/migrate/add_cached_slug_to_#{table_name}.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class <%= migration_class_name %> < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
add_column <%= table_name.to_sym.inspect %>, :cached_slug, :string
|
5
|
+
add_index <%= table_name.to_sym.inspect %>, :cached_slug
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.down
|
9
|
+
remove_column <%= table_name.to_sym.inspect %>, :cached_slug
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Slugged
|
2
|
+
module Generators
|
3
|
+
class SlugsGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
|
6
|
+
def self.source_root
|
7
|
+
@_ps_source_root ||= File.expand_path("templates", File.dirname(__FILE__))
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.next_migration_number(dirname) #:nodoc:
|
11
|
+
if ActiveRecord::Base.timestamped_migrations
|
12
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
else
|
14
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_migration_file
|
19
|
+
migration_template "migration.erb", "db/migrate/create_slugged_slugs.rb"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|