validates_timeliness 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README.rdoc +15 -2
- data/Rakefile +2 -2
- data/lib/validates_timeliness.rb +2 -1
- data/lib/validates_timeliness/active_record/attribute_methods.rb +32 -92
- data/lib/validates_timeliness/active_record/multiparameter_attributes.rb +7 -8
- data/lib/validates_timeliness/formats.rb +62 -40
- data/lib/validates_timeliness/validator.rb +2 -2
- data/spec/active_record/attribute_methods_spec.rb +15 -108
- data/spec/formats_spec.rb +22 -0
- data/spec/ginger_scenarios.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/validator_spec.rb +0 -6
- metadata +24 -38
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 2.1.0 [2009-06-20]
|
2
|
+
- Added ambiguous year threshold setting in Formats class to customize the threshold for 2 digit years (See README)
|
3
|
+
- Fixed interpolation values in custom error message for Rails 2.2+
|
4
|
+
- Fixed custom I18n local override of en locale
|
5
|
+
- Dramatically simplified ActiveRecord monkey patching and hackery
|
6
|
+
|
1
7
|
= 2.0.0 [2009-04-12]
|
2
8
|
- Error value formats are now specified in the i18n locale file instead of updating plugin hash. See OTHER CUSTOMISATION section in README.
|
3
9
|
- Date/time select helper extension is disabled by default. To enable see DISPLAY INVALID VALUES IN DATE HELPERS section in README to enable.
|
data/README.rdoc
CHANGED
@@ -190,7 +190,7 @@ Here is what each format token means:
|
|
190
190
|
|
191
191
|
Special Cases:
|
192
192
|
yy = 2 or 4 digit year
|
193
|
-
|
193
|
+
yyyy = exactly 4 digit year
|
194
194
|
mmm = month long name (e.g. 'Jul' or 'July')
|
195
195
|
ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
|
196
196
|
u = microseconds matches 1 to 3 digits
|
@@ -206,7 +206,7 @@ To see all defined formats look in lib/validates_timeliness/formats.rb.
|
|
206
206
|
|
207
207
|
The perenial problem for non-US developers or applications not primarily for the
|
208
208
|
US, is the US date format of m/d/yy. This is ambiguous with the European format
|
209
|
-
of d/
|
209
|
+
of d/m/yy. By default the plugin uses the US formats as this is the Ruby default
|
210
210
|
when it does date interpretation, and is in keeping PoLS (principle of least
|
211
211
|
surprise).
|
212
212
|
|
@@ -250,6 +250,19 @@ Now a time of '59:30:23' will be interpreted as 11:30:59 pm. This option saves
|
|
250
250
|
you adding a new one and deleting an old one to get it to work.
|
251
251
|
|
252
252
|
|
253
|
+
=== AMBIGUOUS YEAR THRESHOLD
|
254
|
+
|
255
|
+
When dealing with 2 digit year values, by default a year is interpreted as being
|
256
|
+
in the last century at or above 30. You can customize this however
|
257
|
+
|
258
|
+
ValidatesTimeliness::Formats.ambiguous_year_threshold = 20
|
259
|
+
|
260
|
+
Now you get:
|
261
|
+
|
262
|
+
year of 19 is considered 2019
|
263
|
+
year of 20 is considered 1920
|
264
|
+
|
265
|
+
|
253
266
|
=== TEMPORAL RESTRICTION ERRORS:
|
254
267
|
|
255
268
|
When using the validation temporal restrictions there are times when the restriction
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'date'
|
|
5
5
|
require 'spec/rake/spectask'
|
6
6
|
|
7
7
|
GEM = "validates_timeliness"
|
8
|
-
GEM_VERSION = "2.
|
8
|
+
GEM_VERSION = "2.1.0"
|
9
9
|
AUTHOR = "Adam Meehan"
|
10
10
|
EMAIL = "adam.meehan@gmail.com"
|
11
11
|
HOMEPAGE = "http://github.com/adzap/validates_timeliness"
|
@@ -34,7 +34,7 @@ task :default => :spec
|
|
34
34
|
desc "Run specs"
|
35
35
|
Spec::Rake::SpecTask.new do |t|
|
36
36
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
37
|
-
t.spec_opts = %w(
|
37
|
+
t.spec_opts = %w(--color)
|
38
38
|
end
|
39
39
|
|
40
40
|
|
data/lib/validates_timeliness.rb
CHANGED
@@ -31,7 +31,7 @@ module ValidatesTimeliness
|
|
31
31
|
|
32
32
|
def load_error_messages
|
33
33
|
if defined?(I18n)
|
34
|
-
I18n.load_path
|
34
|
+
I18n.load_path.unshift(LOCALE_PATH)
|
35
35
|
I18n.reload!
|
36
36
|
else
|
37
37
|
defaults = YAML::load(IO.read(LOCALE_PATH))['en']
|
@@ -45,6 +45,7 @@ module ValidatesTimeliness
|
|
45
45
|
def setup_for_rails
|
46
46
|
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
47
47
|
self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
|
48
|
+
self.enable_active_record_datetime_parser!
|
48
49
|
load_error_messages
|
49
50
|
end
|
50
51
|
end
|
@@ -1,124 +1,66 @@
|
|
1
1
|
module ValidatesTimeliness
|
2
|
+
|
3
|
+
def self.enable_active_record_datetime_parser!
|
4
|
+
::ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)
|
5
|
+
end
|
6
|
+
|
2
7
|
module ActiveRecord
|
3
8
|
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# and does not allow custom formats. These methods restore that ability while
|
8
|
-
# respecting the automatic timezone handling.
|
9
|
+
# Overrides write method for date, time and datetime columns
|
10
|
+
# to use plugin parser. Also adds mechanism to store value
|
11
|
+
# before type cast.
|
9
12
|
#
|
10
|
-
# The automatic timezone handling sets the assigned attribute value to the current
|
11
|
-
# zone in Time.zone. To preserve this localised value and capture the raw value
|
12
|
-
# we cache the localised value on write and store the raw value in the attributes
|
13
|
-
# hash for later retrieval and possibly validation. Any value from the database
|
14
|
-
# will not be in the attribute cache on first read so will be considered in default
|
15
|
-
# timezone and converted to local time. It is then stored back in the attributes
|
16
|
-
# hash and cached to avoid the need for any subsequent differentiation.
|
17
13
|
module AttributeMethods
|
18
14
|
|
19
15
|
def self.included(base)
|
20
16
|
base.extend ClassMethods
|
21
17
|
base.class_eval do
|
22
|
-
alias_method_chain :
|
18
|
+
alias_method_chain :read_attribute_before_type_cast, :timeliness
|
23
19
|
class << self
|
24
20
|
alias_method_chain :define_attribute_methods, :timeliness
|
25
21
|
end
|
26
22
|
end
|
27
23
|
end
|
28
24
|
|
29
|
-
# Adds check for cached date/time attributes which have been type cast already
|
30
|
-
# and value can be used from cache. This prevents the raw date/time value from
|
31
|
-
# being type cast using default Rails type casting when writing values
|
32
|
-
# to the database.
|
33
|
-
def read_attribute_with_timeliness(attr_name)
|
34
|
-
attr_name = attr_name.to_s
|
35
|
-
if !(value = @attributes[attr_name]).nil?
|
36
|
-
column = column_for_attribute(attr_name)
|
37
|
-
if column && [:date, :time, :datetime].include?(column.type) && @attributes_cache.has_key?(attr_name)
|
38
|
-
return @attributes_cache[attr_name]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
read_attribute_without_timeliness(attr_name)
|
42
|
-
end
|
43
|
-
|
44
|
-
# If Rails dirty attributes is enabled then the value is added to
|
45
|
-
# changed attributes if changed. Can't use the default dirty checking
|
46
|
-
# implementation as it chains the write_attribute method which deletes
|
47
|
-
# the attribute from the cache.
|
48
25
|
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
49
|
-
|
26
|
+
@attributes_cache["_#{attr_name}_before_type_cast"] = value
|
50
27
|
|
51
|
-
|
52
|
-
new = new.to_time
|
53
|
-
new = new.in_time_zone if time_zone_aware
|
54
|
-
end
|
28
|
+
value = ValidatesTimeliness::Parser.parse(value, type)
|
55
29
|
|
56
|
-
if
|
57
|
-
|
58
|
-
|
59
|
-
changed_attributes[attr_name] = (old.duplicable? ? old.clone : old)
|
60
|
-
end
|
30
|
+
if value && type != :date
|
31
|
+
value = value.to_time
|
32
|
+
value = value.in_time_zone if time_zone_aware
|
61
33
|
end
|
62
|
-
|
63
|
-
|
34
|
+
|
35
|
+
write_attribute(attr_name.to_sym, value)
|
64
36
|
end
|
65
37
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# database using default timezone.
|
70
|
-
def read_date_time_attribute(attr_name, type, time_zone_aware, reload = false)
|
71
|
-
cached = @attributes_cache[attr_name]
|
72
|
-
return cached if @attributes_cache.has_key?(attr_name) && !reload
|
73
|
-
|
74
|
-
if @attributes_cache.has_key?(attr_name)
|
75
|
-
time = read_attribute_before_type_cast(attr_name)
|
76
|
-
time = ValidatesTimeliness::Parser.parse(time, type)
|
77
|
-
else
|
78
|
-
time = read_attribute(attr_name)
|
79
|
-
@attributes[attr_name] = (time && time_zone_aware ? time.in_time_zone : time) unless frozen?
|
80
|
-
end
|
81
|
-
@attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
38
|
+
def read_attribute_before_type_cast_with_timeliness(attr_name)
|
39
|
+
return @attributes_cache["_#{attr_name}_before_type_cast"] if @attributes_cache.has_key?("_#{attr_name}_before_type_cast")
|
40
|
+
read_attribute_before_type_cast_without_timeliness(attr_name)
|
82
41
|
end
|
83
42
|
|
84
43
|
module ClassMethods
|
85
44
|
|
86
45
|
def define_attribute_methods_with_timeliness
|
87
46
|
return if generated_methods?
|
88
|
-
|
89
|
-
unless instance_method_already_implemented?(name)
|
90
|
-
if [:date, :time, :datetime].include?(column.type)
|
91
|
-
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
92
|
-
define_read_method_for_dates_and_times(name, column.type, time_zone_aware)
|
93
|
-
end
|
94
|
-
end
|
47
|
+
timeliness_methods = []
|
95
48
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
49
|
+
columns_hash.each do |name, column|
|
50
|
+
if [:date, :time, :datetime].include?(column.type)
|
51
|
+
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
52
|
+
|
53
|
+
class_eval <<-EOV
|
54
|
+
def #{name}=(value)
|
55
|
+
write_date_time_attribute('#{name}', value, #{column.type.inspect}, #{time_zone_aware})
|
56
|
+
end
|
57
|
+
EOV
|
58
|
+
timeliness_methods << name
|
101
59
|
end
|
102
60
|
end
|
103
|
-
define_attribute_methods_without_timeliness
|
104
|
-
end
|
105
61
|
|
106
|
-
|
107
|
-
|
108
|
-
def #{attr_name}=(value)
|
109
|
-
write_date_time_attribute('#{attr_name}', value, #{type.inspect}, #{time_zone_aware})
|
110
|
-
end
|
111
|
-
EOV
|
112
|
-
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
113
|
-
end
|
114
|
-
|
115
|
-
def define_read_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
116
|
-
method_body = <<-EOV
|
117
|
-
def #{attr_name}(reload = false)
|
118
|
-
read_date_time_attribute('#{attr_name}', #{type.inspect}, #{time_zone_aware}, reload)
|
119
|
-
end
|
120
|
-
EOV
|
121
|
-
evaluate_attribute_method attr_name, method_body
|
62
|
+
define_attribute_methods_without_timeliness
|
63
|
+
@generated_methods += timeliness_methods
|
122
64
|
end
|
123
65
|
|
124
66
|
end
|
@@ -127,5 +69,3 @@ module ValidatesTimeliness
|
|
127
69
|
|
128
70
|
end
|
129
71
|
end
|
130
|
-
|
131
|
-
ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)
|
@@ -10,9 +10,9 @@ module ValidatesTimeliness
|
|
10
10
|
def self.included(base)
|
11
11
|
base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness
|
12
12
|
end
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
13
|
+
|
14
|
+
# Assign dates and times as formatted strings to force the use of the plugin parser
|
15
|
+
# and store a before_type_cast value for attribute
|
16
16
|
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
|
17
17
|
errors = []
|
18
18
|
callstack.each do |name, values|
|
@@ -46,18 +46,17 @@ module ValidatesTimeliness
|
|
46
46
|
when :time
|
47
47
|
extract_time_from_multiparameter_attributes(values)
|
48
48
|
when :datetime
|
49
|
-
|
50
|
-
extract_date_from_multiparameter_attributes(date_values) + " " + extract_time_from_multiparameter_attributes(time_values)
|
49
|
+
extract_date_from_multiparameter_attributes(values) + " " + extract_time_from_multiparameter_attributes(values)
|
51
50
|
end
|
52
51
|
end
|
53
52
|
|
54
53
|
def extract_date_from_multiparameter_attributes(values)
|
55
|
-
|
54
|
+
year = ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0"))
|
55
|
+
[year, *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-")
|
56
56
|
end
|
57
57
|
|
58
58
|
def extract_time_from_multiparameter_attributes(values)
|
59
|
-
values
|
60
|
-
values.map { |s| s.rjust(2, "0") }.join(":")
|
59
|
+
values[3..5].map { |s| s.rjust(2, "0") }.join(":")
|
61
60
|
end
|
62
61
|
|
63
62
|
end
|
@@ -21,6 +21,17 @@ module ValidatesTimeliness
|
|
21
21
|
:format_tokens,
|
22
22
|
:format_proc_args
|
23
23
|
|
24
|
+
|
25
|
+
# Set the threshold value for a two digit year to be considered last century
|
26
|
+
# Default: 30
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
# year = '29' is considered 2029
|
30
|
+
# year = '30' is considered 1930
|
31
|
+
#
|
32
|
+
cattr_accessor :ambiguous_year_threshold
|
33
|
+
self.ambiguous_year_threshold = 30
|
34
|
+
|
24
35
|
# Format tokens:
|
25
36
|
# y = year
|
26
37
|
# m = month
|
@@ -46,7 +57,7 @@ module ValidatesTimeliness
|
|
46
57
|
#
|
47
58
|
# Special Cases:
|
48
59
|
# yy = 2 or 4 digit year
|
49
|
-
#
|
60
|
+
# yyyy = exactly 4 digit year
|
50
61
|
# mmm = month long name (e.g. 'Jul' or 'July')
|
51
62
|
# ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
|
52
63
|
# u = microseconds matches 1 to 6 digits
|
@@ -85,6 +96,7 @@ module ValidatesTimeliness
|
|
85
96
|
@@datetime_formats = [
|
86
97
|
'yyyy-mm-dd hh:nn:ss',
|
87
98
|
'yyyy-mm-dd h:nn',
|
99
|
+
'yyyy-mm-dd h:nn_ampm',
|
88
100
|
'yyyy-mm-dd hh:nn:ss.u',
|
89
101
|
'm/d/yy h:nn:ss',
|
90
102
|
'm/d/yy h:nn_ampm',
|
@@ -226,6 +238,49 @@ module ValidatesTimeliness
|
|
226
238
|
compile_format_expressions
|
227
239
|
end
|
228
240
|
|
241
|
+
def full_hour(hour, meridian)
|
242
|
+
hour = hour.to_i
|
243
|
+
return hour if meridian.nil?
|
244
|
+
if meridian.delete('.').downcase == 'am'
|
245
|
+
hour == 12 ? 0 : hour
|
246
|
+
else
|
247
|
+
hour == 12 ? hour : hour + 12
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def unambiguous_year(year)
|
252
|
+
if year.length <= 2
|
253
|
+
century = Time.now.year.to_s[0..1].to_i
|
254
|
+
century -= 1 if year.to_i >= ambiguous_year_threshold
|
255
|
+
year = "#{century}#{year.rjust(2,'0')}"
|
256
|
+
end
|
257
|
+
year.to_i
|
258
|
+
end
|
259
|
+
|
260
|
+
def month_index(month)
|
261
|
+
return month.to_i if month.to_i.nonzero?
|
262
|
+
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
|
263
|
+
end
|
264
|
+
|
265
|
+
def month_names
|
266
|
+
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
|
267
|
+
end
|
268
|
+
|
269
|
+
def abbr_month_names
|
270
|
+
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
|
271
|
+
end
|
272
|
+
|
273
|
+
def microseconds(usec)
|
274
|
+
(".#{usec}".to_f * 1_000_000).to_i
|
275
|
+
end
|
276
|
+
|
277
|
+
def offset_in_seconds(offset)
|
278
|
+
sign = offset =~ /^-/ ? -1 : 1
|
279
|
+
parts = offset.scan(/\d\d/).map {|p| p.to_f }
|
280
|
+
parts[1] = parts[1].to_f / 60
|
281
|
+
(parts[0] + parts[1]) * sign * 3600
|
282
|
+
end
|
283
|
+
|
229
284
|
private
|
230
285
|
|
231
286
|
# Compile formats into validation regexps and format procs
|
@@ -259,7 +314,12 @@ module ValidatesTimeliness
|
|
259
314
|
args = order.invert.sort.map {|p| arg_map[p[1]][1] }
|
260
315
|
arr = [nil] * 7
|
261
316
|
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
|
262
|
-
proc_string =
|
317
|
+
proc_string = <<-EOL
|
318
|
+
lambda {|#{args.join(',')}|
|
319
|
+
md ||= nil
|
320
|
+
[#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i }
|
321
|
+
}
|
322
|
+
EOL
|
263
323
|
eval proc_string
|
264
324
|
end
|
265
325
|
|
@@ -285,44 +345,6 @@ module ValidatesTimeliness
|
|
285
345
|
end
|
286
346
|
end
|
287
347
|
|
288
|
-
def full_hour(hour, meridian)
|
289
|
-
hour = hour.to_i
|
290
|
-
return hour if meridian.nil?
|
291
|
-
if meridian.delete('.').downcase == 'am'
|
292
|
-
hour == 12 ? 0 : hour
|
293
|
-
else
|
294
|
-
hour == 12 ? hour : hour + 12
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
def unambiguous_year(year, threshold=30)
|
299
|
-
year = "#{year.to_i < threshold ? '20' : '19'}#{year}" if year.length == 2
|
300
|
-
year.to_i
|
301
|
-
end
|
302
|
-
|
303
|
-
def month_index(month)
|
304
|
-
return month.to_i if month.to_i.nonzero?
|
305
|
-
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
|
306
|
-
end
|
307
|
-
|
308
|
-
def month_names
|
309
|
-
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
|
310
|
-
end
|
311
|
-
|
312
|
-
def abbr_month_names
|
313
|
-
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
|
314
|
-
end
|
315
|
-
|
316
|
-
def microseconds(usec)
|
317
|
-
(".#{usec}".to_f * 1_000_000).to_i
|
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
|
326
348
|
end
|
327
349
|
end
|
328
350
|
end
|
@@ -94,7 +94,7 @@ module ValidatesTimeliness
|
|
94
94
|
restriction = [restriction] unless restriction.is_a?(Array)
|
95
95
|
|
96
96
|
if defined?(I18n)
|
97
|
-
message =
|
97
|
+
message = I18n.t('activerecord.errors.messages')[option]
|
98
98
|
subs = message.scan(/\{\{([^\}]*)\}\}/)
|
99
99
|
interpolations = {}
|
100
100
|
subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
|
@@ -118,7 +118,7 @@ module ValidatesTimeliness
|
|
118
118
|
def add_error(record, attr_name, message, interpolate=nil)
|
119
119
|
if defined?(I18n)
|
120
120
|
custom = custom_error_messages[message]
|
121
|
-
record.errors.add(attr_name,
|
121
|
+
record.errors.add(attr_name, message, { :default => custom }.merge(interpolate || {}))
|
122
122
|
else
|
123
123
|
message = error_messages[message] if message.is_a?(Symbol)
|
124
124
|
message = message % interpolate
|
@@ -20,24 +20,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
20
20
|
@person.birth_date_and_time = "2000-01-01 12:00"
|
21
21
|
end
|
22
22
|
|
23
|
-
it "should call read_date_time_attribute when date attribute is retrieved" do
|
24
|
-
@person.should_receive(:read_date_time_attribute)
|
25
|
-
@person.birth_date = "2000-01-01"
|
26
|
-
@person.birth_date
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should call read_date_time_attribute when time attribute is retrieved" do
|
30
|
-
@person.should_receive(:read_date_time_attribute)
|
31
|
-
@person.birth_time = "12:00"
|
32
|
-
@person.birth_time
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should call read_date_time_attribute when datetime attribute is retrieved" do
|
36
|
-
@person.should_receive(:read_date_time_attribute)
|
37
|
-
@person.birth_date_and_time = "2000-01-01 12:00"
|
38
|
-
@person.birth_date_and_time
|
39
|
-
end
|
40
|
-
|
41
23
|
it "should call parser on write for datetime attribute" do
|
42
24
|
ValidatesTimeliness::Parser.should_receive(:parse).once
|
43
25
|
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
@@ -103,7 +85,20 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
103
85
|
@person.birth_date_and_time_before_type_cast.should be_nil
|
104
86
|
end
|
105
87
|
|
106
|
-
|
88
|
+
if RAILS_VER < '2.1'
|
89
|
+
|
90
|
+
it "should return time object from database in default timezone" do
|
91
|
+
ActiveRecord::Base.default_timezone = :utc
|
92
|
+
time_string = "2000-01-01 09:00:00"
|
93
|
+
@person = Person.new
|
94
|
+
@person.birth_date_and_time = time_string
|
95
|
+
@person.save
|
96
|
+
@person.reload
|
97
|
+
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
|
98
|
+
end
|
99
|
+
|
100
|
+
else
|
101
|
+
|
107
102
|
it "should return stored time string as Time with correct timezone" do
|
108
103
|
Time.zone = 'Melbourne'
|
109
104
|
time_string = "2000-06-01 02:03:04"
|
@@ -121,84 +116,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
121
116
|
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000'
|
122
117
|
end
|
123
118
|
|
124
|
-
describe "dirty attributes" do
|
125
|
-
|
126
|
-
it "should return true for attribute changed? when value updated" do
|
127
|
-
time_string = "2000-01-01 02:03:04"
|
128
|
-
@person.birth_date_and_time = time_string
|
129
|
-
@person.birth_date_and_time_changed?.should be_true
|
130
|
-
end
|
131
|
-
|
132
|
-
it "should show changes when time attribute changed from nil to Time object" do
|
133
|
-
time_string = "2000-01-01 02:03:04"
|
134
|
-
@person.birth_date_and_time = time_string
|
135
|
-
time = @person.birth_date_and_time
|
136
|
-
@person.changes.should == {"birth_date_and_time" => [nil, time]}
|
137
|
-
end
|
138
|
-
|
139
|
-
it "should show changes when time attribute changed from Time object to nil" do
|
140
|
-
time_string = "2020-01-01 02:03:04"
|
141
|
-
@person.birth_date_and_time = time_string
|
142
|
-
@person.save false
|
143
|
-
@person.reload
|
144
|
-
time = @person.birth_date_and_time
|
145
|
-
@person.birth_date_and_time = nil
|
146
|
-
@person.changes.should == {"birth_date_and_time" => [time, nil]}
|
147
|
-
end
|
148
|
-
|
149
|
-
it "should show no changes when assigned same value as Time object" do
|
150
|
-
time_string = "2020-01-01 02:03:04"
|
151
|
-
@person.birth_date_and_time = time_string
|
152
|
-
@person.save false
|
153
|
-
@person.reload
|
154
|
-
time = @person.birth_date_and_time
|
155
|
-
@person.birth_date_and_time = time
|
156
|
-
@person.changes.should == {}
|
157
|
-
end
|
158
|
-
|
159
|
-
it "should show no changes when assigned same value as time string" do
|
160
|
-
time_string = "2020-01-01 02:03:04"
|
161
|
-
@person.birth_date_and_time = time_string
|
162
|
-
@person.save false
|
163
|
-
@person.reload
|
164
|
-
@person.birth_date_and_time = time_string
|
165
|
-
@person.changes.should == {}
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
else
|
170
|
-
|
171
|
-
it "should return time object from database in default timezone" do
|
172
|
-
ActiveRecord::Base.default_timezone = :utc
|
173
|
-
time_string = "2000-01-01 09:00:00"
|
174
|
-
@person = Person.new
|
175
|
-
@person.birth_date_and_time = time_string
|
176
|
-
@person.save
|
177
|
-
@person.reload
|
178
|
-
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
it "should return same time object on repeat reads on existing object" do
|
184
|
-
Time.zone = 'Melbourne' unless RAILS_VER < '2.1'
|
185
|
-
time_string = "2000-01-01 09:00:00"
|
186
|
-
@person = Person.new
|
187
|
-
@person.birth_date_and_time = time_string
|
188
|
-
@person.save!
|
189
|
-
@person.reload
|
190
|
-
time = @person.birth_date_and_time
|
191
|
-
@person.birth_date_and_time.should == time
|
192
|
-
end
|
193
|
-
|
194
|
-
it "should return same date object on repeat reads on existing object" do
|
195
|
-
date_string = Date.today
|
196
|
-
@person = Person.new
|
197
|
-
@person.birth_date = date_string
|
198
|
-
@person.save!
|
199
|
-
@person.reload
|
200
|
-
date = @person.birth_date
|
201
|
-
@person.birth_date.should == date
|
202
119
|
end
|
203
120
|
|
204
121
|
it "should return correct date value after new value assigned" do
|
@@ -220,15 +137,5 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|
220
137
|
@person.reload
|
221
138
|
@person.birth_date.should == tomorrow
|
222
139
|
end
|
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
|
140
|
+
|
234
141
|
end
|
data/spec/formats_spec.rb
CHANGED
@@ -155,6 +155,28 @@ describe ValidatesTimeliness::Formats do
|
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
|
+
describe "parsing date with ambiguous year" do
|
159
|
+
it "should return year in current century if year below threshold" do
|
160
|
+
time_array = formats.parse('01-02-29', :date)
|
161
|
+
time_array.should == [2029,2,1,0,0,0,0]
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should return year in last century if year at or above threshold" do
|
165
|
+
time_array = formats.parse('01-02-30', :date)
|
166
|
+
time_array.should == [1930,2,1,0,0,0,0]
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should allow custom threshold" do
|
170
|
+
default = ValidatesTimeliness::Formats.ambiguous_year_threshold
|
171
|
+
ValidatesTimeliness::Formats.ambiguous_year_threshold = 40
|
172
|
+
time_array = formats.parse('01-02-39', :date)
|
173
|
+
time_array.should == [2039,2,1,0,0,0,0]
|
174
|
+
time_array = formats.parse('01-02-40', :date)
|
175
|
+
time_array.should == [1940,2,1,0,0,0,0]
|
176
|
+
ValidatesTimeliness::Formats.ambiguous_year_threshold = default
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
158
180
|
describe "removing formats" do
|
159
181
|
it "should remove format from format array" do
|
160
182
|
formats.remove_formats(:time, 'h.nn_ampm')
|
data/spec/ginger_scenarios.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
data/spec/validator_spec.rb
CHANGED
@@ -506,12 +506,6 @@ describe ValidatesTimeliness::Validator do
|
|
506
506
|
configure_validator(:type => :date, :before => before)
|
507
507
|
validator.send(:interpolation_values, :before, before.to_date).should == {:restriction => before}
|
508
508
|
end
|
509
|
-
|
510
|
-
it "should return empty hash if no interpolation keys are in message" do
|
511
|
-
before = '1900-01-01'
|
512
|
-
configure_validator(:type => :date, :before => before, :before_message => 'too late')
|
513
|
-
validator.send(:interpolation_values, :before, before.to_date).should be_empty
|
514
|
-
end
|
515
509
|
else
|
516
510
|
it "should return array of interpolation values" do
|
517
511
|
before = '1900-01-01'
|
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: 2.
|
4
|
+
version: 2.1.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-06-20 00:00:00 +10:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,54 +30,40 @@ files:
|
|
30
30
|
- Rakefile
|
31
31
|
- TODO
|
32
32
|
- CHANGELOG
|
33
|
-
- lib/validates_timeliness
|
34
|
-
- lib/validates_timeliness/core_ext
|
35
|
-
- lib/validates_timeliness/core_ext/date.rb
|
36
|
-
- lib/validates_timeliness/core_ext/date_time.rb
|
37
|
-
- lib/validates_timeliness/core_ext/time.rb
|
38
|
-
- lib/validates_timeliness/action_view
|
39
|
-
- lib/validates_timeliness/action_view/instance_tag.rb
|
40
|
-
- lib/validates_timeliness/locale
|
41
|
-
- lib/validates_timeliness/locale/en.yml
|
42
|
-
- lib/validates_timeliness/validation_methods.rb
|
43
|
-
- lib/validates_timeliness/active_record
|
44
|
-
- lib/validates_timeliness/active_record/attribute_methods.rb
|
45
33
|
- lib/validates_timeliness/active_record/multiparameter_attributes.rb
|
34
|
+
- lib/validates_timeliness/active_record/attribute_methods.rb
|
46
35
|
- lib/validates_timeliness/parser.rb
|
47
|
-
- lib/validates_timeliness/
|
36
|
+
- lib/validates_timeliness/core_ext/date.rb
|
37
|
+
- lib/validates_timeliness/core_ext/time.rb
|
38
|
+
- lib/validates_timeliness/core_ext/date_time.rb
|
48
39
|
- lib/validates_timeliness/validator.rb
|
49
|
-
- lib/validates_timeliness/
|
50
|
-
- lib/validates_timeliness/
|
51
|
-
- lib/validates_timeliness/spec/rails/matchers
|
40
|
+
- lib/validates_timeliness/validation_methods.rb
|
41
|
+
- lib/validates_timeliness/locale/en.yml
|
52
42
|
- lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb
|
43
|
+
- lib/validates_timeliness/action_view/instance_tag.rb
|
44
|
+
- lib/validates_timeliness/formats.rb
|
53
45
|
- lib/validates_timeliness.rb
|
54
|
-
- spec/
|
46
|
+
- spec/active_record/multiparameter_attributes_spec.rb
|
47
|
+
- spec/active_record/attribute_methods_spec.rb
|
48
|
+
- spec/formats_spec.rb
|
49
|
+
- spec/parser_spec.rb
|
55
50
|
- spec/core_ext/dummy_time_spec.rb
|
56
|
-
- spec/validator_spec.rb
|
57
|
-
- spec/action_view
|
58
|
-
- spec/action_view/instance_tag_spec.rb
|
59
|
-
- spec/ginger_scenarios.rb
|
60
51
|
- spec/spec_helper.rb
|
61
|
-
- spec/
|
62
|
-
- spec/active_record
|
63
|
-
- spec/active_record/attribute_methods_spec.rb
|
64
|
-
- spec/active_record/multiparameter_attributes_spec.rb
|
65
|
-
- spec/time_travel
|
66
|
-
- spec/time_travel/time_travel.rb
|
52
|
+
- spec/ginger_scenarios.rb
|
67
53
|
- spec/time_travel/time_extensions.rb
|
54
|
+
- spec/time_travel/time_travel.rb
|
68
55
|
- spec/time_travel/MIT-LICENSE
|
69
|
-
- spec/parser_spec.rb
|
70
|
-
- spec/spec
|
71
|
-
- spec/spec/rails
|
72
|
-
- spec/spec/rails/matchers
|
73
56
|
- spec/spec/rails/matchers/validate_timeliness_spec.rb
|
74
|
-
- spec/
|
75
|
-
- spec/
|
76
|
-
- spec/resources/sqlite_patch.rb
|
57
|
+
- spec/validator_spec.rb
|
58
|
+
- spec/action_view/instance_tag_spec.rb
|
77
59
|
- spec/resources/schema.rb
|
78
60
|
- spec/resources/application.rb
|
61
|
+
- spec/resources/person.rb
|
62
|
+
- spec/resources/sqlite_patch.rb
|
79
63
|
has_rdoc: true
|
80
64
|
homepage: http://github.com/adzap/validates_timeliness
|
65
|
+
licenses: []
|
66
|
+
|
81
67
|
post_install_message:
|
82
68
|
rdoc_options: []
|
83
69
|
|
@@ -98,9 +84,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
84
|
requirements: []
|
99
85
|
|
100
86
|
rubyforge_project: validatestime
|
101
|
-
rubygems_version: 1.3.
|
87
|
+
rubygems_version: 1.3.3
|
102
88
|
signing_key:
|
103
|
-
specification_version:
|
89
|
+
specification_version: 3
|
104
90
|
summary: Date and time validation plugin for Rails 2.x which allows custom formats
|
105
91
|
test_files: []
|
106
92
|
|