glue 0.20.0 → 0.21.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.
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