rd_find_by_param 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,208 @@
1
+ begin
2
+ $KCODE = 'u'
3
+ require 'rubygems'
4
+ require 'active_support'
5
+ rescue LoadError
6
+ end
7
+
8
+ module Railslove
9
+ module Plugins
10
+ module FindByParam
11
+ def self.enable # :nodoc:
12
+ return if ActiveRecord::Base.kind_of?(self::ClassMethods)
13
+
14
+ ActiveRecord::Base.class_eval do
15
+ class_inheritable_accessor :permalink_options
16
+ self.permalink_options = {:param => :id}
17
+
18
+ #default finders these are overwritten if you use make_permalink in
19
+ # your model
20
+ def self.find_by_param(value,args={}) # :nodoc:
21
+ find_by_id(value,args)
22
+ end
23
+ def self.find_by_param!(value,args={}) # :nodoc:
24
+ find(value,args)
25
+ end
26
+ end
27
+ ActiveRecord::Base.extend(self::ClassMethods)
28
+ end
29
+
30
+ module ClassMethods
31
+
32
+
33
+ =begin rdoc
34
+
35
+ This method initializes find_by_param
36
+
37
+ class Post < ActiveRecord::Base
38
+ make_permalink :with => :title, :prepend_id => true
39
+ end
40
+
41
+ The only required parameter, is <tt>:with</tt>.
42
+
43
+ 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.
44
+
45
+ If your you can just say make_permalink :with => :login and you're done.
46
+
47
+ You can use for example User.find_by_param(params[:id], args) to find the user by the defined permalink.
48
+
49
+ == Available options
50
+
51
+ <tt>:with</tt>:: (required) The attribute that should be used as permalink
52
+ <tt>:field</tt>:: The name of your permalink column. make_permalink first checks if there is a column, default is 'permalink'.
53
+ <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, default is false.
54
+ <tt>:param_size</tt>:: [Number] Desired maximum size of the permalink, default is 50.
55
+ <tt>:escape</tt>:: [true|false] Do you want to escape the permalink value? (strip chars like öä?&) - actually you must do that, default is true.
56
+ <tt>:validate</tt>:: [true|false] Don't validate the :with field - set this to false if you validate it on your own, default is true.
57
+ <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>
58
+ =end
59
+ def make_permalink(options={})
60
+ options[:field] ||= "permalink"
61
+ 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
62
+ options[:escape] ||= true
63
+ options[:prepend_id] ||= false
64
+ options[:param_size] ||= 50
65
+ options[:validate] = true if options[:validate].nil?
66
+
67
+ # 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.
68
+ if !options[:prepend_id] || !options[:validate]
69
+ validate :validate_param_is_not_blank
70
+ end
71
+
72
+ if forbidden = options.delete(:forbidden)
73
+ if forbidden.is_a? Regexp
74
+ options[:forbidden_match] = forbidden
75
+ else
76
+ options[:forbidden_strings] = Array(forbidden).map(&:to_s)
77
+ end
78
+ end
79
+
80
+ if self.column_names.include?(options[:field].to_s)
81
+ options[:param] = options[:field]
82
+ before_save :save_permalink
83
+ end
84
+
85
+ self.permalink_options = options
86
+ extend Railslove::Plugins::FindByParam::SingletonMethods
87
+ include Railslove::Plugins::FindByParam::InstanceMethods
88
+ rescue ActiveRecord::ActiveRecordError
89
+ puts "[find_by_param error] database not available?"
90
+ end
91
+ end
92
+
93
+ module SingletonMethods
94
+
95
+ =begin rdoc
96
+
97
+ Search for an object by the defined permalink column. Similar to
98
+ +find_by_login+. Returns +nil+ if nothing is found. Accepts an options hash as
99
+ second parameter which is passed on to the rails finder.
100
+ =end
101
+ def find_by_param(value, args={})
102
+ if permalink_options[:prepend_id]
103
+ param = "id"
104
+ value = value.to_i
105
+ else
106
+ param = permalink_options[:param]
107
+ end
108
+ self.send("find_by_#{param}".to_sym, value, args)
109
+ end
110
+
111
+ =begin rdoc
112
+
113
+ Like +find_by_param+ but raises an <tt>ActiveRecord::RecordNotFound</tt> error
114
+ if nothing is found - similar to find().
115
+
116
+ Accepts an options hash as second parameter which is passed on to the rails
117
+ finder.
118
+ =end
119
+ def find_by_param!(value, args={})
120
+ param = permalink_options[:param]
121
+ obj = find_by_param(value, args)
122
+ raise ::ActiveRecord::RecordNotFound unless obj
123
+ obj
124
+ end
125
+ end
126
+
127
+ module InstanceMethods
128
+ def to_param
129
+ value = self.send(permalink_options[:param]).dup.to_s.downcase rescue ""
130
+ ''.tap do |param|
131
+ if value.blank?
132
+ param << id.to_s
133
+ else
134
+ param << "#{id}-" if permalink_options[:prepend_id]
135
+ param << escape_and_truncate_permalink(value)
136
+ end
137
+ end
138
+ end
139
+
140
+ protected
141
+
142
+ def save_permalink
143
+ return unless self.class.column_names.include?(permalink_options[:field].to_s)
144
+ counter = 0
145
+ base_value = escape_and_truncate_permalink(send(permalink_options[:with])).downcase
146
+ permalink_value = base_value.to_s
147
+
148
+ conditions = ["#{self.class.table_name}.#{permalink_options[:field]} = ?", permalink_value]
149
+ unless new_record?
150
+ conditions.first << " and #{self.class.table_name}.#{self.class.primary_key} != ?"
151
+ conditions << self.send(self.class.primary_key.to_sym)
152
+ end
153
+ while is_forbidden?(permalink_value) or
154
+ self.class.count(:all, :conditions => conditions) > 0
155
+ counter += 1
156
+ permalink_value = "#{base_value}-#{counter}"
157
+
158
+ if permalink_value.size > permalink_options[:param_size]
159
+ length = permalink_options[:param_size] - counter.to_s.size - 2
160
+ truncated_base = base_value[0..length]
161
+ permalink_value = "#{truncated_base}-#{counter}"
162
+ end
163
+
164
+ conditions[1] = permalink_value
165
+ end
166
+ write_attribute(permalink_options[:field], permalink_value)
167
+ true
168
+ end
169
+
170
+ def validate_param_is_not_blank
171
+ if self.escape_and_truncate_permalink(self.send(permalink_options[:with])).blank?
172
+ errors.add(permalink_options[:with],
173
+ "must have at least one non special character (a-z 0-9)")
174
+ end
175
+ end
176
+
177
+ def escape_permalink(value)
178
+ value.to_s.parameterize
179
+ end
180
+
181
+ def is_forbidden?(permalink_value)
182
+ is_forbidden_string?(permalink_value) ||
183
+ matches_forbidden_regexp?(permalink_value)
184
+ end
185
+
186
+ def is_forbidden_string?(permalink_value)
187
+ permalink_options[:forbidden_strings] &&
188
+ permalink_options[:forbidden_strings].include?(permalink_value)
189
+ end
190
+
191
+ def matches_forbidden_regexp?(permalink_value)
192
+ permalink_options[:forbidden_match] &&
193
+ permalink_options[:forbidden_match] =~ permalink_value
194
+ end
195
+
196
+ def escape_and_truncate_permalink(value)
197
+ p = self.escape_permalink(value)[0...self.permalink_options[:param_size]]
198
+ p.ends_with?('-') ? p.chop : p
199
+ end
200
+ end
201
+
202
+ end
203
+ end
204
+ end
205
+
206
+ if defined?(ActiveRecord)
207
+ Railslove::Plugins::FindByParam.enable
208
+ end
@@ -0,0 +1,7 @@
1
+ module Railslove
2
+ module Plugins
3
+ module FindByParam
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rd_find_by_param
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Michael Bumann
14
+ - Gregor Schmidt
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2011-03-16 00:00:00 -04:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: activerecord
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 7
31
+ segments:
32
+ - 3
33
+ - 0
34
+ version: "3.0"
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: activesupport
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 3
48
+ - 0
49
+ version: "3.0"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: sqlite3-ruby
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: rake
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ type: :development
79
+ version_requirements: *id004
80
+ description: find_by_param is a nice and easy way to handle permalinks and dealing with searching for to_param values
81
+ email: sean@railsdog.com
82
+ executables: []
83
+
84
+ extensions: []
85
+
86
+ extra_rdoc_files: []
87
+
88
+ files:
89
+ - lib/find_by_param/version.rb
90
+ - lib/find_by_param.rb
91
+ has_rdoc: true
92
+ homepage: http://github.com/railsdog/find_by_param
93
+ licenses: []
94
+
95
+ post_install_message:
96
+ rdoc_options: []
97
+
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ hash: 3
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ requirements: []
119
+
120
+ rubyforge_project: "[none]"
121
+ rubygems_version: 1.3.7
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: Rails plugin to handle permalink values
125
+ test_files: []
126
+