validates_timeliness 2.2.2 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,5 +1,14 @@
1
+ = 2.3.0 [2010-02-04]
2
+ - Backwards incompatible change to :equal_to option. Fixed error message clash with :equal_to option which exists in Rails already. Option is now :is_at.
3
+ - Fixed I18n support so it returns missing translation message instead of error
4
+ - Fixed attribute method bug. Write method was bypassed when method was first generated and used Rails default parser.
5
+ - Fixed date/time selects when using enable_datetime_select_extension! when some values empty
6
+ - Fixed ISO8601 datetime format which is now split into two formats
7
+ - Changed I18n error value format to fallback to global default if missing in locale
8
+ - Refactored date/time select invalid value extension to use param values. Functionality will be extracted from plugin for v3.
9
+
1
10
  = 2.2.2 [2009-09-19]
2
- - Fixed dummy_time using make_time to respect timezone. Fixes 1.9.1 bug.
11
+ - Fixed dummy_time using make_time to respect timezone. Fixes 1.9.1 bug.
3
12
 
4
13
  = 2.2.1 [2009-09-12]
5
14
  - Fixed dummy date part for time types in Validator.type_cast_value
@@ -8,7 +17,7 @@
8
17
  = 2.2.0 [2009-09-12]
9
18
  - Ruby 1.9 support!
10
19
  - Customise dummy date values for time types. See DUMMY DATE FOR TIME TYPES.
11
- - Fixed matcher conflict with Shoulda. Load plugin matcher manually now see matcher section in README
20
+ - Fixed matcher conflict with Shoulda. Load plugin matcher manually now see matcher section in README
12
21
  - Fixed :ignore_usec when used with :with_time or :with_date
13
22
  - Some clean up and refactoring
14
23
 
@@ -65,7 +74,7 @@
65
74
  - Added Rails 2.2 i18n support. Plugin error messages can specified in locale files. See README.
66
75
  - ignore_datetime_restriction_errors setting has been moved from AR to ValidatesTimeliness::Validator.ignore_restriction_errors
67
76
  - date_time_error_value_formats setting has been moved from AR to ValidatesTimeliness::Validator.error_value_formats
68
- - Namespaced modules and specs
77
+ - Namespaced modules and specs
69
78
  - Clean up of specs
70
79
  - fixed a few bugs
71
80
  - accessor methods not generating properly due method name stored as symbol in generated_attributes which fails on lookup
data/README.rdoc CHANGED
@@ -5,10 +5,10 @@
5
5
 
6
6
  == DESCRIPTION:
7
7
 
8
- Validate dates, times and datetimes for Rails 2.x. Plays nicely with new Rails 2.1
9
- features such as automatic timezone handling and dirty attributes. Allows you to
10
- add custom formats or remove defaults easily. This allows you to control what you
11
- think should be a valid date or time string.
8
+ Validate dates, times and datetimes for Rails 2.x. Plays nicely with Rails
9
+ automatic timezone handling. Allows you to add custom formats or remove defaults
10
+ easily. This allows you to control what you think should be a valid date or
11
+ time string.
12
12
 
13
13
 
14
14
  == FEATURES:
@@ -46,24 +46,24 @@ As gem
46
46
 
47
47
  == USAGE:
48
48
 
49
- To validate a model with a date, time or datetime attribute you just use the
49
+ To validate a model with a date, time or datetime attribute you just use the
50
50
  validation method
51
51
 
52
52
  class Person < ActiveRecord::Base
53
53
  validates_date :date_of_birth
54
54
 
55
55
  end
56
-
56
+
57
57
  The list of validation methods available are as follows:
58
58
  validates_date - validate value as date
59
59
  validates_time - validate value as time only i.e. '12:20pm'
60
60
  validates_datetime - validate value as a full date and time
61
-
61
+
62
62
  The validation methods take the usual options plus some specific ones to restrict
63
63
  the valid range of dates or times allowed
64
64
 
