glue 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +161 -110
  2. data/INSTALL +12 -12
  3. data/README +1 -1
  4. data/Rakefile +43 -45
  5. data/doc/AUTHORS +5 -5
  6. data/doc/LICENSE +3 -3
  7. data/doc/RELEASES +32 -24
  8. data/install.rb +7 -17
  9. data/lib/facet/object/alias_class.rb +12 -0
  10. data/lib/glue.rb +35 -35
  11. data/lib/glue/array.rb +46 -46
  12. data/lib/glue/aspects.rb +199 -209
  13. data/lib/glue/attribute.rb +15 -15
  14. data/lib/glue/autoreload.rb +1 -1
  15. data/lib/glue/builder.rb +48 -0
  16. data/lib/glue/builder/xml.rb +114 -0
  17. data/lib/glue/cache.rb +189 -0
  18. data/lib/glue/configuration.rb +108 -90
  19. data/lib/glue/flexob.rb +17 -17
  20. data/lib/glue/hash.rb +71 -71
  21. data/lib/glue/helper.rb +12 -12
  22. data/lib/glue/idgen.rb +9 -0
  23. data/lib/glue/idgen/md5.rb +24 -0
  24. data/lib/glue/idgen/sequential.rb +15 -0
  25. data/lib/glue/literal_method.rb +44 -0
  26. data/lib/glue/localization.rb +130 -0
  27. data/lib/glue/logger.rb +98 -98
  28. data/lib/glue/misc.rb +7 -7
  29. data/lib/glue/mixins.rb +19 -19
  30. data/lib/glue/number.rb +8 -8
  31. data/lib/glue/object.rb +2 -2
  32. data/lib/glue/pool.rb +43 -43
  33. data/lib/glue/property.rb +392 -392
  34. data/lib/glue/sanitize.rb +34 -34
  35. data/lib/glue/settings.rb +1 -1
  36. data/lib/glue/snapshot.rb +104 -0
  37. data/lib/glue/string.rb +129 -129
  38. data/lib/glue/time.rb +53 -53
  39. data/lib/glue/uri.rb +162 -162
  40. data/lib/glue/validation.rb +421 -421
  41. data/lib/vendor/blankslate.rb +53 -0
  42. data/test/glue/builder/tc_xml.rb +56 -0
  43. data/test/glue/tc_aspects.rb +90 -90
  44. data/test/glue/tc_attribute.rb +11 -11
  45. data/test/glue/tc_builder.rb +30 -0
  46. data/test/glue/tc_configuration.rb +97 -97
  47. data/test/glue/tc_flexob.rb +10 -10
  48. data/test/glue/tc_hash.rb +23 -23
  49. data/test/glue/tc_localization.rb +49 -0
  50. data/test/glue/tc_logger.rb +31 -31
  51. data/test/glue/tc_numbers.rb +9 -9
  52. data/test/glue/tc_property.rb +67 -67
  53. data/test/glue/tc_property_mixins.rb +17 -17
  54. data/test/glue/tc_property_type_checking.rb +13 -13
  55. data/test/glue/tc_strings.rb +94 -94
  56. data/test/glue/tc_uri.rb +65 -65
  57. data/test/glue/tc_validation.rb +196 -196
  58. metadata +26 -4
@@ -1,6 +1,6 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: validation.rb 1 2005-04-11 11:04:30Z gmosx $
3
+ # $Id: validation.rb 182 2005-07-22 10:07:50Z gmosx $
4
4
 
5
5
  module Glue
6
6
 
@@ -10,11 +10,11 @@ module Glue
10
10
  #
11
11
  # The following validation macros are available:
12
12
  #
13
- # * validate_value
14
- # * validate_confirmation
15
- # * validate_format
16
- # * validate_length
17
- # * validate_inclusion
13
+ # * validate_value
14
+ # * validate_confirmation
15
+ # * validate_format
16
+ # * validate_length
17
+ # * validate_inclusion
18
18
  #
