rd_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.
@@ -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
+