65
65
  Temporal options (or restrictions):
66
- :equal_to - Attribute must be equal to value to be valid
66
+ :is_at - Attribute must be equal to value to be valid
67
67
  :before - Attribute must be before this value to be valid
68
68
  :on_or_before - Attribute must be equal to or before this value to be valid
69
69
  :after - Attribute must be after this value to be valid
@@ -78,15 +78,15 @@ Regular validation options:
78
78
 
79
79
  Special options:
80
80
  :with_time - Validate a date attribute value combined with a time value against any temporal restrictions
81
- :with_date - Validate a time attribute value combined with a date value against any temporal restrictions
81
+ :with_date - Validate a time attribute value combined with a date value against any temporal restrictions
82
82
  :ignore_usec - Ignores microsecond value on datetime restrictions
83
83
  :format - Limit validation to a single format for special cases. Takes plugin format value.
84
-
84
+
85
85
  Message options: - Use these to override the default error messages
86
86
  :invalid_date_message
87
87
  :invalid_time_message
88
88
  :invalid_datetime_message
89
- :equal_to_message
89
+ :is_at_message
90
90
  :before_message
91
91
  :on_or_before_message
92
92
  :after_message
@@ -99,7 +99,7 @@ The temporal restrictions, with_date and with_time can take 4 different value ty
99
99
  * Proc or lambda object which may take an optional parameter being the record object
100
100
  * A symbol matching the method name in the model
101
101
 
102
- When an attribute value is compared to temporal restrictions, they are compared as
102
+ When an attribute value is compared to temporal restrictions, they are compared as
103
103
  the same type as the validation method type. So using validates_date means all
104
104
  values are compared as dates. This is except in the case of with_time and with_date
105
105
  options which effectively force the value to validated as a datetime against the
@@ -109,7 +109,7 @@ temporal options.
109
109
 
110
110
  validates_date :date_of_birth :before => lambda { 18.years.ago },
111
111
  :before_message => "must be at least 18 years old"
112
-
112
+
113
113
  validates_time :breakfast_time, :on_or_after => '6:00am',
114
114
  :on_or_after_message => 'must be after opening time',
115
115
  :before => :second_breakfast_time,
@@ -118,16 +118,16 @@ temporal options.
118
118
  validates_datetime :appointment_date, :before => lambda { 1.week.from_now }
119
119
 
120
120
  validates_date :entry_date, :with_time => '17:00', :on_or_before => :competition_closing
121
-
121
+
122
122
 
123
123
  === DATE/TIME FORMATS:
124
124
 
125
- So what formats does the plugin allow. Well there are default formats which can
126
- be added to easily using the plugins format rules. Also formats can be easily
127
- removed without hacking the plugin at all.
125
+ So what formats does the plugin allow. Well there are default formats which can
126
+ be added to easily using the plugins format rules. Also formats can be easily
127
+ removed without hacking the plugin at all.
128
128
 
129
- Below are the default formats. If you think they are easy to read then you will
130
- be happy to know that is exactly the format you can use to define your own if
129
+ Below are the default formats. If you think they are easy to read then you will
130
+ be happy to know that is exactly the format you can use to define your own if
131
131
  you want. No complex regular expressions or duck punching (monkey patching) the
132
132
  plugin is needed.
133
133
 
@@ -165,13 +165,14 @@ NOTE: To use non-US date formats see US/EURO FORMATS section
165
165
  yyyy-mm-dd hh:nn:ss
166
166
  yyyy-mm-dd h:nn
167
167
  ddd mmm d hh:nn:ss zo yyyy # Ruby time string
168
- yyyy-mm-ddThh:nn:ss(?:Z|zo) # ISO 8601
168
+ yyyy-mm-ddThh:nn:ssZ # ISO 8601 without zone offset
169
+ yyyy-mm-ddThh:nn:sszo # ISO 8601 with zone offset
169
170
 
170
171
  NOTE: To use non-US date formats see US/EURO FORMATS section
