find_by_param 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +121 -0
- data/Rakefile +45 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/find_by_param.rb +192 -0
- data/shoulda_macros/find_by_param.rb +34 -0
- data/shoulda_macros/matchers.rb +49 -0
- data/tasks/find_by_param_tasks.rake +4 -0
- data/test/find_by_param_test.rb +156 -0
- data/test/schema.rb +21 -0
- data/test/test_helper.rb +17 -0
- data/uninstall.rb +1 -0
- data/version.yml +4 -0
- metadata +121 -0
data/.gitignore
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 [Michael Bumann - Railslove.com]
|
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,121 @@
|
|
1
|
+
find_by_param
|
2
|
+
=============
|
3
|
+
|
4
|
+
*find_by_param* helps you dealing with permalinks and finding objects by our
|
5
|
+
permalink value
|
6
|
+
|
7
|
+
class Post < ActiveRecord:Base
|
8
|
+
make_permalink :with => :title
|
9
|
+
end
|
10
|
+
|
11
|
+
now you can do
|
12
|
+
|
13
|
+
Post.find_by_param(...)
|
14
|
+
|
15
|
+
If you have a permalink-column *find_by_param* saves the permalink there and
|
16
|
+
uses that otherwise it just uses the provided attribute.
|
17
|
+
|
18
|
+
|
19
|
+
Installation
|
20
|
+
========
|
21
|
+
|
22
|
+
As gem from gemcutter:
|
23
|
+
-------------
|
24
|
+
|
25
|
+
% gem install find_by_param
|
26
|
+
|
27
|
+
Building your own
|
28
|
+
-------------
|
29
|
+
|
30
|
+
% rake gemspec
|
31
|
+
% rake install
|
32
|
+
|
33
|
+
As Rails plugin
|
34
|
+
-------------
|
35
|
+
|
36
|
+
% script/plugin install git@github.com:bumi/find_by_param.git
|
37
|
+
|
38
|
+
|
39
|
+
Examples
|
40
|
+
========
|
41
|
+
|
42
|
+
Configuration
|
43
|
+
-------------
|
44
|
+
|
45
|
+
make_permalink :with => :login
|
46
|
+
make_permalink :with => :title, :prepend_id => true
|
47
|
+
make_permalink :with => :name, :forbidden => %w(new edit)
|
48
|
+
|
49
|
+
Client code
|
50
|
+
-----------
|
51
|
+
|
52
|
+
Post.create(:title => "hey ho let's go!").to_param #=> "hey-ho-lets-go"
|
53
|
+
# ... to_param is the method Rails calls to create the URL values
|
54
|
+
|
55
|
+
Post.find_by_param("hey-ho-lets-go") #=> <Post>
|
56
|
+
|
57
|
+
Post.find_by_param("is-not-there") #=> nil
|
58
|
+
Post.find_by_param!("is-not-there") #=> raises ActiveRecord::RecordNotFound
|
59
|
+
|
60
|
+
|
61
|
+
Basic Documentation
|
62
|
+
===================
|
63
|
+
|
64
|
+
Options for make_permalink
|
65
|
+
--------------------------
|
66
|
+
|
67
|
+
The following options may be used, when configuring the permalink generation
|
68
|
+
with `make_permalink`.
|
69
|
+
|
70
|
+
* `:with` (required)
|
71
|
+
|
72
|
+
The attribute that should be used as permalink
|
73
|
+
|
74
|
+
* `:field`
|
75
|
+
|
76
|
+
The name of your permalink column. make_permalink first checks if there is
|
77
|
+
a column.
|
78
|
+
|
79
|
+
* `:prepend_id => [true|false]`
|
80
|
+
|
81
|
+
Do you want to prepend the ID to the permalink? for URLs like:
|
82
|
+
`posts/123-my-post-title` - *find_by_param* uses the ID column to search.
|
83
|
+
|
84
|
+
* `:escape => [true|false]`
|
85
|
+
|
86
|
+
Do you want to escape the permalink value? (strip chars like `öä?&`) -
|
87
|
+
actually you must do that
|
88
|
+
|
89
|
+
* `:forbidden => [Regexp|String|Array of Strings]`
|
90
|
+
|
91
|
+
Define which values should be forbidden. This is useful when combining user
|
92
|
+
defined values to generate permalinks in combination with restful routing.
|
93
|
+
**Make sure, especially in the case of a Regexp argument, that values may
|
94
|
+
become valid by adding or incrementing a trailing integer.**
|
95
|
+
|
96
|
+
|
97
|
+
Class methods provided by *find_by_param*
|
98
|
+
---------------------------------------
|
99
|
+
|
100
|
+
The following methods are added as public methods to all classes containing a
|
101
|
+
permalink field.
|
102
|
+
|
103
|
+
* `find_by_param(id)`
|
104
|
+
|
105
|
+
Look up a value by its permalink value, returns matching instance or
|
106
|
+
`nil`, if none is found.
|
107
|
+
|
108
|
+
* `find_by_param!(id)`
|
109
|
+
|
110
|
+
Look up a value by its permalink value, returns matching instance or
|
111
|
+
raises `ActiveRecord::RecordNotFound`, if none is found.
|
112
|
+
|
113
|
+
|
114
|
+
Issues
|
115
|
+
=======
|
116
|
+
|
117
|
+
* Alex Sharp (http://github.com/ajsharp) pointed to an issue with STI. Better call make_permalink in every child class and not only in the parent class..
|
118
|
+
* write nice docs
|
119
|
+
* write nicer tests
|
120
|
+
|
121
|
+
Copyright (c) 2007 \[Michael Bumann - Railslove.com\], released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the find_by_param plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the find_by_param plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'FindByParam'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
gem 'jeweler', '>= 0.11.0'
|
26
|
+
require 'jeweler'
|
27
|
+
Jeweler::Tasks.new do |s|
|
28
|
+
s.name = "find_by_param"
|
29
|
+
s.summary = "Rails plugin to handle permalink values"
|
30
|
+
s.email = "michael@railslove.com"
|
31
|
+
s.homepage = "http://github.com/bumi/find_by_param"
|
32
|
+
s.description = "find_by_param is a nice and easy way to handle " +
|
33
|
+
"permalinks and dealing with searching for to_param values"
|
34
|
+
s.authors = ["Michael Bumann", "Gregor Schmidt"]
|
35
|
+
s.add_dependency('activerecord', '>= 2.3')
|
36
|
+
s.add_dependency('activesupport', '>= 2.3')
|
37
|
+
|
38
|
+
s.add_development_dependency('sqlite3-ruby')
|
39
|
+
s.add_development_dependency('jeweler', '>= 0.11.0')
|
40
|
+
s.add_development_dependency('rake')
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
puts "Jeweler not available. Install it with: sudo gem install jeweler --version '>= 0.11.0'"
|
44
|
+
exit(1)
|
45
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'find_by_param'
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
puts IO.read(File.join(File.dirname(__FILE__), 'README'))
|
@@ -0,0 +1,192 @@
|
|
1
|
+
begin
|
2
|
+
$KCODE = 'u'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_support'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
module Railslove
|
8
|
+
module Plugins
|
9
|
+
module FindByParam
|
10
|
+
def self.enable
|
11
|
+
return if ActiveRecord::Base.kind_of?(self::ClassMethods)
|
12
|
+
|
13
|
+
ActiveRecord::Base.class_eval do
|
14
|
+
class_inheritable_accessor :permalink_options
|
15
|
+
self.permalink_options = {:param => :id}
|
16
|
+
|
17
|
+
#default finders these are overwritten if you use make_permalink in
|
18
|
+
# your model
|
19
|
+
def self.find_by_param(value,args={})
|
20
|
+
find_by_id(value,args)
|
21
|
+
end
|
22
|
+
def self.find_by_param!(value,args={})
|
23
|
+
find(value,args)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
ActiveRecord::Base.extend(self::ClassMethods)
|
27
|
+
end
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
|
31
|
+
|
32
|
+
=begin rdoc
|
33
|
+
|
34
|
+
This method initializes find_by_param
|
35
|
+
|
36
|
+
class Post < ActiveRecord::Base
|
37
|
+
make_permalink :with => :title, :prepend_id=>true
|
38
|
+
end
|
39
|
+
|
40
|
+
The only required parameter, is <tt>:with</tt>.
|
41
|
+
|
42
|
+
If you want to use a non URL-save attribute as permalink your model should have a permalink-column to save the escaped permalink value. This field is then used for search.
|
43
|
+
|
44
|
+
If your you can just say make_permalink :with => :login and you're done.
|
45
|
+
|
46
|
+
You can use for example User.find_by_param(params[:id], args) to find the user by the defined permalink.
|
47
|
+
|
48
|
+
== Available options
|
49
|
+
|
50
|
+
<tt>:with</tt>:: (required) The attribute that should be used as permalink
|
51
|
+
<tt>:field</tt>:: The name of your permalink column. make_permalink first checks if there is a column.
|
52
|
+
<tt>:prepend_id</tt>:: [true|false] Do you want to prepend the ID to the permalink? for URLs like: posts/123-my-post-title - find_by_param uses the ID column to search.
|
53
|
+
<tt>:escape</tt>:: [true|false] Do you want to escape the permalink value? (strip chars like öä?&) - actually you must do that
|
54
|
+
<tt>:validate</tt>:: [true|false] Don't validate the :with field - set this to false if you validate it on your own
|
55
|
+
<tt>:forbidden</tt>:: [Regexp|String|Array of Strings] Define which values should be forbidden. This is useful when combining user defined values to generate permalinks in combination with restful routing. <b>Make sure, especially in the case of a Regexp argument, that values may become valid by adding or incrementing a trailing integer.</b>
|
56
|
+
=end
|
57
|
+
def make_permalink(options={})
|
58
|
+
options[:field] ||= "permalink"
|
59
|
+
options[:param] = options[:with] # :with => :login - but if we have a spcific permalink column we need to set :param to the name of that column
|
60
|
+
options[:escape] ||= true
|
61
|
+
options[:prepend_id] ||= false
|
62
|
+
options[:param_size] ||= 50
|
63
|
+
options[:validate] = true if options[:validate].nil?
|
64
|
+
|
65
|
+
# validate if there is something we can use as param. you can overwrite the validate_param_is_not_blank method to customize the validation and the error messge.
|
66
|
+
if !options[:prepend_id] || !options[:validate]
|
67
|
+
validate :validate_param_is_not_blank
|
68
|
+
end
|
69
|
+
|
70
|
+
if forbidden = options.delete(:forbidden)
|
71
|
+
if forbidden.is_a? Regexp
|
72
|
+
options[:forbidden_match] = forbidden
|
73
|
+
else
|
74
|
+
options[:forbidden_strings] = Array(forbidden).map(&:to_s)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if self.column_names.include?(options[:field].to_s)
|
79
|
+
options[:param] = options[:field]
|
80
|
+
before_save :save_permalink
|
81
|
+
end
|
82
|
+
|
83
|
+
self.permalink_options = options
|
84
|
+
extend Railslove::Plugins::FindByParam::SingletonMethods
|
85
|
+
include Railslove::Plugins::FindByParam::InstanceMethods
|
86
|
+
rescue
|
87
|
+
puts "[find_by_param error] database not available?"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
module SingletonMethods
|
92
|
+
|
93
|
+
=begin rdoc
|
94
|
+
|
95
|
+
Search for an object by the defined permalink column. Similar to find_by_login.
|
96
|
+
Returns nil if nothing is found
|
97
|
+
Accepts an options hash as a second parameter which is passed on to the rails finder.
|
98
|
+
=end
|
99
|
+
def find_by_param(value,args={})
|
100
|
+
if permalink_options[:prepend_id]
|
101
|
+
param = "id"
|
102
|
+
value = value.to_i
|
103
|
+
else
|
104
|
+
param = permalink_options[:param]
|
105
|
+
end
|
106
|
+
self.send("find_by_#{param}".to_sym, value, args)
|
107
|
+
end
|
108
|
+
|
109
|
+
=begin rdoc
|
110
|
+
|
111
|
+
Like find_by_param but raises an ActiveRecord::RecordNotFound error if nothing is found. Similar to find()
|
112
|
+
|
113
|
+
Accepts an options hash as a second parameter which is passed on to the rails finder.
|
114
|
+
=end
|
115
|
+
def find_by_param!(value, args={})
|
116
|
+
param = permalink_options[:param]
|
117
|
+
obj = find_by_param(value, args)
|
118
|
+
raise ::ActiveRecord::RecordNotFound unless obj
|
119
|
+
obj
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
module InstanceMethods
|
124
|
+
def to_param
|
125
|
+
value = self.send(permalink_options[:param]).dup.to_s.downcase rescue ""
|
126
|
+
returning "" do |param|
|
127
|
+
param << "#{id}" if value.blank? || permalink_options[:prepend_id]
|
128
|
+
param << "-" if permalink_options[:prepend_id]
|
129
|
+
param << "#{escape_and_truncate_permalink(value)}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
def save_permalink
|
136
|
+
return unless self.class.column_names.include?(permalink_options[:field].to_s)
|
137
|
+
counter = 0
|
138
|
+
base_value = escape_and_truncate_permalink(send(permalink_options[:with])).downcase
|
139
|
+
permalink_value = "#{base_value}"
|
140
|
+
|
141
|
+
conditions = ["#{self.class.table_name}.#{permalink_options[:field]} = ?", permalink_value]
|
142
|
+
unless new_record?
|
143
|
+
conditions.first << " and #{self.class.table_name}.#{self.class.primary_key} != ?"
|
144
|
+
conditions << self.send(self.class.primary_key.to_sym)
|
145
|
+
end
|
146
|
+
while is_forbidden?(permalink_value) or
|
147
|
+
self.class.count(:all, :conditions => conditions) > 0
|
148
|
+
permalink_value = "#{base_value}-#{counter += 1}"
|
149
|
+
conditions[1] = permalink_value
|
150
|
+
end
|
151
|
+
write_attribute(permalink_options[:field], permalink_value)
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
def validate_param_is_not_blank
|
156
|
+
errors.add(permalink_options[:with], "must have at least one non special character (a-z 0-9)") if self.escape_permalink( self.send(permalink_options[:with]) ).blank?
|
157
|
+
end
|
158
|
+
|
159
|
+
def escape_permalink(value)
|
160
|
+
value.to_s.parameterize
|
161
|
+
end
|
162
|
+
|
163
|
+
def is_forbidden?(permalink_value)
|
164
|
+
is_forbidden_string?(permalink_value) ||
|
165
|
+
matches_forbidden_regexp?(permalink_value)
|
166
|
+
end
|
167
|
+
|
168
|
+
def is_forbidden_string?(permalink_value)
|
169
|
+
permalink_options[:forbidden_strings] &&
|
170
|
+
permalink_options[:forbidden_strings].include?(permalink_value)
|
171
|
+
end
|
172
|
+
|
173
|
+
def matches_forbidden_regexp?(permalink_value)
|
174
|
+
permalink_options[:forbidden_match] &&
|
175
|
+
permalink_options[:forbidden_match] =~ permalink_value
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
#this escapes and truncates a value.
|
180
|
+
#used to escape and truncate permalink value
|
181
|
+
def escape_and_truncate_permalink(value)
|
182
|
+
self.escape_permalink(value)[0...self.permalink_options[:param_size]]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if defined?(ActiveRecord)
|
191
|
+
Railslove::Plugins::FindByParam.enable
|
192
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'matchers')
|
2
|
+
# this is experimental. My first try to write a shoulda matcher
|
3
|
+
module Railslove
|
4
|
+
module Plugins
|
5
|
+
module FindByParam
|
6
|
+
module Shoulda
|
7
|
+
include Matchers
|
8
|
+
|
9
|
+
def should_make_permalink(options = {})
|
10
|
+
klass = self.name.gsub(/Test$/, '').constantize
|
11
|
+
|
12
|
+
options[:field] ||= "permalink"
|
13
|
+
options[:param] = options[:with]
|
14
|
+
options[:escape] ||= true
|
15
|
+
options[:prepend_id] ||= false
|
16
|
+
options[:param_size] ||= 50
|
17
|
+
options[:validate] ||= true
|
18
|
+
if klass.column_names.include?(options[:field].to_s)
|
19
|
+
options[:param] = options[:field]
|
20
|
+
end
|
21
|
+
|
22
|
+
matcher = validate_find_by_param_options(options)
|
23
|
+
|
24
|
+
should matcher.description do
|
25
|
+
assert_accepts(matcher, klass)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
class Test::Unit::TestCase
|
33
|
+
extend Railslove::Plugins::FindByParam::Shoulda
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# this nesting is actually a bit stupid :)
|
2
|
+
module Railslove
|
3
|
+
module Plugins
|
4
|
+
module FindByParam
|
5
|
+
module Shoulda
|
6
|
+
module Matchers
|
7
|
+
def validate_find_by_param_options(options)
|
8
|
+
FindByParamMatcher.new(options)
|
9
|
+
end
|
10
|
+
class FindByParamMatcher
|
11
|
+
def initialize(options)
|
12
|
+
@expected_options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def matches?(klass)
|
16
|
+
@klass = klass
|
17
|
+
valid_permalink_options? && responds_to_find_by_param_class_methods? && responds_to_find_by_param_instance_methods?
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
"have valid find_by_pram_configuration"
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message
|
25
|
+
"invalid find_by_param configuration: expected: #{@expected_options.inspect} got: #{@klass.permalink_options.inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
def valid_permalink_options?
|
30
|
+
@expected_options == @klass.permalink_options
|
31
|
+
end
|
32
|
+
|
33
|
+
def responds_to_find_by_param_class_methods?
|
34
|
+
%w{make_permalink find_by_param find_by_param!}.all? do |method|
|
35
|
+
@klass.respond_to?(method)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def responds_to_find_by_param_instance_methods?
|
40
|
+
%w{escape_permalink}.all? do |method|
|
41
|
+
@klass.new.respond_to?(method)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
# TODO: make some nice mock objects!!!!!!!!!!!!!!!!!!
|
4
|
+
class Post < ActiveRecord::Base; end
|
5
|
+
class User < ActiveRecord::Base; end
|
6
|
+
class Article < ActiveRecord::Base; end
|
7
|
+
class Author < ActiveRecord::Base;
|
8
|
+
def full_name
|
9
|
+
[first_name, last_name].join(" ")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# TODO DO BETTER TESTING!!!!
|
14
|
+
class FindByParamTest < Test::Unit::TestCase
|
15
|
+
|
16
|
+
def test_default_should_return_id
|
17
|
+
post = Post.create(:title=>"hey ho let's go!")
|
18
|
+
assert_equal post.to_param, post.id.to_s
|
19
|
+
assert_equal post.permalink, nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_permalink_should_be_saved
|
23
|
+
Post.class_eval "make_permalink :with => :title"
|
24
|
+
post = Post.create(:title=>"hey ho let's go!")
|
25
|
+
assert_equal "hey-ho-let-s-go", post.to_param
|
26
|
+
assert_equal post.permalink, post.to_param
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_permalink_should_be_allowed_on_virtual_attributes
|
30
|
+
Author.class_eval "make_permalink :with => :full_name"
|
31
|
+
author = Author.create(:first_name => "Bugs", :last_name => "Bunny")
|
32
|
+
assert_equal author.to_param, "bugs-bunny"
|
33
|
+
assert_equal author.permalink, author.to_param
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_permalink_should_not_create_forbidden_permalinks_given_one_string
|
37
|
+
Author.class_eval "make_permalink :with => :first_name, :forbidden => 'me'"
|
38
|
+
author = Author.create(:first_name => "me")
|
39
|
+
assert_not_equal author.to_param, "me"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_permalink_should_not_create_forbidden_permalinks_given_mulitple_strings
|
43
|
+
Author.class_eval "make_permalink :with => :first_name, :forbidden => %w{you me}"
|
44
|
+
author1 = Author.create(:first_name => "me")
|
45
|
+
author2 = Author.create(:first_name => "you")
|
46
|
+
|
47
|
+
assert_not_equal author1.to_param, "me"
|
48
|
+
assert_not_equal author2.to_param, "you"
|
49
|
+
assert_not_equal author1.to_param, author2.to_param
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_permalink_should_not_create_forbidden_permalinks_given_a_regexp
|
53
|
+
Author.class_eval 'make_permalink :with => :first_name, :forbidden => /\D$/'
|
54
|
+
author1 = Author.create(:first_name => "me")
|
55
|
+
author2 = Author.create(:first_name => "you")
|
56
|
+
|
57
|
+
assert_not_equal author1.to_param, "me"
|
58
|
+
assert_not_equal author2.to_param, "you"
|
59
|
+
assert_not_equal author1.to_param, author2.to_param
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_permalink_should_be_trunkated
|
63
|
+
Post.class_eval "make_permalink :with => :title"
|
64
|
+
post = Post.create(:title=>"thisoneisaveryveryveryveryveryveryverylonglonglonglongtitlethisoneisaveryveryveryveryveryveryverylonglonglonglongtitle")
|
65
|
+
assert_equal "thisoneisaveryveryveryveryveryveryverylonglonglong", post.to_param
|
66
|
+
assert_equal post.to_param.size, 50
|
67
|
+
assert_equal post.permalink, post.to_param
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_permalink_should_be_trunkated_to_custom_size
|
71
|
+
Post.class_eval "make_permalink :with => :title, :param_size=>10"
|
72
|
+
post = Post.create(:title=>"thisoneisaveryveryveryveryveryveryverylonglonglonglongtitlethisoneisaveryveryveryveryveryveryverylonglonglonglongtitle")
|
73
|
+
assert_equal "thisoneisa",post.to_param
|
74
|
+
assert_equal post.permalink, post.to_param
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_should_search_field_for_to_param_field
|
78
|
+
User.class_eval "make_permalink :with => :login"
|
79
|
+
user = User.create(:login=>"bumi")
|
80
|
+
assert_equal user, User.find_by_param("bumi")
|
81
|
+
assert_equal user, User.find_by_param!("bumi")
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_should_validate_presence_of_the_field_used_to_create_the_param
|
85
|
+
User.class_eval "make_permalink :with => :login"
|
86
|
+
user = User.create(:login=>nil)
|
87
|
+
assert_equal false, user.valid?
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_to_param_should_perpend_id
|
91
|
+
Article.class_eval "make_permalink :with => :title, :prepend_id=>true "
|
92
|
+
article = Article.create(:title=>"hey ho let's go!")
|
93
|
+
assert_equal article.to_param, "#{article.id}-hey-ho-let-s-go"
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_should_increment_counter_if_not_unique
|
97
|
+
Post.class_eval "make_permalink :with => :title"
|
98
|
+
Post.create(:title=>"my awesome title!")
|
99
|
+
post = Post.create(:title=>"my awesome title!")
|
100
|
+
assert_equal "my-awesome-title-1", post.to_param
|
101
|
+
assert_equal post.permalink, post.to_param
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_should_record_not_found_error
|
105
|
+
assert_raise(ActiveRecord::RecordNotFound) { Post.find_by_param!("isnothere") }
|
106
|
+
end
|
107
|
+
def test_should_return_nil_if_not_found
|
108
|
+
assert_equal nil, Post.find_by_param("isnothere")
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_should_strip_special_chars
|
112
|
+
assert_equal "+-he-l-l-o-ni-ce-duaode", Post.new.send(:escape_permalink, "+*(he/=&l$l<o !ni^?ce-`duäöde;:@")
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_does_not_leak_options
|
116
|
+
Post.class_eval "make_permalink :with => :title, :forbidden => 'foo'"
|
117
|
+
assert_equal( {:param => "permalink",
|
118
|
+
:param_size => 50,
|
119
|
+
:field => "permalink",
|
120
|
+
:with => :title,
|
121
|
+
:prepend_id => false,
|
122
|
+
:escape => true,
|
123
|
+
:validate => true,
|
124
|
+
:forbidden_strings => ["foo"]}, Post.permalink_options)
|
125
|
+
|
126
|
+
User.class_eval "make_permalink :with => :login, :forbidden => %w{foo bar}"
|
127
|
+
assert_equal( {:param => :login,
|
128
|
+
:param_size => 50,
|
129
|
+
:field => "permalink",
|
130
|
+
:with => :login,
|
131
|
+
:prepend_id => false,
|
132
|
+
:escape => true,
|
133
|
+
:validate => true,
|
134
|
+
:forbidden_strings => ["foo", "bar"]}, User.permalink_options)
|
135
|
+
|
136
|
+
Article.class_eval "make_permalink :with => :title, :prepend_id => true, :forbidden => /baz$/"
|
137
|
+
assert_equal( {:param => :title,
|
138
|
+
:param_size => 50,
|
139
|
+
:field => "permalink",
|
140
|
+
:with => :title,
|
141
|
+
:prepend_id => true,
|
142
|
+
:escape => true,
|
143
|
+
:validate => true,
|
144
|
+
:forbidden_match => /baz$/}, Article.permalink_options)
|
145
|
+
|
146
|
+
Author.class_eval "make_permalink :with => :full_name, :validate => false"
|
147
|
+
assert_equal( {:param => "permalink",
|
148
|
+
:param_size => 50,
|
149
|
+
:field => "permalink",
|
150
|
+
:with => :full_name,
|
151
|
+
:prepend_id => false,
|
152
|
+
:escape => true,
|
153
|
+
:validate => false}, Author.permalink_options)
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
|
3
|
+
create_table :posts, :force => true do |t|
|
4
|
+
t.string :title
|
5
|
+
t.string :permalink
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :articles, :force => true do |t|
|
9
|
+
t.string :title
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :users, :force => true do |t|
|
13
|
+
t.string :login
|
14
|
+
end
|
15
|
+
|
16
|
+
create_table :authors, :force => true do |t|
|
17
|
+
t.string :first_name
|
18
|
+
t.string :last_name
|
19
|
+
t.string :permalink
|
20
|
+
end
|
21
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support'
|
5
|
+
#require 'active_support/multibyte'
|
6
|
+
require 'find_by_param'
|
7
|
+
class ActiveRecord::Base
|
8
|
+
class_inheritable_accessor :permalink_options
|
9
|
+
self.permalink_options = {:param => :id}
|
10
|
+
end
|
11
|
+
ActiveRecord::Base.send(:include, Railslove::Plugins::FindByParam)
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection({
|
14
|
+
'adapter' => 'sqlite3',
|
15
|
+
'database' => ':memory:'
|
16
|
+
})
|
17
|
+
load(File.join(File.dirname(__FILE__), 'schema.rb'))
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
data/version.yml
ADDED
metadata
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: find_by_param
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Bumann
|
8
|
+
- Gregor Schmidt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-01-03 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: activerecord
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "2.3"
|
25
|
+
version:
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activesupport
|
28
|
+
type: :runtime
|
29
|
+
version_requirement:
|
30
|
+
version_requirements: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "2.3"
|
35
|
+
version:
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: sqlite3-ruby
|
38
|
+
type: :development
|
39
|
+
version_requirement:
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: jeweler
|
48
|
+
type: :development
|
49
|
+
version_requirement:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.11.0
|
55
|
+
version:
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
type: :development
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
version:
|
66
|
+
description: find_by_param is a nice and easy way to handle permalinks and dealing with searching for to_param values
|
67
|
+
email: michael@railslove.com
|
68
|
+
executables: []
|
69
|
+
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files:
|
73
|
+
- README.md
|
74
|
+
files:
|
75
|
+
- .gitignore
|
76
|
+
- MIT-LICENSE
|
77
|
+
- README.md
|
78
|
+
- Rakefile
|
79
|
+
- init.rb
|
80
|
+
- install.rb
|
81
|
+
- lib/find_by_param.rb
|
82
|
+
- shoulda_macros/find_by_param.rb
|
83
|
+
- shoulda_macros/matchers.rb
|
84
|
+
- tasks/find_by_param_tasks.rake
|
85
|
+
- test/find_by_param_test.rb
|
86
|
+
- test/schema.rb
|
87
|
+
- test/test_helper.rb
|
88
|
+
- uninstall.rb
|
89
|
+
- version.yml
|
90
|
+
has_rdoc: true
|
91
|
+
homepage: http://github.com/bumi/find_by_param
|
92
|
+
licenses: []
|
93
|
+
|
94
|
+
post_install_message:
|
95
|
+
rdoc_options:
|
96
|
+
- --charset=UTF-8
|
97
|
+
require_paths:
|
98
|
+
- lib
|
99
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: "0"
|
110
|
+
version:
|
111
|
+
requirements: []
|
112
|
+
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.3.5
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Rails plugin to handle permalink values
|
118
|
+
test_files:
|
119
|
+
- test/find_by_param_test.rb
|
120
|
+
- test/schema.rb
|
121
|
+
- test/test_helper.rb
|