adzap-validates_timeliness 1.1.7 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README.rdoc +25 -0
- data/Rakefile +1 -1
- data/TODO +3 -1
- data/lib/validates_timeliness.rb +10 -14
- 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/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 +5 -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,3 +1,5 @@
|
|
1
|
-
- :format option
|
2
1
|
- valid formats could come from locale file
|
3
2
|
- add replace_formats instead add_formats :before
|
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
|
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
|
@@ -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
|
@@ -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
@@ -30,6 +30,7 @@ require 'active_record'
|
|
30
30
|
require 'active_record/version'
|
31
31
|
require 'action_controller'
|
32
32
|
require 'action_view'
|
33
|
+
require 'action_mailer'
|
33
34
|
|
34
35
|
require 'spec/rails'
|
35
36
|
require 'time_travel/time_travel'
|
@@ -38,13 +39,15 @@ ActiveRecord::Base.default_timezone = :utc
|
|
38
39
|
RAILS_VER = Rails::VERSION::STRING
|
39
40
|
puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
|
40
41
|
|
41
|
-
require 'validates_timeliness'
|
42
|
-
|
43
42
|
if RAILS_VER >= '2.1'
|
44
43
|
Time.zone_default = ActiveSupport::TimeZone['UTC']
|
45
44
|
ActiveRecord::Base.time_zone_aware_attributes = true
|
46
45
|
end
|
47
46
|
|
47
|
+
require 'validates_timeliness'
|
48
|
+
|
49
|
+
ValidatesTimeliness.enable_datetime_select_extension!
|
50
|
+
|
48
51
|
ActiveRecord::Migration.verbose = false
|
49
52
|
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
50
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: adzap-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 -07: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
|