validates_timeliness 1.1.7 → 2.0.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.
- data/CHANGELOG +8 -0
- data/README.rdoc +25 -0
- data/Rakefile +1 -1
- data/TODO +3 -5
- data/lib/validates_timeliness/action_view/instance_tag.rb +1 -1
- data/lib/validates_timeliness/active_record/attribute_methods.rb +3 -6
- data/lib/validates_timeliness/active_record/multiparameter_attributes.rb +1 -1
- data/lib/validates_timeliness/formats.rb +28 -18
- data/lib/validates_timeliness/locale/en.yml +5 -0
- data/lib/validates_timeliness/parser.rb +46 -0
- data/lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb +4 -3
- data/lib/validates_timeliness/validation_methods.rb +0 -36
- data/lib/validates_timeliness/validator.rb +55 -33
- data/lib/validates_timeliness.rb +10 -14
- data/spec/active_record/attribute_methods_spec.rb +13 -3
- data/spec/formats_spec.rb +32 -55
- data/spec/{validation_methods_spec.rb → parser_spec.rb} +12 -12
- data/spec/spec/rails/matchers/validate_timeliness_spec.rb +26 -0
- data/spec/spec_helper.rb +4 -2
- data/spec/validator_spec.rb +33 -5
- metadata +4 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 2.0.0 [2009-04-12]
|
2
|
+
- Error value formats are now specified in the i18n locale file instead of updating plugin hash. See OTHER CUSTOMISATION section in README.
|
3
|
+
- Date/time select helper extension is disabled by default. To enable see DISPLAY INVALID VALUES IN DATE HELPERS section in README to enable.
|
4
|
+
- Added :format option to limit validation to a single format if desired
|
5
|
+
- Matcher now supports :equal_to option
|
6
|
+
- Formats.parse can take :include_offset option to include offset value from string in seconds, if string contains an offset. Offset not used in rest of plugin yet.
|
7
|
+
- Refactored to remove as much plugin code from ActiveRecord as possible.
|
8
|
+
|
1
9
|
= 1.1.7 [2009-03-26]
|
2
10
|
- Minor change to multiparameter attributes which I had not properly implemented for chaining
|
3
11
|
|
data/README.rdoc
CHANGED
@@ -80,6 +80,7 @@ Special options:
|
|
80
80
|
:with_time - Validate a date attribute value combined with a time value against any temporal restrictions
|
81
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
|
+
:format - Limit validation to a single format for special cases. Takes plugin format value.
|
83
84
|
|
84
85
|
Message options: - Use these to override the default error messages
|
85
86
|
:invalid_date_message
|
@@ -266,6 +267,20 @@ corner cases a little harder to test. In general if you are using procs or
|
|
266
267
|
model methods and you only care when they return a value, then they should
|
267
268
|
return nil in all other situations. Restrictions are skipped if they are nil.
|
268
269
|
|
270
|
+
|
271
|
+
=== DISPLAY INVALID VALUES IN DATE HELPERS:
|
272
|
+
|
273
|
+
The plugin has some extensions to ActionView and ActiveRecord by allowing invalid
|
274
|
+
date and time values to be redisplayed to the user as feedback, instead of
|
275
|
+
a blank field which happens by default in Rails. Though the date helpers make this a
|
276
|
+
pretty rare occurence, given the select dropdowns for each date/time component, but
|
277
|
+
it may be something of interest.
|
278
|
+
|
279
|
+
To activate it, put this in an initializer:
|
280
|
+
|
281
|
+
ValidatesTimeliness.enable_datetime_select_extension!
|
282
|
+
|
283
|
+
|
269
284
|
=== OTHER CUSTOMISATION:
|
270
285
|
|
271
286
|
The error messages for each temporal restrictions can also be globally overridden by
|
@@ -302,12 +317,22 @@ will be inserted.
|
|
302
317
|
And for something a little more specific you can override the format of the interpolation
|
303
318
|
values inserted in the error messages for temporal restrictions like so
|
304
319
|
|
320
|
+
For Rails 2.0/2.1:
|
321
|
+
|
305
322
|
ValidatesTimeliness::Validator.error_value_formats.update(
|
306
323
|
:time => '%H:%M:%S',
|
307
324
|
:date => '%Y-%m-%d',
|
308
325
|
:datetime => '%Y-%m-%d %H:%M:%S'
|
309
326
|
)
|
310
327
|
|
328
|
+
Rails 2.2+ using the I18n system to define new defaults:
|
329
|
+
|
330
|
+
validates_timeliness:
|
331
|
+
error_value_formats:
|
332
|
+
date: '%Y-%m-%d'
|
333
|
+
time: '%H:%M:%S'
|
334
|
+
datetime: '%Y-%m-%d %H:%M:%S'
|
335
|
+
|
311
336
|
Those are Ruby strftime formats not the plugin formats.
|
312
337
|
|
313
338
|
|
data/Rakefile
CHANGED
data/TODO
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
- :format option
|
2
1
|
- valid formats could come from locale file
|
3
2
|
- add replace_formats instead add_formats :before
|
4
|
-
- array of values
|
5
|
-
- use tz value from time string?
|
6
|
-
-
|
7
|
-
|
3
|
+
- array of values for all temporal options
|
4
|
+
- use tz and zo value from time string?
|
5
|
+
- filter valid formats rather than remove for hot swapping without recompilation
|
@@ -46,7 +46,7 @@ module ValidatesTimeliness
|
|
46
46
|
# implementation as it chains the write_attribute method which deletes
|
47
47
|
# the attribute from the cache.
|
48
48
|
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
49
|
-
new =
|
49
|
+
new = ValidatesTimeliness::Parser.parse(value, type)
|
50
50
|
|
51
51
|
if new && type != :date
|
52
52
|
new = new.to_time
|
@@ -73,18 +73,16 @@ module ValidatesTimeliness
|
|
73
73
|
|
74
74
|
if @attributes_cache.has_key?(attr_name)
|
75
75
|
time = read_attribute_before_type_cast(attr_name)
|
76
|
-
time =
|
76
|
+
time = ValidatesTimeliness::Parser.parse(time, type)
|
77
77
|
else
|
78
78
|
time = read_attribute(attr_name)
|
79
|
-
@attributes[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
79
|
+
@attributes[attr_name] = (time && time_zone_aware ? time.in_time_zone : time) unless frozen?
|
80
80
|
end
|
81
81
|
@attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
82
82
|
end
|
83
83
|
|
84
84
|
module ClassMethods
|
85
85
|
|
86
|
-
# Define attribute reader and writer method for date, time and
|
87
|
-
# datetime attributes to use plugin parser.
|
88
86
|
def define_attribute_methods_with_timeliness
|
89
87
|
return if generated_methods?
|
90
88
|
columns_hash.each do |name, column|
|
@@ -105,7 +103,6 @@ module ValidatesTimeliness
|
|
105
103
|
define_attribute_methods_without_timeliness
|
106
104
|
end
|
107
105
|
|
108
|
-
# Define write method for date, time and datetime columns
|
109
106
|
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
110
107
|
method_body = <<-EOV
|
111
108
|
def #{attr_name}=(value)
|
@@ -124,13 +124,13 @@ module ValidatesTimeliness
|
|
124
124
|
{ 's' => [ /s{1}/, '(\d{1,2})', :sec ] },
|
125
125
|
{ 'u' => [ /u{1,}/, '(\d{1,6})', :usec ] },
|
126
126
|
{ 'ampm' => [ /ampm/, '((?:[aApP])\.?[mM]\.?)', :meridian ] },
|
127
|
-
{ 'zo' => [ /zo/, '(
|
127
|
+
{ 'zo' => [ /zo/, '([+-]\d{2}:?\d{2})', :offset ] },
|
128
128
|
{ 'tz' => [ /tz/, '(?:[A-Z]{1,4})' ] },
|
129
129
|
{ '_' => [ /_/, '\s?' ] }
|
130
130
|
]
|
131
131
|
|
132
|
-
# Arguments
|
133
|
-
# time string. The key must
|
132
|
+
# Arguments which will be passed to the format proc if matched in the
|
133
|
+
# time string. The key must be the key from the format tokens. The array
|
134
134
|
# consists of the arry position of the arg, the arg name, and the code to
|
135
135
|
# place in the time array slot. The position can be nil which means the arg
|
136
136
|
# won't be placed in the array.
|
@@ -146,6 +146,7 @@ module ValidatesTimeliness
|
|
146
146
|
:min => [4, 'n', 'n'],
|
147
147
|
:sec => [5, 's', 's'],
|
148
148
|
:usec => [6, 'u', 'microseconds(u)'],
|
149
|
+
:offset => [7, 'z', 'offset_in_seconds(z)'],
|
149
150
|
:meridian => [nil, 'md', nil]
|
150
151
|
}
|
151
152
|
|
@@ -160,21 +161,29 @@ module ValidatesTimeliness
|
|
160
161
|
# Loop through format expressions for type and call proc on matches. Allow
|
161
162
|
# pre or post match strings to exist if strict is false. Otherwise wrap
|
162
163
|
# regexp in start and end anchors.
|
163
|
-
# Returns
|
164
|
-
def parse(string, type,
|
164
|
+
# Returns time array if matches a format, nil otherwise.
|
165
|
+
def parse(string, type, options={})
|
165
166
|
return string unless string.is_a?(String)
|
167
|
+
options.reverse_merge!(:strict => true)
|
168
|
+
|
169
|
+
sets = if options[:format]
|
170
|
+
[ send("#{type}_expressions").assoc(options[:format]) ]
|
171
|
+
else
|
172
|
+
expression_set(type, string)
|
173
|
+
end
|
166
174
|
|
167
175
|
matches = nil
|
168
|
-
|
169
|
-
full = /\A#{regexp}\Z/ if strict
|
176
|
+
processor = sets.each do |format, regexp, proc|
|
177
|
+
full = /\A#{regexp}\Z/ if options[:strict]
|
170
178
|
full ||= case type
|
171
179
|
when :date then /\A#{regexp}/
|
172
180
|
when :time then /#{regexp}\Z/
|
173
181
|
when :datetime then /\A#{regexp}\Z/
|
174
182
|
end
|
175
|
-
matches = full.match(string.strip)
|
183
|
+
break(proc) if matches = full.match(string.strip)
|
176
184
|
end
|
177
|
-
|
185
|
+
last = options[:include_offset] ? 8 : 7
|
186
|
+
processor.call(*matches[1..last]) if matches
|
178
187
|
end
|
179
188
|
|
180
189
|
# Delete formats of specified type. Error raised if format not found.
|
@@ -206,8 +215,7 @@ module ValidatesTimeliness
|
|
206
215
|
end
|
207
216
|
compile_format_expressions
|
208
217
|
end
|
209
|
-
|
210
|
-
|
218
|
+
|
211
219
|
# Removes formats where the 1 or 2 digit month comes first, to eliminate
|
212
220
|
# formats which are ambiguous with the European style of day then month.
|
213
221
|
# The mmm token is ignored as its not ambigous.
|
@@ -246,22 +254,17 @@ module ValidatesTimeliness
|
|
246
254
|
# argument in the position indicated by the first element of the proc arg
|
247
255
|
# array.
|
248
256
|
#
|
249
|
-
# Examples:
|
250
|
-
#
|
251
|
-
# 'yyyy-mm-dd hh:nn' => lambda {|y,m,d,h,n| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|i| i.to_i } }
|
252
|
-
# 'dd/mm/yyyy h:nn_ampm' => lambda {|d,m,y,h,n,md| md||=0; [unambiguous_year(y),month_index(m),d,full_hour(h,md),n,nil,nil].map {|i| i.to_i } }
|
253
|
-
#
|
254
257
|
def format_proc(order)
|
255
258
|
arg_map = format_proc_args
|
256
259
|
args = order.invert.sort.map {|p| arg_map[p[1]][1] }
|
257
260
|
arr = [nil] * 7
|
258
261
|
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
|
259
|
-
proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.to_i } }"
|
262
|
+
proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i } }"
|
260
263
|
eval proc_string
|
261
264
|
end
|
262
265
|
|
263
266
|
def compile_formats(formats)
|
264
|
-
formats.map { |format|
|
267
|
+
formats.map { |format| [ format, *format_expression_generator(format) ] }
|
265
268
|
end
|
266
269
|
|
267
270
|
# Pick expression set and combine date and datetimes for
|
@@ -313,6 +316,13 @@ module ValidatesTimeliness
|
|
313
316
|
def microseconds(usec)
|
314
317
|
(".#{usec}".to_f * 1_000_000).to_i
|
315
318
|
end
|
319
|
+
|
320
|
+
def offset_in_seconds(offset)
|
321
|
+
sign = offset =~ /^-/ ? -1 : 1
|
322
|
+
parts = offset.scan(/\d\d/).map {|p| p.to_f }
|
323
|
+
parts[1] = parts[1].to_f / 60
|
324
|
+
(parts[0] + parts[1]) * sign * 3600
|
325
|
+
end
|
316
326
|
end
|
317
327
|
end
|
318
328
|
end
|
@@ -11,3 +11,8 @@ en:
|
|
11
11
|
after: "must be after {{restriction}}"
|
12
12
|
on_or_after: "must be on or after {{restriction}}"
|
13
13
|
between: "must be between {{earliest}} and {{latest}}"
|
14
|
+
validates_timeliness:
|
15
|
+
error_value_formats:
|
16
|
+
date: '%Y-%m-%d'
|
17
|
+
time: '%H:%M:%S'
|
18
|
+
datetime: '%Y-%m-%d %H:%M:%S'
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module Parser
|
3
|
+
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def parse(raw_value, type, options={})
|
7
|
+
return nil if raw_value.blank?
|
8
|
+
return raw_value if raw_value.acts_like?(:time) || raw_value.is_a?(Date)
|
9
|
+
|
10
|
+
options.reverse_merge!(:strict => true)
|
11
|
+
|
12
|
+
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, options)
|
13
|
+
raise if time_array.nil?
|
14
|
+
|
15
|
+
# Rails dummy time date part is defined as 2000-01-01
|
16
|
+
time_array[0..2] = 2000, 1, 1 if type == :time
|
17
|
+
|
18
|
+
# Date.new enforces days per month, unlike Time
|
19
|
+
date = Date.new(*time_array[0..2]) unless type == :time
|
20
|
+
|
21
|
+
return date if type == :date
|
22
|
+
|
23
|
+
make_time(time_array[0..7])
|
24
|
+
rescue
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def make_time(time_array)
|
29
|
+
if Time.respond_to?(:zone) && ValidatesTimeliness.use_time_zones
|
30
|
+
Time.zone.local(*time_array)
|
31
|
+
else
|
32
|
+
begin
|
33
|
+
time_zone = ValidatesTimeliness.default_timezone
|
34
|
+
Time.send(time_zone, *time_array)
|
35
|
+
rescue ArgumentError, TypeError
|
36
|
+
zone_offset = time_zone == :local ? DateTime.local_offset : 0
|
37
|
+
time_array.pop # remove microseconds
|
38
|
+
DateTime.civil(*(time_array << zone_offset))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -10,6 +10,7 @@ module Spec
|
|
10
10
|
}
|
11
11
|
|
12
12
|
OPTION_TEST_SETTINGS = {
|
13
|
+
:equal_to => { :method => :+, :modify_on => :invalid },
|
13
14
|
:before => { :method => :-, :modify_on => :valid },
|
14
15
|
:after => { :method => :+, :modify_on => :valid },
|
15
16
|
:on_or_before => { :method => :+, :modify_on => :invalid },
|
@@ -27,9 +28,9 @@ module Spec
|
|
27
28
|
|
28
29
|
valid = test_validity
|
29
30
|
|
31
|
+
valid = test_option(:equal_to) if @options[:equal_to] && valid
|
30
32
|
valid = test_option(:before) if @options[:before] && valid
|
31
33
|
valid = test_option(:after) if @options[:after] && valid
|
32
|
-
|
33
34
|
valid = test_option(:on_or_before) if @options[:on_or_before] && valid
|
34
35
|
valid = test_option(:on_or_after) if @options[:on_or_after] && valid
|
35
36
|
|
@@ -116,7 +117,7 @@ module Spec
|
|
116
117
|
end
|
117
118
|
|
118
119
|
def error_message_for(option)
|
119
|
-
msg = @validator.
|
120
|
+
msg = @validator.error_messages[option]
|
120
121
|
restriction = @validator.class.send(:evaluate_option_value, @validator.configuration[option], @type, @record)
|
121
122
|
|
122
123
|
if restriction
|
@@ -135,7 +136,7 @@ module Spec
|
|
135
136
|
|
136
137
|
def format_value(value)
|
137
138
|
return value if value.is_a?(String)
|
138
|
-
value.strftime(
|
139
|
+
value.strftime(@validator.class.error_value_formats[@type])
|
139
140
|
end
|
140
141
|
end
|
141
142
|
|
@@ -7,27 +7,6 @@ module ValidatesTimeliness
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
|
10
|
-
def parse_date_time(raw_value, type, strict=true)
|
11
|
-
return nil if raw_value.blank?
|
12
|
-
return raw_value if raw_value.acts_like?(:time) || raw_value.is_a?(Date)
|
13
|
-
|
14
|
-
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, strict)
|
15
|
-
raise if time_array.nil?
|
16
|
-
|
17
|
-
# Rails dummy time date part is defined as 2000-01-01
|
18
|
-
time_array[0..2] = 2000, 1, 1 if type == :time
|
19
|
-
|
20
|
-
# Date.new enforces days per month, unlike Time
|
21
|
-
date = Date.new(*time_array[0..2]) unless type == :time
|
22
|
-
|
23
|
-
return date if type == :date
|
24
|
-
|
25
|
-
# Create time object which checks time part, and return time object
|
26
|
-
make_time(time_array)
|
27
|
-
rescue
|
28
|
-
nil
|
29
|
-
end
|
30
|
-
|
31
10
|
def validates_time(*attr_names)
|
32
11
|
configuration = attr_names.extract_options!
|
33
12
|
configuration[:type] = :time
|
@@ -59,21 +38,6 @@ module ValidatesTimeliness
|
|
59
38
|
end
|
60
39
|
end
|
61
40
|
|
62
|
-
# Time.zone. Rails 2.0 should be default_timezone.
|
63
|
-
def make_time(time_array)
|
64
|
-
if Time.respond_to?(:zone) && time_zone_aware_attributes
|
65
|
-
Time.zone.local(*time_array)
|
66
|
-
else
|
67
|
-
begin
|
68
|
-
Time.send(::ActiveRecord::Base.default_timezone, *time_array)
|
69
|
-
rescue ArgumentError, TypeError
|
70
|
-
zone_offset = ::ActiveRecord::Base.default_timezone == :local ? DateTime.local_offset : 0
|
71
|
-
time_array.pop # remove microseconds
|
72
|
-
DateTime.civil(*(time_array << zone_offset))
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
41
|
end
|
78
42
|
|
79
43
|
end
|
@@ -2,14 +2,7 @@ module ValidatesTimeliness
|
|
2
2
|
|
3
3
|
class Validator
|
4
4
|
cattr_accessor :ignore_restriction_errors
|
5
|
-
cattr_accessor :error_value_formats
|
6
|
-
|
7
5
|
self.ignore_restriction_errors = false
|
8
|
-
self.error_value_formats = {
|
9
|
-
:time => '%H:%M:%S',
|
10
|
-
:date => '%Y-%m-%d',
|
11
|
-
:datetime => '%Y-%m-%d %H:%M:%S'
|
12
|
-
}
|
13
6
|
|
14
7
|
RESTRICTION_METHODS = {
|
15
8
|
:equal_to => :==,
|
@@ -21,8 +14,8 @@ module ValidatesTimeliness
|
|
21
14
|
}
|
22
15
|
|
23
16
|
VALID_OPTIONS = [
|
24
|
-
:on, :if, :unless, :allow_nil, :empty, :allow_blank,
|
25
|
-
:with_time, :with_date, :ignore_usec,
|
17
|
+
:on, :if, :unless, :allow_nil, :empty, :allow_blank,
|
18
|
+
:with_time, :with_date, :ignore_usec, :format,
|
26
19
|
:invalid_time_message, :invalid_date_message, :invalid_datetime_message
|
27
20
|
] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten
|
28
21
|
|
@@ -36,18 +29,32 @@ module ValidatesTimeliness
|
|
36
29
|
end
|
37
30
|
|
38
31
|
def call(record, attr_name, value)
|
39
|
-
value = record.class.parse_date_time(value, type, false) if value.is_a?(String)
|
40
32
|
raw_value = raw_value(record, attr_name) || value
|
41
33
|
|
34
|
+
if value.is_a?(String) || configuration[:format]
|
35
|
+
strict = !configuration[:format].nil?
|
36
|
+
value = ValidatesTimeliness::Parser.parse(raw_value, type, :strict => strict, :format => configuration[:format])
|
37
|
+
end
|
38
|
+
|
42
39
|
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
if raw_value.blank?
|
42
|
+
add_error(record, attr_name, :blank)
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
if value.nil?
|
47
|
+
add_error(record, attr_name, "invalid_#{type}".to_sym)
|
48
|
+
return
|
49
|
+
end
|
47
50
|
|
48
51
|
validate_restrictions(record, attr_name, value)
|
49
52
|
end
|
50
|
-
|
53
|
+
|
54
|
+
def error_messages
|
55
|
+
@error_messages ||= self.class.default_error_messages.merge(custom_error_messages)
|
56
|
+
end
|
57
|
+
|
51
58
|
private
|
52
59
|
|
53
60
|
def raw_value(record, attr_name)
|
@@ -55,12 +62,12 @@ module ValidatesTimeliness
|
|
55
62
|
end
|
56
63
|
|
57
64
|
def validate_restrictions(record, attr_name, value)
|
58
|
-
value = if
|
65
|
+
value = if configuration[:with_time] || configuration[:with_date]
|
59
66
|
restriction_type = :datetime
|
60
67
|
combine_date_and_time(value, record)
|
61
68
|
else
|
62
69
|
restriction_type = type
|
63
|
-
self.class.type_cast_value(value, type,
|
70
|
+
self.class.type_cast_value(value, type, configuration[:ignore_usec])
|
64
71
|
end
|
65
72
|
return if value.nil?
|
66
73
|
|
@@ -69,7 +76,7 @@ module ValidatesTimeliness
|
|
69
76
|
begin
|
70
77
|
restriction = self.class.evaluate_option_value(restriction, restriction_type, record)
|
71
78
|
next if restriction.nil?
|
72
|
-
restriction = self.class.type_cast_value(restriction, restriction_type,
|
79
|
+
restriction = self.class.type_cast_value(restriction, restriction_type, configuration[:ignore_usec])
|
73
80
|
|
74
81
|
unless evaluate_restriction(restriction, value, method)
|
75
82
|
add_error(record, attr_name, option, interpolation_values(option, restriction))
|
@@ -87,7 +94,7 @@ module ValidatesTimeliness
|
|
87
94
|
restriction = [restriction] unless restriction.is_a?(Array)
|
88
95
|
|
89
96
|
if defined?(I18n)
|
90
|
-
message = custom_error_messages[option] || I18n.
|
97
|
+
message = custom_error_messages[option] || I18n.t('activerecord.errors.messages')[option]
|
91
98
|
subs = message.scan(/\{\{([^\}]*)\}\}/)
|
92
99
|
interpolations = {}
|
93
100
|
subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
|
@@ -110,7 +117,6 @@ module ValidatesTimeliness
|
|
110
117
|
|
111
118
|
def add_error(record, attr_name, message, interpolate=nil)
|
112
119
|
if defined?(I18n)
|
113
|
-
# use i18n support in AR for message or use custom message passed to validation method
|
114
120
|
custom = custom_error_messages[message]
|
115
121
|
record.errors.add(attr_name, custom || message, interpolate || {})
|
116
122
|
else
|
@@ -120,10 +126,6 @@ module ValidatesTimeliness
|
|
120
126
|
end
|
121
127
|
end
|
122
128
|
|
123
|
-
def error_messages
|
124
|
-
@error_messages ||= ValidatesTimeliness.default_error_messages.merge(custom_error_messages)
|
125
|
-
end
|
126
|
-
|
127
129
|
def custom_error_messages
|
128
130
|
@custom_error_messages ||= configuration.inject({}) {|msgs, (k, v)|
|
129
131
|
if md = /(.*)_message$/.match(k.to_s)
|
@@ -132,33 +134,53 @@ module ValidatesTimeliness
|
|
132
134
|
msgs
|
133
135
|
}
|
134
136
|
end
|
135
|
-
|
137
|
+
|
136
138
|
def combine_date_and_time(value, record)
|
137
139
|
if type == :date
|
138
140
|
date = value
|
139
|
-
time =
|
141
|
+
time = configuration[:with_time]
|
140
142
|
else
|
141
|
-
date =
|
143
|
+
date = configuration[:with_date]
|
142
144
|
time = value
|
143
145
|
end
|
144
146
|
date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record)
|
145
147
|
return if date.nil? || time.nil?
|
146
|
-
|
148
|
+
ValidatesTimeliness::Parser.make_time([date.year, date.month, date.day, time.hour, time.min, time.sec, time.usec])
|
147
149
|
end
|
148
150
|
|
149
151
|
def validate_options(options)
|
150
|
-
invalid_for_type = ([:time, :date, :datetime] - [
|
151
|
-
invalid_for_type << :with_date unless
|
152
|
-
invalid_for_type << :with_time unless
|
152
|
+
invalid_for_type = ([:time, :date, :datetime] - [type]).map {|k| "invalid_#{k}_message".to_sym }
|
153
|
+
invalid_for_type << :with_date unless type == :time
|
154
|
+
invalid_for_type << :with_time unless type == :date
|
153
155
|
options.assert_valid_keys(VALID_OPTIONS - invalid_for_type)
|
154
156
|
end
|
155
157
|
|
156
158
|
# class methods
|
157
159
|
class << self
|
158
160
|
|
161
|
+
def default_error_messages
|
162
|
+
if defined?(I18n)
|
163
|
+
I18n.t('activerecord.errors.messages')
|
164
|
+
else
|
165
|
+
::ActiveRecord::Errors.default_error_messages
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def error_value_formats
|
170
|
+
if defined?(I18n)
|
171
|
+
I18n.t('validates_timeliness.error_value_formats')
|
172
|
+
else
|
173
|
+
@@error_value_formats
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def error_value_formats=(formats)
|
178
|
+
@@error_value_formats = formats
|
179
|
+
end
|
180
|
+
|
159
181
|
def evaluate_option_value(value, type, record)
|
160
182
|
case value
|
161
|
-
when Time, Date
|
183
|
+
when Time, Date
|
162
184
|
value
|
163
185
|
when Symbol
|
164
186
|
evaluate_option_value(record.send(value), type, record)
|
@@ -169,7 +191,7 @@ module ValidatesTimeliness
|
|
169
191
|
when Range
|
170
192
|
evaluate_option_value([value.first, value.last], type, record)
|
171
193
|
else
|
172
|
-
|
194
|
+
ValidatesTimeliness::Parser.parse(value, type, :strict => false)
|
173
195
|
end
|
174
196
|
end
|
175
197
|
|
@@ -192,7 +214,7 @@ module ValidatesTimeliness
|
|
192
214
|
nil
|
193
215
|
end
|
194
216
|
if ignore_usec && value.is_a?(Time)
|
195
|
-
::
|
217
|
+
ValidatesTimeliness::Parser.make_time(Array(value).reverse[4..9])
|
196
218
|
else
|
197
219
|
value
|
198
220
|
end
|
data/lib/validates_timeliness.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'validates_timeliness/formats'
|
2
|
+
require 'validates_timeliness/parser'
|
2
3
|
require 'validates_timeliness/validator'
|
3
4
|
require 'validates_timeliness/validation_methods'
|
4
5
|
require 'validates_timeliness/spec/rails/matchers/validate_timeliness' if ENV['RAILS_ENV'] == 'test'
|
@@ -14,9 +15,11 @@ require 'validates_timeliness/core_ext/date_time'
|
|
14
15
|
module ValidatesTimeliness
|
15
16
|
|
16
17
|
mattr_accessor :default_timezone
|
17
|
-
|
18
18
|
self.default_timezone = :utc
|
19
19
|
|
20
|
+
mattr_accessor :use_time_zones
|
21
|
+
self.use_time_zones = false
|
22
|
+
|
20
23
|
LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/validates_timeliness/locale/en.yml')
|
21
24
|
|
22
25
|
class << self
|
@@ -31,25 +34,18 @@ module ValidatesTimeliness
|
|
31
34
|
I18n.load_path += [ LOCALE_PATH ]
|
32
35
|
I18n.reload!
|
33
36
|
else
|
34
|
-
|
35
|
-
errors =
|
37
|
+
defaults = YAML::load(IO.read(LOCALE_PATH))['en']
|
38
|
+
errors = defaults['activerecord']['errors']['messages'].inject({}) {|h,(k,v)| h[k.to_sym] = v.gsub(/\{\{\w*\}\}/, '%s');h }
|
36
39
|
::ActiveRecord::Errors.default_error_messages.update(errors)
|
40
|
+
|
41
|
+
ValidatesTimeliness::Validator.error_value_formats = defaults['validates_timeliness']['error_value_formats'].symbolize_keys
|
37
42
|
end
|
38
43
|
end
|
39
44
|
|
40
|
-
def default_error_messages
|
41
|
-
if Rails::VERSION::STRING < '2.2'
|
42
|
-
::ActiveRecord::Errors.default_error_messages
|
43
|
-
else
|
44
|
-
I18n.translate('activerecord.errors.messages')
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
45
|
def setup_for_rails
|
49
|
-
major, minor = Rails::VERSION::MAJOR, Rails::VERSION::MINOR
|
50
46
|
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
51
|
-
self.
|
52
|
-
|
47
|
+
self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
|
48
|
+
load_error_messages
|
53
49
|
end
|
54
50
|
end
|
55
51
|
end
|
@@ -39,17 +39,17 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
39
39
|
end
|
40
40
|
|
41
41
|
it "should call parser on write for datetime attribute" do
|
42
|
-
|
42
|
+
ValidatesTimeliness::Parser.should_receive(:parse).once
|
43
43
|
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
44
44
|
end
|
45
45
|
|
46
46
|
it "should call parser on write for date attribute" do
|
47
|
-
|
47
|
+
ValidatesTimeliness::Parser.should_receive(:parse).once
|
48
48
|
@person.birth_date = "2000-01-01"
|
49
49
|
end
|
50
50
|
|
51
51
|
it "should call parser on write for time attribute" do
|
52
|
-
|
52
|
+
ValidatesTimeliness::Parser.should_receive(:parse).once
|
53
53
|
@person.birth_time = "12:00"
|
54
54
|
end
|
55
55
|
|
@@ -221,4 +221,14 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
221
221
|
@person.birth_date.should == tomorrow
|
222
222
|
end
|
223
223
|
|
224
|
+
it "should skip storing value in attributes hash on read if record frozen" do
|
225
|
+
@person = Person.new
|
226
|
+
@person.birth_date = Date.today
|
227
|
+
@person.save!
|
228
|
+
@person.reload
|
229
|
+
@person.freeze
|
230
|
+
@person.frozen?.should be_true
|
231
|
+
lambda { @person.birth_date }.should_not raise_error
|
232
|
+
@person.birth_date.should == Date.today
|
233
|
+
end
|
224
234
|
end
|
data/spec/formats_spec.rb
CHANGED
@@ -6,46 +6,6 @@ describe ValidatesTimeliness::Formats do
|
|
6
6
|
before do
|
7
7
|
@formats = ValidatesTimeliness::Formats
|
8
8
|
end
|
9
|
-
|
10
|
-
describe "expression generator" do
|
11
|
-
it "should generate regexp for time" do
|
12
|
-
generate_regexp_str('hh:nn:ss').should == '/(\d{2}):(\d{2}):(\d{2})/'
|
13
|
-
end
|
14
|
-
|
15
|
-
it "should generate regexp for time with meridian" do
|
16
|
-
generate_regexp_str('hh:nn:ss ampm').should == '/(\d{2}):(\d{2}):(\d{2}) ((?:[aApP])\.?[mM]\.?)/'
|
17
|
-
end
|
18
|
-
|
19
|
-
it "should generate regexp for time with meridian and optional space between" do
|
20
|
-
generate_regexp_str('hh:nn:ss_ampm').should == '/(\d{2}):(\d{2}):(\d{2})\s?((?:[aApP])\.?[mM]\.?)/'
|
21
|
-
end
|
22
|
-
|
23
|
-
it "should generate regexp for time with single or double digits" do
|
24
|
-
generate_regexp_str('h:n:s').should == '/(\d{1,2}):(\d{1,2}):(\d{1,2})/'
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should generate regexp for date" do
|
28
|
-
generate_regexp_str('yyyy-mm-dd').should == '/(\d{4})-(\d{2})-(\d{2})/'
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should generate regexp for date with slashes" do
|
32
|
-
generate_regexp_str('dd/mm/yyyy').should == '/(\d{2})\/(\d{2})\/(\d{4})/'
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should generate regexp for date with dots" do
|
36
|
-
generate_regexp_str('dd.mm.yyyy').should == '/(\d{2})\.(\d{2})\.(\d{4})/'
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should generate regexp for Ruby time string" do
|
40
|
-
expected = '/(\w{3,9}) (\w{3,9}) (\d{2}):(\d{2}):(\d{2}) (?:[+-]\d{2}:?\d{2}) (\d{4})/'
|
41
|
-
generate_regexp_str('ddd mmm hh:nn:ss zo yyyy').should == expected
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should generate regexp for iso8601 datetime" do
|
45
|
-
expected = '/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:Z|(?:[+-]\d{2}:?\d{2}))/'
|
46
|
-
generate_regexp_str('yyyy-mm-ddThh:nn:ss(?:Z|zo)').should == expected
|
47
|
-
end
|
48
|
-
end
|
49
9
|
|
50
10
|
describe "format proc generator" do
|
51
11
|
it "should generate proc which outputs date array with values in correct order" do
|
@@ -71,6 +31,10 @@ describe ValidatesTimeliness::Formats do
|
|
71
31
|
it "should generate proc which outputs time array with microseconds" do
|
72
32
|
generate_proc('hh:nn:ss.u').call('01', '02', '03', '99').should == [0,0,0,1,2,3,990000]
|
73
33
|
end
|
34
|
+
|
35
|
+
it "should generate proc which outputs datetime array with zone offset" do
|
36
|
+
generate_proc('yyyy-mm-dd hh:nn:ss.u zo').call('2001', '02', '03', '04', '05', '06', '99', '+10:00').should == [2001,2,3,4,5,6,990000,36000]
|
37
|
+
end
|
74
38
|
end
|
75
39
|
|
76
40
|
describe "validation regexps" do
|
@@ -136,49 +100,62 @@ describe ValidatesTimeliness::Formats do
|
|
136
100
|
end
|
137
101
|
end
|
138
102
|
|
139
|
-
describe "
|
103
|
+
describe "parse" do
|
140
104
|
|
141
105
|
it "should return time array from date string" do
|
142
|
-
time_array = formats.parse('12:13:14', :time, true)
|
106
|
+
time_array = formats.parse('12:13:14', :time, :strict => true)
|
143
107
|
time_array.should == [0,0,0,12,13,14,0]
|
144
108
|
end
|
145
109
|
|
146
110
|
it "should return date array from time string" do
|
147
|
-
time_array = formats.parse('2000-02-01', :date, true)
|
111
|
+
time_array = formats.parse('2000-02-01', :date, :strict => true)
|
148
112
|
time_array.should == [2000,2,1,0,0,0,0]
|
149
113
|
end
|
150
114
|
|
151
115
|
it "should return datetime array from string value" do
|
152
|
-
time_array = formats.parse('2000-02-01 12:13:14', :datetime, true)
|
116
|
+
time_array = formats.parse('2000-02-01 12:13:14', :datetime, :strict => true)
|
153
117
|
time_array.should == [2000,2,1,12,13,14,0]
|
154
118
|
end
|
155
119
|
|
156
120
|
it "should parse date string when type is datetime" do
|
157
|
-
time_array = formats.parse('2000-02-01', :datetime, false)
|
121
|
+
time_array = formats.parse('2000-02-01', :datetime, :strict => false)
|
158
122
|
time_array.should == [2000,2,1,0,0,0,0]
|
159
123
|
end
|
160
124
|
|
161
125
|
it "should ignore time when extracting date and strict is false" do
|
162
|
-
time_array = formats.parse('2000-02-01 12:
|
126
|
+
time_array = formats.parse('2000-02-01 12:13', :date, :strict => false)
|
163
127
|
time_array.should == [2000,2,1,0,0,0,0]
|
164
128
|
end
|
165
129
|
|
166
130
|
it "should ignore time when extracting date from format with trailing year and strict is false" do
|
167
|
-
time_array = formats.parse('01-02-2000 12:
|
131
|
+
time_array = formats.parse('01-02-2000 12:13', :date, :strict => false)
|
168
132
|
time_array.should == [2000,2,1,0,0,0,0]
|
169
133
|
end
|
170
134
|
|
171
135
|
it "should ignore date when extracting time and strict is false" do
|
172
|
-
time_array = formats.parse('2000-02-01 12:
|
173
|
-
time_array.should == [0,0,0,12,
|
136
|
+
time_array = formats.parse('2000-02-01 12:13', :time, :strict => false)
|
137
|
+
time_array.should == [0,0,0,12,13,0,0]
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should return zone offset when :include_offset options is true" do
|
141
|
+
time_array = formats.parse('2000-02-01T12:13:14-10:30', :datetime, :include_offset => true)
|
142
|
+
time_array.should == [2000,2,1,12,13,14,0,-37800]
|
174
143
|
end
|
175
144
|
end
|
176
145
|
|
177
|
-
describe "
|
178
|
-
|
179
|
-
formats.
|
146
|
+
describe "parse with format option" do
|
147
|
+
it "should return values if string matches specified format" do
|
148
|
+
time_array = formats.parse('2000-02-01 12:13:14', :datetime, :format => 'yyyy-mm-dd hh:nn:ss')
|
149
|
+
time_array.should == [2000,2,1,12,13,14,0]
|
180
150
|
end
|
181
|
-
|
151
|
+
|
152
|
+
it "should return nil if string does not match specified format" do
|
153
|
+
time_array = formats.parse('2000-02-01 12:13', :datetime, :format => 'yyyy-mm-dd hh:nn:ss')
|
154
|
+
time_array.should be_nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "removing formats" do
|
182
159
|
it "should remove format from format array" do
|
183
160
|
formats.remove_formats(:time, 'h.nn_ampm')
|
184
161
|
formats.time_formats.should_not include("h o'clock")
|
@@ -196,7 +173,7 @@ describe ValidatesTimeliness::Formats do
|
|
196
173
|
|
197
174
|
after do
|
198
175
|
formats.time_formats << 'h.nn_ampm'
|
199
|
-
|
176
|
+
formats.compile_format_expressions
|
200
177
|
end
|
201
178
|
end
|
202
179
|
|
@@ -254,7 +231,7 @@ describe ValidatesTimeliness::Formats do
|
|
254
231
|
|
255
232
|
def validate(time_string, type)
|
256
233
|
valid = false
|
257
|
-
formats.send("#{type}_expressions").each do |
|
234
|
+
formats.send("#{type}_expressions").each do |format, regexp, processor|
|
258
235
|
valid = true and break if /\A#{regexp}\Z/ =~ time_string
|
259
236
|
end
|
260
237
|
valid
|
@@ -1,39 +1,39 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe ValidatesTimeliness::
|
3
|
+
describe ValidatesTimeliness::Parser do
|
4
4
|
attr_accessor :person
|
5
5
|
|
6
|
-
describe "
|
6
|
+
describe "parse" do
|
7
7
|
it "should return time object for valid time string" do
|
8
|
-
|
8
|
+
parse("2000-01-01 12:13:14", :datetime).should be_kind_of(Time)
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should return nil for time string with invalid date part" do
|
12
|
-
|
12
|
+
parse("2000-02-30 12:13:14", :datetime).should be_nil
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should return nil for time string with invalid time part" do
|
16
|
-
|
16
|
+
parse("2000-02-01 25:13:14", :datetime).should be_nil
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should return Time object when passed a Time object" do
|
20
|
-
|
20
|
+
parse(Time.now, :datetime).should be_kind_of(Time)
|
21
21
|
end
|
22
22
|
|
23
23
|
if RAILS_VER >= '2.1'
|
24
24
|
it "should convert time string into current timezone" do
|
25
25
|
Time.zone = 'Melbourne'
|
26
|
-
time =
|
26
|
+
time = parse("2000-01-01 12:13:14", :datetime)
|
27
27
|
Time.zone.utc_offset.should == 10.hours
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should return nil for invalid date string" do
|
32
|
-
|
32
|
+
parse("2000-02-30", :date).should be_nil
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
36
|
-
|
35
|
+
def parse(*args)
|
36
|
+
ValidatesTimeliness::Parser.parse(*args)
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
@@ -43,14 +43,14 @@ describe ValidatesTimeliness::ValidationMethods do
|
|
43
43
|
|
44
44
|
it "should create time using current timezone" do
|
45
45
|
Time.zone = 'Melbourne'
|
46
|
-
time =
|
46
|
+
time = ValidatesTimeliness::Parser.make_time([2000,1,1,12,0,0])
|
47
47
|
time.zone.should == "EST"
|
48
48
|
end
|
49
49
|
|
50
50
|
else
|
51
51
|
|
52
52
|
it "should create time using default timezone" do
|
53
|
-
time =
|
53
|
+
time = ValidatesTimeliness::Parser.make_time([2000,1,1,12,0,0])
|
54
54
|
time.zone.should == "UTC"
|
55
55
|
end
|
56
56
|
|
@@ -5,6 +5,7 @@ end
|
|
5
5
|
|
6
6
|
class WithValidation < Person
|
7
7
|
validates_date :birth_date,
|
8
|
+
:equal_to => '2000-01-01',
|
8
9
|
:before => '2000-01-10',
|
9
10
|
:after => '2000-01-01',
|
10
11
|
:on_or_before => '2000-01-09',
|
@@ -12,6 +13,7 @@ class WithValidation < Person
|
|
12
13
|
:between => ['2000-01-01', '2000-01-03']
|
13
14
|
|
14
15
|
validates_time :birth_time,
|
16
|
+
:equal_to => '09:00',
|
15
17
|
:before => '23:00',
|
16
18
|
:after => '09:00',
|
17
19
|
:on_or_before => '22:00',
|
@@ -19,6 +21,7 @@ class WithValidation < Person
|
|
19
21
|
:between => ['09:00', '17:00']
|
20
22
|
|
21
23
|
validates_datetime :birth_date_and_time,
|
24
|
+
:equal_to => '2000-01-01 09:00',
|
22
25
|
:before => '2000-01-10 23:00',
|
23
26
|
:after => '2000-01-01 09:00',
|
24
27
|
:on_or_before => '2000-01-09 23:00',
|
@@ -61,6 +64,29 @@ describe "ValidateTimeliness matcher" do
|
|
61
64
|
end
|
62
65
|
end
|
63
66
|
|
67
|
+
describe "with equal_to option" do
|
68
|
+
test_values = {
|
69
|
+
:date => ['2000-01-01', '2000-01-02'],
|
70
|
+
:time => ['09:00', '09:01'],
|
71
|
+
:datetime => ['2000-01-01 09:00', '2000-01-01 09:01']
|
72
|
+
}
|
73
|
+
|
74
|
+
[:date, :time, :datetime].each do |type|
|
75
|
+
|
76
|
+
it "should report that #{type} is validated" do
|
77
|
+
with_validation.should self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][0])
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should report that #{type} is not validated when option value is incorrect" do
|
81
|
+
with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][1])
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should report that #{type} is not validated with option" do
|
85
|
+
no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][0])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
64
90
|
describe "with before option" do
|
65
91
|
test_values = {
|
66
92
|
:date => ['2000-01-10', '2000-01-11'],
|
data/spec/spec_helper.rb
CHANGED
@@ -39,13 +39,15 @@ ActiveRecord::Base.default_timezone = :utc
|
|
39
39
|
RAILS_VER = Rails::VERSION::STRING
|
40
40
|
puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
|
41
41
|
|
42
|
-
require 'validates_timeliness'
|
43
|
-
|
44
42
|
if RAILS_VER >= '2.1'
|
45
43
|
Time.zone_default = ActiveSupport::TimeZone['UTC']
|
46
44
|
ActiveRecord::Base.time_zone_aware_attributes = true
|
47
45
|
end
|
48
46
|
|
47
|
+
require 'validates_timeliness'
|
48
|
+
|
49
|
+
ValidatesTimeliness.enable_datetime_select_extension!
|
50
|
+
|
49
51
|
ActiveRecord::Migration.verbose = false
|
50
52
|
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
51
53
|
|
data/spec/validator_spec.rb
CHANGED
@@ -66,7 +66,7 @@ describe ValidatesTimeliness::Validator do
|
|
66
66
|
|
67
67
|
it "should return array of Time objects when restriction is array of strings" do
|
68
68
|
time1, time2 = "2000-01-02", "2000-01-01"
|
69
|
-
evaluate_option_value([time1, time2], :datetime).should == [
|
69
|
+
evaluate_option_value([time1, time2], :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
|
70
70
|
end
|
71
71
|
|
72
72
|
it "should return array of Time objects when restriction is Range of Time objects" do
|
@@ -76,7 +76,7 @@ describe ValidatesTimeliness::Validator do
|
|
76
76
|
|
77
77
|
it "should return array of Time objects when restriction is Range of time strings" do
|
78
78
|
time1, time2 = "2000-01-02", "2000-01-01"
|
79
|
-
evaluate_option_value(time1..time2, :datetime).should == [
|
79
|
+
evaluate_option_value(time1..time2, :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
|
80
80
|
end
|
81
81
|
def evaluate_option_value(restriction, type)
|
82
82
|
configure_validator(:type => type)
|
@@ -473,6 +473,20 @@ describe ValidatesTimeliness::Validator do
|
|
473
473
|
end
|
474
474
|
end
|
475
475
|
|
476
|
+
describe "instance with format option" do
|
477
|
+
it "should validate attribute when value matches format" do
|
478
|
+
configure_validator(:type => :time, :format => 'hh:nn:ss')
|
479
|
+
validate_with(:birth_time, "12:00:00")
|
480
|
+
should_have_no_error(:birth_time, :invalid_time)
|
481
|
+
end
|
482
|
+
|
483
|
+
it "should not validate attribute when value does not match format" do
|
484
|
+
configure_validator(:type => :time, :format => 'hh:nn:ss')
|
485
|
+
validate_with(:birth_time, "12:00")
|
486
|
+
should_have_error(:birth_time, :invalid_time)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
476
490
|
describe "custom_error_messages" do
|
477
491
|
it "should return hash of custom error messages from configuration with _message truncated from keys" do
|
478
492
|
configure_validator(:type => :date, :invalid_date_message => 'thats no date')
|
@@ -554,12 +568,18 @@ describe ValidatesTimeliness::Validator do
|
|
554
568
|
describe "custom formats" do
|
555
569
|
|
556
570
|
before :all do
|
557
|
-
|
558
|
-
ValidatesTimeliness::Validator.error_value_formats = {
|
571
|
+
custom = {
|
559
572
|
:time => '%H:%M %p',
|
560
573
|
:date => '%d-%m-%Y',
|
561
574
|
:datetime => '%d-%m-%Y %H:%M %p'
|
562
575
|
}
|
576
|
+
|
577
|
+
if defined?(I18n)
|
578
|
+
I18n.backend.store_translations 'en', :validates_timeliness => { :error_value_formats => custom }
|
579
|
+
else
|
580
|
+
@@formats = ValidatesTimeliness::Validator.error_value_formats
|
581
|
+
ValidatesTimeliness::Validator.error_value_formats = custom
|
582
|
+
end
|
563
583
|
end
|
564
584
|
|
565
585
|
it "should format datetime value of restriction" do
|
@@ -581,12 +601,20 @@ describe ValidatesTimeliness::Validator do
|
|
581
601
|
end
|
582
602
|
|
583
603
|
after :all do
|
584
|
-
|
604
|
+
if defined?(I18n)
|
605
|
+
I18n.reload!
|
606
|
+
else
|
607
|
+
ValidatesTimeliness::Validator.error_value_formats = @@formats
|
608
|
+
end
|
585
609
|
end
|
586
610
|
end
|
587
611
|
|
588
612
|
end
|
589
613
|
|
614
|
+
def parse(*args)
|
615
|
+
ValidatesTimeliness::Parser.parse(*args)
|
616
|
+
end
|
617
|
+
|
590
618
|
def configure_validator(options={})
|
591
619
|
@validator = ValidatesTimeliness::Validator.new(options)
|
592
620
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validates_timeliness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Meehan
|
@@ -9,7 +9,7 @@ autorequire: validates_timeliness
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-04-12 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -43,6 +43,7 @@ files:
|
|
43
43
|
- lib/validates_timeliness/active_record
|
44
44
|
- lib/validates_timeliness/active_record/attribute_methods.rb
|
45
45
|
- lib/validates_timeliness/active_record/multiparameter_attributes.rb
|
46
|
+
- lib/validates_timeliness/parser.rb
|
46
47
|
- lib/validates_timeliness/formats.rb
|
47
48
|
- lib/validates_timeliness/validator.rb
|
48
49
|
- lib/validates_timeliness/spec
|
@@ -56,7 +57,6 @@ files:
|
|
56
57
|
- spec/action_view
|
57
58
|
- spec/action_view/instance_tag_spec.rb
|
58
59
|
- spec/ginger_scenarios.rb
|
59
|
-
- spec/validation_methods_spec.rb
|
60
60
|
- spec/spec_helper.rb
|
61
61
|
- spec/formats_spec.rb
|
62
62
|
- spec/active_record
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- spec/time_travel/time_travel.rb
|
67
67
|
- spec/time_travel/time_extensions.rb
|
68
68
|
- spec/time_travel/MIT-LICENSE
|
69
|
+
- spec/parser_spec.rb
|
69
70
|
- spec/spec
|
70
71
|
- spec/spec/rails
|
71
72
|
- spec/spec/rails/matchers
|