validates_timeliness 2.0.0 → 2.1.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 +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
|
|