171
172
 
172
173
  Here is what each format token means:
173
174
 
174
- Format tokens:
175
+ Format tokens:
175
176
  y = year
176
177
  m = month
177
178
  d = day
@@ -184,10 +185,10 @@ Here is what each format token means:
184
185
  tz = Timezone abbreviation (e.g. UTC, GMT, PST, EST)
185
186
  zo = Timezone offset (e.g. +10:00, -08:00, +1000)
186
187
 
187
- Repeating tokens:
188
+ Repeating tokens:
188
189
  x = 1 or 2 digits for unit (e.g. 'h' means an hour can be '9' or '09')
189
190
  xx = 2 digits exactly for unit (e.g. 'hh' means an hour can only be '09')
190
-
191
+
191
192
  Special Cases:
192
193
  yy = 2 or 4 digit year
193
194
  yyyy = exactly 4 digit year
@@ -195,9 +196,9 @@ Here is what each format token means:
195
196
  ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
196
197
  u = microseconds matches 1 to 3 digits
197
198
 
198
- All other characters are considered literal. For the technically minded
199
- (well you are developers), these formats are compiled into regular expressions
200
- at runtime so don't add any extra overhead than using regular expressions
199
+ All other characters are considered literal. For the technically minded
200
+ (well you are developers), these formats are compiled into regular expressions
201
+ at runtime so don't add any extra overhead than using regular expressions
201
202
  directly. So, no, it won't make your app slow!
202
203
 
203
204
  To see all defined formats look in lib/validates_timeliness/formats.rb.
@@ -208,18 +209,18 @@ The perenial problem for non-US developers or applications not primarily for the
208
209
  US, is the US date format of m/d/yy. This is ambiguous with the European format
209
210
  of d/m/yy. By default the plugin uses the US formats as this is the Ruby default
210
211
  when it does date interpretation, and is in keeping PoLS (principle of least
211
- surprise).
212
+ surprise).
212
213
 
213
214
  To switch to using the European (or Rest of The World) formats put this in an
214
215
  initializer or environment.rb
215
216
 
216
- ValidatesTimeliness::Formats.remove_us_formats
217
+ ValidatesTimeliness::Formats.remove_us_formats
217
218
 
218
219
  Now '01/02/2000' will be parsed as 1st February 2000, instead of 2nd January 2000.
219
220
 
220
221
  === CUSTOMISING FORMATS:
221
222
 
222
- I hear you say "Thats greats but I don't want X format to be valid". Well to
223
+ I hear you say "Thats greats but I don't want X format to be valid". Well to
223
224
  remove a format stick this in an initializer file
224
225
 
225
226
  ValidatesTimeliness::Formats.remove_formats(:date, 'm\d\yy')
@@ -233,15 +234,15 @@ Ahh, then add it yourself. Again stick this in an initializer file
233
234
 
234
235
  Now "10 o'clock" will be a valid value. So easy, no more whingeing!
235
236
 
236
- You can embed regular expressions in the format but no gurantees that it will
237
- remain intact. If you avoid the use of any token characters and regexp dots or
238
- backslashes as special characters in the regexp, it may well work as expected.
239
- For special characters use POSIX character classes for safety. See the ISO 8601
237
+ You can embed regular expressions in the format but no gurantees that it will
238
+ remain intact. If you avoid the use of any token characters and regexp dots or
239
+ backslashes as special characters in the regexp, it may well work as expected.
240
+ For special characters use POSIX character classes for safety. See the ISO 8601
240
241
  datetime for an example of an embedded regular expression.
241
242
 
242
- Because formats are evaluated in order, adding a format which may be ambiguous
243
+ Because formats are evaluated in order, adding a format which may be ambiguous
243
244
  with an existing format, will mean your format is ignored. If you need to make
244
- your new format higher precedence than an existing format, you can include the
245
+ your new format higher precedence than an existing format, you can include the
245
246
  before option like so
246
247
 