19
19
  # Og/Database specific validation methods are added in the
20
20
  # file og/validation.rb
@@ -22,37 +22,37 @@ module Glue
22
22
  # === Example
23
23
  #
24
24
  # class User
25
- # prop_accessor :name, String
26
- # prop_accessor :level, Fixnum
25
+ # prop_accessor :name, String
26
+ # prop_accessor :level, Fixnum
27
27
  #
28
- # validate_length :name, :range => 2..6
29
- # validate_unique :name, :msg => :name_allready_exists
30
- # validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
31
- # end
28
+ # validate_length :name, :range => 2..6
29
+ # validate_unique :name, :msg => :name_allready_exists
30
+ # validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
31
+ # end
32
32
  #
33
- # class CustomUserValidator
34
- # include Validation
35
- # validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
36
- # end
33
+ # class CustomUserValidator
34
+ # include Validation
35
+ # validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
36
+ # end
37
37
  #
38
- # user = @request.fill(User.new)
39
- # user.level = 15
38
+ # user = @request.fill(User.new)
39
+ # user.level = 15
40
40
  #
41
- # unless user.valid?
42
- # user.save
43
- # else
44
- # p user.errors[:name]
45
- # end
41
+ # unless user.valid?
42
+ # user.save
43
+ # else
44
+ # p user.errors[:name]
45
+ # end
46
46
  #
47
- # unless user.save
48
- # p user.errors.on(:name)
49
- # end
47
+ # unless user.save
48
+ # p user.errors.on(:name)
49
+ # end
50
50
  #
51
- # unless errors = CustomUserValidator.errors(user)
52
- # user.save
53
- # else
54
- # p errors[:name]
55
- # end
51
+ # unless errors = CustomUserValidator.errors(user)
52
+ # user.save
53
+ # else
54
+ # p errors[:name]
55
+ # end
56
56
  #
57
57
  #--
58
58
  # TODO: all validation methods should imply validate_value
@@ -61,401 +61,401 @@ module Glue
61
61
 
62
62
  module Validation
63
63
 