247
248
  ValidatesTimeliness::Formats.add_formats(:time, 'ss:nn:hh', :before => 'hh:nn:ss')
@@ -279,7 +280,7 @@ The value should be an array of 3 values being year, month and day in that order
279
280
 
280
281
  When using the validation temporal restrictions there are times when the restriction
281
282
  value itself may be invalid. Normally this will add an error to the model such as
282
- 'restriction :before value was invalid'. These can be annoying if you are using
283
+ 'restriction :before value was invalid'. These can be annoying if you are using
283
284
  procs or methods as restrictions and don't care if they don't evaluate properly
284
285
  and you want the validation to complete. In these situations you turn them off.
285
286
 
@@ -288,8 +289,8 @@ To turn them off:
288
289
  ValidatesTimeliness::Validator.ignore_restriction_errors = true
289
290
 
290
291
  A word of warning though, as this may hide issues with the model and make those
291
- corner cases a little harder to test. In general if you are using procs or
292
- model methods and you only care when they return a value, then they should
292
+ corner cases a little harder to test. In general if you are using procs or
293
+ model methods and you only care when they return a value, then they should
293
294
  return nil in all other situations. Restrictions are skipped if they are nil.
294
295
 
295
296
 
@@ -305,10 +306,11 @@ To activate it, put this in an initializer:
305
306
 
306
307
  ValidatesTimeliness.enable_datetime_select_extension!
307
308
 
309
+ This will be removed from v3 as it adds too little to maintain.
308
310
 
309
311
  === OTHER CUSTOMISATION:
310
312
 
311
- The error messages for each temporal restrictions can also be globally overridden by
313
+ The error messages for each temporal restrictions can also be globally overridden by
312
314
  updating the default AR error messages like so
313
315
 
314
316
  For Rails 2.0/2.1:
@@ -317,6 +319,7 @@ For Rails 2.0/2.1:
317
319
  :invalid_date => "is not a valid date",
318
320
  :invalid_time => "is not a valid time",
319
321
  :invalid_datetime => "is not a valid datetime",
322
+ :is_at => "must be at %s",
320
323
  :before => "must be before %s",
321
324
  :on_or_before => "must be on or before %s",
322
325
  :after => "must be after %s",
@@ -332,8 +335,14 @@ Rails 2.2+ using the I18n system to define new defaults:
332
335
  activerecord:
333
336
  errors:
334
337
  messages:
335
- on_or_before: "must be equal to or before {{restriction}}"
336
- on_or_after: "must be equal to or after {{restriction}}"
338
+ invalid_date: "is not a valid date"
339
+ invalid_time: "is not a valid time"
340
+ invalid_datetime: "is not a valid datetime"
341
+ is_at: "must be at {{restriction}}"
342
+ before: "must be before {{restriction}}"
343
+ on_or_before: "must be on or before {{restriction}}"
344
+ after: "must be after {{restriction}}"
345
+ on_or_after: "must be on or after {{restriction}}"
337
346
  between: "must be between {{earliest}} and {{latest}}"
338
347
 
339
348
  The {{restriction}} signifies where the interpolation value for the restriction
@@ -364,9 +373,9 @@ Those are Ruby strftime formats not the plugin formats.
364
373
  === RSPEC MATCHER:
365
374
 
366
375
  To sweeten the deal that little bit more, you have an Rspec matcher available for
367
- you model specs. Now you can easily test the validations you have just written
368
- with the plugin or better yet *before* you write them! You just use the
369
- validation options you want as you would with the validation method. Those
376
+ you model specs. Now you can easily test the validations you have just written
377
+ with the plugin or better yet *before* you write them! You just use the
378
+ validation options you want as you would with the validation method. Those
370
379
  options are then verified and reported if they fail.
371
380
 
372
381
  First require it in your spec_helper.rb
@@ -391,4 +400,4 @@ The matcher names are just the singular of the validation methods.
391
400
 
392
401
  == LICENSE:
393
402
 
394
- Copyright (c) 2008 Adam Meehan, released under the MIT license
403
+ Copyright (c) 2008-2010 Adam Meehan, released under the MIT license
data/Rakefile CHANGED
@@ -7,10 +7,6 @@ require 'lib/validates_timeliness/version'
7
7
 
8
8
  GEM = "validates_timeliness"
9
9
  GEM_VERSION = ValidatesTimeliness::VERSION
10
- AUTHOR = "Adam Meehan"
11
- EMAIL = "adam.meehan@gmail.com"
12
- HOMEPAGE = "http://github.com/adzap/validates_timeliness"
13
- SUMMARY = "Date and time validation plugin for Rails 2.x which allows custom formats"
14
10
 
15
11
  spec = Gem::Specification.new do |s|
16
12
  s.name = GEM
@@ -19,12 +15,12 @@ spec = Gem::Specification.new do |s|
19
15
  s.rubyforge_project = "validatestime"
20
16
  s.has_rdoc = true
21
17
  s.extra_rdoc_files = ["README.rdoc", "LICENSE", "TODO", "CHANGELOG"]
22
- s.summary = SUMMARY
18
+ s.summary = "Date and time validation plugin for Rails 2.x which allows custom formats"
23
19
  s.description = s.summary
24
- s.author = AUTHOR
25
- s.email = EMAIL
26
- s.homepage = HOMEPAGE
27
-
20
+ s.author = "Adam Meehan"
21
+ s.email = "adam.meehan@gmail.com"
22
+ s.homepage = "http://github.com/adzap/validates_timeliness"
23
+
28
24
  s.require_path = 'lib'
29
25
  s.autorequire = GEM
30
26
  s.files = %w(LICENSE README.rdoc Rakefile TODO CHANGELOG) + Dir.glob("{lib,spec}/**/*")
data/TODO CHANGED
@@ -3,3 +3,6 @@
3
3
  - array of values for all temporal options
4
4
  - use tz and zo value from time string?
5
5
  - filter valid formats rather than remove for hot swapping without recompilation
6
+ - config generator
7
+ - move all config into top namespace
8
+ - remove action_view stuff
@@ -8,9 +8,9 @@ require 'validates_timeliness/active_record/multiparameter_attributes'
8
8
  require 'validates_timeliness/action_view/instance_tag'
9
9
 
10
10
  module ValidatesTimeliness
11
-
11
+
12
12
  mattr_accessor :default_timezone
13
- self.default_timezone = :utc
13
+ self.default_timezone = :utc
14
14
 
15
15
  mattr_accessor :use_time_zones
16
16
  self.use_time_zones = false
@@ -25,18 +25,18 @@ module ValidatesTimeliness
25
25
  end
26
26
 
27
27
  def load_error_messages
28
+ defaults = YAML::load(IO.read(LOCALE_PATH))['en']
29
+ ValidatesTimeliness::Validator.error_value_formats = defaults['validates_timeliness']['error_value_formats'].symbolize_keys
30
+
28
31
  if defined?(I18n)
29
32
  I18n.load_path.unshift(LOCALE_PATH)
30
33
  I18n.reload!
31
34
  else
32
- defaults = YAML::load(IO.read(LOCALE_PATH))['en']
33
35
  errors = defaults['activerecord']['errors']['messages'].inject({}) {|h,(k,v)| h[k.to_sym] = v.gsub(/\{\{\w*\}\}/, '%s');h }
34
36
  ::ActiveRecord::Errors.default_error_messages.update(errors)
35
-
36
- ValidatesTimeliness::Validator.error_value_formats = defaults['validates_timeliness']['error_value_formats'].symbolize_keys
37
37
  end
38
38
  end
39
-
39
+
40
40
  def setup_for_rails
41
41
  self.default_timezone = ::ActiveRecord::Base.default_timezone
42
42
  self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
@@ -1,3 +1,4 @@
1
+ # TODO remove this from the plugin for v3.
1
2
  module ValidatesTimeliness
2
3
 
3
4
  def self.enable_datetime_select_invalid_value_extension!
@@ -6,43 +7,46 @@ module ValidatesTimeliness
6
7
 
7
8
  module ActionView
8
9
 
9
- # Intercepts the date and time select helpers to allow the
10
- # attribute value before type cast to be used as in the select helpers.
11
- # This means that an invalid date or time will be redisplayed rather than the
12
- # type cast value which would be nil if invalid.
13
- module InstanceTag
14
-
10
+ # Intercepts the date and time select helpers to reuse the values from the
11
+ # the params rather than the parsed value. This allows invalid date/time
12
+ # values to be redisplayed instead of blanks to aid correction by the user.
13
+ # Its a minor usability improvement which is rarely an issue for the user.
14
+ #
15
+ module InstanceTag
16
+
15
17
  def self.included(base)
16
- selector_method = Rails::VERSION::STRING < '2.2' ? :date_or_time_select : :datetime_selector
18
+ selector_method = Rails::VERSION::STRING.to_f < 2.2 ? :date_or_time_select : :datetime_selector
17
19
  base.class_eval do
18
20
  alias_method :datetime_selector_without_timeliness, selector_method
19
21
  alias_method selector_method, :datetime_selector_with_timeliness
20
22
  end
21
23
  base.alias_method_chain :value, :timeliness
22
24
  end
23
-
24
- TimelinessDateTime = Struct.new(:year, :month, :day, :hour, :min, :sec)
25
-
25
+
26
+ TimelinessDateTime = Struct.new(:year, :month, :day, :hour, :min, :sec)
27
+
26
28
  def datetime_selector_with_timeliness(*args)
27
29
  @timeliness_date_or_time_tag = true
28
30
  datetime_selector_without_timeliness(*args)
29
31
  end
30
-
32
+
31
33
  def value_with_timeliness(object)
32
- return value_without_timeliness(object) unless @timeliness_date_or_time_tag
33
-
34
- raw_value = value_before_type_cast(object)
35
-
36
- if raw_value.nil? || raw_value.acts_like?(:time) || raw_value.is_a?(Date)
34
+ unless @timeliness_date_or_time_tag && @template_object.params[@object_name]
37
35
  return value_without_timeliness(object)
38
36
  end
39
-
40
- time_array = ValidatesTimeliness::Formats.parse(raw_value, :datetime)
41
-
42
- TimelinessDateTime.new(*time_array[0..5])
43
- end
44
-
37
+
38
+ pairs = @template_object.params[@object_name].select {|k,v| k =~ /^#{@method_name}\(/ }
39
+ return value_without_timeliness(object) if pairs.empty?
40
+
41
+ values = pairs.map do |(param, value)|
42
+ position = param.scan(/\(([0-9]*).*\)/).first.first
43
+ [position, value]
44
+ end.sort {|a,b| a[0] <=> b[0] }.map {|v| v[1] }
45
+
46
+ TimelinessDateTime.new(*values)
47
+ end
48
+
45
49
  end
46
50
 
47
- end
51
+ end
48
52
  end
@@ -51,15 +51,17 @@ module ValidatesTimeliness
51
51
  if [:date, :time, :datetime].include?(column.type)
52
52
  time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
53
53
 
54
- define_method("#{name}=") do |value|
54
+ method_name = "#{name}="
55
+ define_method(method_name) do |value|
55
56
  write_date_time_attribute(name, value, column.type, time_zone_aware)
56
57
  end
57
- timeliness_methods << name
58
+ timeliness_methods << method_name
58
59
  end
59
60
  end
60
61
 
61
62
  define_attribute_methods_without_timeliness
62
- @generated_methods += timeliness_methods
63
+ # add generated methods which is a Set object hence no += method
64
+ timeliness_methods.each {|attr| generated_methods << attr }
63
65
  end
64
66
 
65
67
  end