64
- # Encapsulates a list of validation errors.
65
-
66
- class Errors
67
- attr_accessor :errors
68
-
69
- cattr_accessor :no_value, 'No value provided'
70
- cattr_accessor :no_confirmation, 'Invalid confirmation'
71
- cattr_accessor :invalid_format, 'Invalid format'
72
- cattr_accessor :too_short, 'Too short, must be more than %d characters long'
73
- cattr_accessor :too_long, 'Too long, must be less than %d characters long'
74
- cattr_accessor :invalid_length, 'Must be %d characters long'
75
- cattr_accessor :no_inclusion, 'The value is invalid'
76
- cattr_accessor :no_numeric, 'The value must be numeric'
77
- cattr_accessor :not_unique, 'The value is already used'
78
-
79
- def initialize
80
- @errors = {}
81
- end
82
-
83
- def add(attr, message)
84
- (@errors[attr] ||= []) << message
85
- end
86
-
87
- def on(attr)
88
- @errors[attr]
89
- end
90
- alias_method :[], :on
91
-
92
- # Yields each attribute and associated message.
93
-
94
- def each
95
- @errors.each_key do |attr|
96
- @errors[attr].each { |msg| yield attr, msg }
97
- end
98
- end
99
-
100
- def size
101
- @errors.size
102
- end
103
- alias_method :count, :size
104
-
105
- def empty?
106
- @errors.empty?
107
- end
108
-
109
- def clear
110
- @errors.clear
111
- end
112
- end
113
-
114
- # If the validate method returns true, this
115
- # attributes holds the errors found.
116
-
117
- attr_accessor :errors
118
-
119
- # Call the #validate method for this object.
120
- # If validation errors are found, sets the
121
- # @errors attribute to the Errors object and
122
- # returns true.
123
-
124
- def valid?
125
- begin
126
- @errors = self.class.validate(self)
127
- return @errors.empty?
128
- rescue NoMethodError => e
129
- # gmosx: hmm this is potentially dangerous.
130
- Glue::Validation.eval_validate(self.class)
131
- retry
132
- end
133
- end
134
-
135
- # Evaluate the 'validate' method for the calling
136
- # class.
137
- #
138
- # WARNING: for the moment only evaluates for
139
- # on == :save
140
-
141
- def self.eval_validate(klass)
142
- code = %{
143
- def self.validate(obj, on = :save)
144
- errors = Errors.new
145
- }
146
-
147
- for val, on in klass.__meta[:validations]
148
- code << %{
149
- #{val}
150
- }
151
- end
152
-
153
- code << %{
154
- return errors
155
- end
156
- }
157
-
158
- klass.module_eval(code)
159
- end
160
-
161
- def self.append_features(base)
162
- super
163
-
164
- base.module_eval <<-"end_eval", __FILE__, __LINE__
165
- meta :validations, []
166
- end_eval
167
-
168
- base.extend(MetaLanguage)
169
- end
170
-
171
- # Implements the Validation meta-language.
172
-
173
- module MetaLanguage
174
-
175
- # the postfix attached to confirmation attributes.
176
-
177
- mattr_accessor :confirmation_postfix, '_confirmation'
178
-
179
- # Validates that the attributes have a values, ie they are
180
- # neither nil or empty.
181
- #
182
- # === Example
183
- #
184
- # validate_value :param, :msg => 'No confirmation'
185
-
186
- def validate_value(*params)
187
- c = {
188
- :msg => Glue::Validation::Errors.no_value,
189
- :on => :save
190
- }
191
- c.update(params.pop) if params.last.is_a?(Hash)
192
-
193
- idx = 0
194
- for name in params
195
- code = %{
196
- if obj.#{name}.nil?
197
- errors.add(:#{name}, '#{c[:msg]}')
198
- elsif obj.#{name}.respond_to?(:empty?)
199
- errors.add(:#{name}, '#{c[:msg]}') if obj.#{name}.empty?
200
- end
201
- }
202
-
203
- __meta[:validations] << [code, c[:on]]
204
- end
205
- end
206
-
207
- # Validates the confirmation of +String+ attributes.
208
- #
209
- # === Example
210
- #
211
- # validate_confirmation :password, :msg => 'No confirmation'
212
-
213
- def validate_confirmation(*params)
214
- c = {
215
- :msg => Glue::Validation::Errors.no_confirmation,
216
- :postfix => Glue::Validation::MetaLanguage.confirmation_postfix,
217
- :on => :save
218
- }
219
- c.update(params.pop) if params.last.is_a?(Hash)
220
-
221
-
222
- for name in params
223
- confirm_name = "#{name}#{c[:postfix]}"
224
- eval "attr_accessor :#{confirm_name}"
225
-
226
- code = %{
227
- if obj.#{confirm_name}.nil? or (obj.#{confirm_name} != obj.#{name})
228
- errors.add(:#{name}, '#{c[:msg]}')
229
- end
230
- }
231
-
232
- __meta[:validations] << [code, c[:on]]
233
- end
234
- end
235
-
236
- # Validates the format of +String+ attributes.
237
- # WARNING: regexp options are ignored.
238
- #
239
- # === Example
240
- #
241
- # validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
242
- #
243
- #--
244
- # FIXME: how to get the Regexp options?
245
- #++
246
-
247
- def validate_format(*params)
248
- c = {
249
- :format => nil,
250
- :msg_no_value => Glue::Validation::Errors.no_value,
251
- :msg => Glue::Validation::Errors.invalid_format,
252
- :on => :save
253
- }
254
- c.update(params.pop) if params.last.is_a?(Hash)
255
-
256
- unless c[:format].is_a?(Regexp)
257
- raise(ArgumentError,
258
- 'A regular expression must be supplied as the :format option')
259
- end
260
-
261
- for name in params
262
- code = %{
263
- if obj.#{name}.nil?
264
- errors.add(:#{name}, '#{c[:msg_no_value]}')
265
- else
266
- unless obj.#{name}.to_s.match(/#{c[:format].source}/)
267
- errors.add(:#{name}, '#{c[:msg]}')
268
- end
269
- end;
270
- }
271
-
272
- __meta[:validations] << [code, c[:on]]
273
- end
274
- end
275
-
276
- # Validates the length of +String+ attributes.
277
- #
278
- # === Example
279
- #
280
- # validate_length :name, :max => 30, :msg => 'Too long'
281
- # validate_length :name, :min => 2, :msg => 'Too sort'
282
- # validate_length :name, :range => 2..30
283
- # validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
284
-
285
- def validate_length(*params)
286
- c = {
287
- :min => nil, :max => nil, :range => nil, :length => nil,
288
- :msg => nil,
289
- :msg_no_value => Glue::Validation::Errors.no_value,
290
- :msg_short => Glue::Validation::Errors.too_short,
291
- :msg_long => Glue::Validation::Errors.too_long,
292
- :on => :save
293
- }
294
- c.update(params.pop) if params.last.is_a?(Hash)
295
-
296
- min, max = c[:min], c[:max]
297
- range, length = c[:range], c[:length]
298
-
299
- count = 0
300
- [min, max, range, length].each { |o| count += 1 if o }
301
-
302
- if count == 0
303
- raise(ArgumentError,
304
- 'One of :min, :max, :range, :length must be provided!')
305
- end
306
-
307
- if count > 1
308
- raise(ArgumentError,
309
- 'The :min, :max, :range, :length options are mutually exclusive!')
310
- end
311
-
312
- for name in params
313
- if min
314
- c[:msg] ||= Glue::Validation::Errors.too_short
315
- code = %{
316
- if obj.#{name}.nil?
317
- errors.add(:#{name}, '#{c[:msg_no_value]}')
318
- elsif obj.#{name}.to_s.length < #{min}
319
- msg = '#{c[:msg]}'
320
- msg = (msg % #{min}) rescue msg
321
- errors.add(:#{name}, msg)
322
- end;
323
- }
324
- elsif max
325
- c[:msg] ||= Glue::Validation::Errors.too_long
326
- code = %{
327
- if obj.#{name}.nil?
328
- errors.add(:#{name}, '#{c[:msg_no_value]}')
329
- elsif obj.#{name}.to_s.length > #{max}
330
- msg = '#{c[:msg]}'
331
- msg = (msg % #{max}) rescue msg
332
- errors.add(:#{name}, msg)
333
- end;
334
- }
335
- elsif range
336
- code = %{
337
- if obj.#{name}.nil?
338
- errors.add(:#{name}, '#{c[:msg_no_value]}')
339
- elsif obj.#{name}.to_s.length < #{range.first}
340
- msg = '#{c[:msg_short]}'
341
- msg = (msg % #{range.first}) rescue msg
342
- errors.add(:#{name}, msg)
343
- elsif obj.#{name}.to_s.length > #{range.last}
344
- msg = '#{c[:msg_long]}'
345
- msg = (msg % #{range.last}) rescue msg
346
- errors.add(:#{name}, msg)
347
- end;
348
- }
349
- elsif length
350
- c[:msg] ||= Glue::Validation::Errors.invalid_length
351
- code = %{
352
- if obj.#{name}.nil?
353
- errors.add(:#{name}, '#{c[:msg_no_value]}')
354
- elsif obj.#{name}.to_s.length != #{length}
355
- msg = '#{c[:msg]}'
356
- msg = (msg % #{length}) rescue msg
357
- errors.add(:#{name}, msg)
358
- end;
359
- }
360
- end
361
-
362
- __meta[:validations] << [code, c[:on]]
363
- end
364
- end
365
-
366
- # Validates that the attributes are included in
367
- # an enumeration.
368
- #
369
- # === Example
370
- #
371
- # validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
372
- # validate_inclusion :age, :in => 5..99
373
-
374
- def validate_inclusion(*params)
375
- c = {
376
- :in => nil,
377
- :msg => Glue::Validation::Errors.no_inclusion,
378
- :allow_nil => false,
379
- :on => :save
380
- }
381
- c.update(params.pop) if params.last.is_a?(Hash)
382
-
383
- unless c[:in].respond_to?('include?')
384
- raise(ArgumentError,
385
- 'An object that responds to #include? must be supplied as the :in option')
386
- end
387
-
388
- for name in params
389
- if c[:allow_nil]
390
- code = %{
391
- unless obj.#{name}.nil? or (#{c[:in].inspect}).include?(obj.#{name})
392
- errors.add(:#{name}, '#{c[:msg]}')
393
- end;
394
- }
395
- else
396
- code = %{
397
- unless (#{c[:in].inspect}).include?(obj.#{name})
398
- errors.add(:#{name}, '#{c[:msg]}')
399
- end;
400
- }
401
- end
402
-
403
- __meta[:validations] << [code, c[:on]]
404
- end
405
- end
406
-
407
- # Validates that the attributes have numeric values.
408
- #
409
- # [+:integer+]
410
- # Only accept integers
411
- #
412
- # [+:msg]
413
- # Custom message
414
- #
415
- # [+:on:]
416
- # When to validate
417
- #
418
- # === Example
419
- #
420
- # validate_numeric :age, :msg => 'Must be an integer'
421
-
422
- def validate_numeric(*params)
423
- c = {
424
- :integer => false,
425
- :msg => Glue::Validation::Errors.no_numeric,
426
- :on => :save
427
- }
428
- c.update(params.pop) if params.last.is_a?(Hash)
429
-
430
- for name in params
431
- if c[:integer]
432
- code = %{
433
- unless obj.#{name}.to_s =~ /^[\\+\\-]?\\d+$/
434
- errors.add(:#{name}, '#{c[:msg]}')
435
- end;
436
- }
437
- else
438
- code = %{
439
- begin
440
- Kernel.Float(obj.#{name})
441
- rescue ArgumentError, TypeError
442
- errors.add(:#{name}, '#{c[:msg]}')
443
- end;
444
- }
445
- end
446
-
447
- __meta[:validations] << [code, c[:on]]
448
- end
449
- end
450
- alias_method :validate_numericality, :validate_numeric
451
-
452
- end
64
+ # Encapsulates a list of validation errors.
65
+
66
+ class Errors
67
+ attr_accessor :errors
68
+
69
+ cattr_accessor :no_value, 'No value provided'
70
+ cattr_accessor :no_confirmation, 'Invalid confirmation'
71
+ cattr_accessor :invalid_format, 'Invalid format'
72
+ cattr_accessor :too_short, 'Too short, must be more than %d characters long'
73
+ cattr_accessor :too_long, 'Too long, must be less than %d characters long'
74
+ cattr_accessor :invalid_length, 'Must be %d characters long'
75
+ cattr_accessor :no_inclusion, 'The value is invalid'
76
+ cattr_accessor :no_numeric, 'The value must be numeric'
77
+ cattr_accessor :not_unique, 'The value is already used'
78
+
79
+ def initialize
80
+ @errors = {}
81
+ end
82
+
83
+ def add(attr, message)
84
+ (@errors[attr] ||= []) << message
85
+ end
86
+
87
+ def on(attr)
88
+ @errors[attr]
89
+ end
90
+ alias_method :[], :on
91
+
92
+ # Yields each attribute and associated message.
93
+
94
+ def each
95
+ @errors.each_key do |attr|
96
+ @errors[attr].each { |msg| yield attr, msg }
97
+ end
98
+ end
99
+
100
+ def size
101
+ @errors.size
102
+ end
103
+ alias_method :count, :size
104
+
105
+ def empty?
106
+ @errors.empty?
107
+ end
108
+
109
+ def clear
110
+ @errors.clear
111
+ end
112
+ end
113
+
114
+ # If the validate method returns true, this
115
+ # attributes holds the errors found.
116
+
117
+ attr_accessor :errors
118
+
119
+ # Call the #validate method for this object.
120
+ # If validation errors are found, sets the
121
+ # @errors attribute to the Errors object and
122
+ # returns true.
123
+
124
+ def valid?
125
+ begin
126
+ @errors = self.class.validate(self)
127
+ return @errors.empty?
128
+ rescue NoMethodError => e
129
+ # gmosx: hmm this is potentially dangerous.
130
+ Glue::Validation.eval_validate(self.class)
131
+ retry
132
+ end
133
+ end
134
+
135
+ # Evaluate the 'validate' method for the calling
136
+ # class.
137
+ #
138
+ # WARNING: for the moment only evaluates for
139
+ # on == :save
140
+
141
+ def self.eval_validate(klass)
142
+ code = %{
143
+ def self.validate(obj, on = :save)
144
+ errors = Errors.new
145
+ }
146
+
147
+ for val, on in klass.__meta[:validations]
148
+ code << %{
149
+ #{val}
150
+ }
151
+ end
152
+
153
+ code << %{
154
+ return errors
155
+ end
156
+ }
157
+
158
+ klass.module_eval(code)
159
+ end
160
+
161
+ def self.append_features(base)
162
+ super
163
+
164
+ base.module_eval <<-"end_eval", __FILE__, __LINE__
165
+ meta :validations, []
166
+ end_eval
167
+
168
+ base.extend(MetaLanguage)
169
+ end
170
+
171
+ # Implements the Validation meta-language.
172
+
173
+ module MetaLanguage
174
+
175
+ # the postfix attached to confirmation attributes.
176
+
177
+ mattr_accessor :confirmation_postfix, '_confirmation'
178
+
179
+ # Validates that the attributes have a values, ie they are
180
+ # neither nil or empty.
181
+ #
182
+ # === Example
183
+ #
184
+ # validate_value :param, :msg => 'No confirmation'
185
+
186
+ def validate_value(*params)
187
+ c = {
188
+ :msg => Glue::Validation::Errors.no_value,
189
+ :on => :save
190
+ }
191
+ c.update(params.pop) if params.last.is_a?(Hash)
192
+
193
+ idx = 0
194
+ for name in params
195
+ code = %{
196
+ if obj.#{name}.nil?
197
+ errors.add(:#{name}, '#{c[:msg]}')
198
+ elsif obj.#{name}.respond_to?(:empty?)
199
+ errors.add(:#{name}, '#{c[:msg]}') if obj.#{name}.empty?
200
+ end
201
+ }
202
+
203
+ __meta[:validations] << [code, c[:on]]
204
+ end
205
+ end
206
+
207
+ # Validates the confirmation of +String+ attributes.
208
+ #
209
+ # === Example
210
+ #
211
+ # validate_confirmation :password, :msg => 'No confirmation'
212
+
213
+ def validate_confirmation(*params)
214
+ c = {
215
+ :msg => Glue::Validation::Errors.no_confirmation,
216
+ :postfix => Glue::Validation::MetaLanguage.confirmation_postfix,
217
+ :on => :save
218
+ }
219
+ c.update(params.pop) if params.last.is_a?(Hash)
220
+
221
+
222
+ for name in params
223
+ confirm_name = "#{name}#{c[:postfix]}"
224
+ eval "attr_accessor :#{confirm_name}"
225
+
226
+ code = %{
227
+ if obj.#{confirm_name}.nil? or (obj.#{confirm_name} != obj.#{name})
228
+ errors.add(:#{name}, '#{c[:msg]}')
229
+ end
230
+ }
231
+
232
+ __meta[:validations] << [code, c[:on]]
233
+ end
234
+ end
235
+
236
+ # Validates the format of +String+ attributes.
237
+ # WARNING: regexp options are ignored.
238
+ #
239
+ # === Example
240
+ #
241
+ # validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
242
+ #
243
+ #--
244
+ # FIXME: how to get the Regexp options?
245
+ #++
246
+
247
+ def validate_format(*params)
248
+ c = {
249
+ :format => nil,
250
+ :msg_no_value => Glue::Validation::Errors.no_value,
251
+ :msg => Glue::Validation::Errors.invalid_format,
252
+ :on => :save
253
+ }
254
+ c.update(params.pop) if params.last.is_a?(Hash)
255
+
256
+ unless c[:format].is_a?(Regexp)
257
+ raise(ArgumentError,
258
+ 'A regular expression must be supplied as the :format option')
259
+ end
260
+
261
+ for name in params
262
+ code = %{
263
+ if obj.#{name}.nil?
264
+ errors.add(:#{name}, '#{c[:msg_no_value]}')
265
+ else
266
+ unless obj.#{name}.to_s.match(/#{c[:format].source}/)
267
+ errors.add(:#{name}, '#{c[:msg]}')
268
+ end
269
+ end;
270
+ }
271
+
272
+ __meta[:validations] << [code, c[:on]]
273
+ end
274
+ end
275
+
276
+ # Validates the length of +String+ attributes.
277
+ #
278
+ # === Example
279
+ #
280
+ # validate_length :name, :max => 30, :msg => 'Too long'
281
+ # validate_length :name, :min => 2, :msg => 'Too sort'
282
+ # validate_length :name, :range => 2..30
283
+ # validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
284
+
285
+ def validate_length(*params)
286
+ c = {
287
+ :min => nil, :max => nil, :range => nil, :length => nil,
288
+ :msg => nil,
289
+ :msg_no_value => Glue::Validation::Errors.no_value,
290
+ :msg_short => Glue::Validation::Errors.too_short,
291
+ :msg_long => Glue::Validation::Errors.too_long,
292
+ :on => :save
293
+ }
294
+ c.update(params.pop) if params.last.is_a?(Hash)
295
+
296
+ min, max = c[:min], c[:max]
297
+ range, length = c[:range], c[:length]
298
+
299
+ count = 0
300
+ [min, max, range, length].each { |o| count += 1 if o }
301
+
302
+ if count == 0
303
+ raise(ArgumentError,
304
+ 'One of :min, :max, :range, :length must be provided!')
305
+ end
306
+
307
+ if count > 1
308
+ raise(ArgumentError,
309
+ 'The :min, :max, :range, :length options are mutually exclusive!')
310
+ end
311
+
312
+ for name in params
313
+ if min
314
+ c[:msg] ||= Glue::Validation::Errors.too_short
315
+ code = %{
316
+ if obj.#{name}.nil?
317
+ errors.add(:#{name}, '#{c[:msg_no_value]}')
318
+ elsif obj.#{name}.to_s.length < #{min}
319
+ msg = '#{c[:msg]}'
320
+ msg = (msg % #{min}) rescue msg
321
+ errors.add(:#{name}, msg)
322
+ end;
323
+ }
324
+ elsif max
325
+ c[:msg] ||= Glue::Validation::Errors.too_long
326
+ code = %{
327
+ if obj.#{name}.nil?
328
+ errors.add(:#{name}, '#{c[:msg_no_value]}')
329
+ elsif obj.#{name}.to_s.length > #{max}
330
+ msg = '#{c[:msg]}'
331
+ msg = (msg % #{max}) rescue msg
332
+ errors.add(:#{name}, msg)
333
+ end;
334
+ }
335
+ elsif range
336
+ code = %{
337
+ if obj.#{name}.nil?
338
+ errors.add(:#{name}, '#{c[:msg_no_value]}')
339
+ elsif obj.#{name}.to_s.length < #{range.first}
340
+ msg = '#{c[:msg_short]}'
341
+ msg = (msg % #{range.first}) rescue msg
342
+ errors.add(:#{name}, msg)
343
+ elsif obj.#{name}.to_s.length > #{range.last}
344
+ msg = '#{c[:msg_long]}'
345
+ msg = (msg % #{range.last}) rescue msg
346
+ errors.add(:#{name}, msg)
347
+ end;
348
+ }
349
+ elsif length
350
+ c[:msg] ||= Glue::Validation::Errors.invalid_length
351
+ code = %{
352
+ if obj.#{name}.nil?
353
+ errors.add(:#{name}, '#{c[:msg_no_value]}')
354
+ elsif obj.#{name}.to_s.length != #{length}
355
+ msg = '#{c[:msg]}'
356
+ msg = (msg % #{length}) rescue msg
357
+ errors.add(:#{name}, msg)
358
+ end;
359
+ }
360
+ end
361
+
362
+ __meta[:validations] << [code, c[:on]]
363
+ end
364
+ end
365
+
366
+ # Validates that the attributes are included in
367
+ # an enumeration.
368
+ #
369
+ # === Example
370
+ #
371
+ # validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
372
+ # validate_inclusion :age, :in => 5..99
373
+
374
+ def validate_inclusion(*params)
375
+ c = {
376
+ :in => nil,
377
+ :msg => Glue::Validation::Errors.no_inclusion,
378
+ :allow_nil => false,
379
+ :on => :save
380
+ }
381
+ c.update(params.pop) if params.last.is_a?(Hash)
382
+
383
+ unless c[:in].respond_to?('include?')
384
+ raise(ArgumentError,
385
+ 'An object that responds to #include? must be supplied as the :in option')
386
+ end
387
+
388
+ for name in params
389
+ if c[:allow_nil]
390
+ code = %{
391
+ unless obj.#{name}.nil? or (#{c[:in].inspect}).include?(obj.#{name})
392
+ errors.add(:#{name}, '#{c[:msg]}')
393
+ end;
394
+ }
395
+ else
396
+ code = %{
397
+ unless (#{c[:in].inspect}).include?(obj.#{name})
398
+ errors.add(:#{name}, '#{c[:msg]}')
399
+ end;
400
+ }
401
+ end
402
+
403
+ __meta[:validations] << [code, c[:on]]
404
+ end
405
+ end
406
+
407
+ # Validates that the attributes have numeric values.
408
+ #
409
+ # [+:integer+]
410
+ # Only accept integers
411
+ #
412
+ # [+:msg]
413
+ # Custom message
414
+ #
415
+ # [+:on:]
416
+ # When to validate
417
+ #
418
+ # === Example
419
+ #
420
+ # validate_numeric :age, :msg => 'Must be an integer'
421
+
422
+ def validate_numeric(*params)
423
+ c = {
424
+ :integer => false,
425
+ :msg => Glue::Validation::Errors.no_numeric,
426
+ :on => :save
427
+ }
428
+ c.update(params.pop) if params.last.is_a?(Hash)
429
+
430
+ for name in params
431
+ if c[:integer]
432
+ code = %{
433
+ unless obj.#{name}.to_s =~ /^[\\+\\-]?\\d+$/
434
+ errors.add(:#{name}, '#{c[:msg]}')
435
+ end;
436
+ }
437
+ else
438
+ code = %{
439
+ begin
440
+ Kernel.Float(obj.#{name})
441
+ rescue ArgumentError, TypeError
442
+ errors.add(:#{name}, '#{c[:msg]}')
443
+ end;
444
+ }
445
+ end
446
+
447
+ __meta[:validations] << [code, c[:on]]
448
+ end
449
+ end
450
+ alias_method :validate_numericality, :validate_numeric
451
+
452
+ end
453
453
 
454
454
  end
455
455
 
456
456
  end
457
457
 
458
458
  class Module # :nodoc: all
459
- include Glue::Validation::MetaLanguage
459
+ include Glue::Validation::MetaLanguage
460
460
  end
461
461