validates_timeliness 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +43 -0
- data/LICENSE +20 -0
- data/README.rdoc +320 -0
- data/Rakefile +58 -0
- data/TODO +8 -0
- data/lib/validates_timeliness.rb +66 -0
- data/lib/validates_timeliness/action_view/instance_tag.rb +45 -0
- data/lib/validates_timeliness/active_record/attribute_methods.rb +157 -0
- data/lib/validates_timeliness/active_record/multiparameter_attributes.rb +64 -0
- data/lib/validates_timeliness/core_ext/date.rb +13 -0
- data/lib/validates_timeliness/core_ext/date_time.rb +13 -0
- data/lib/validates_timeliness/core_ext/time.rb +13 -0
- data/lib/validates_timeliness/formats.rb +310 -0
- data/lib/validates_timeliness/locale/en.yml +11 -0
- data/lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb +121 -0
- data/lib/validates_timeliness/validation_methods.rb +82 -0
- data/lib/validates_timeliness/validator.rb +120 -0
- data/spec/action_view/instance_tag_spec.rb +38 -0
- data/spec/active_record/attribute_methods_spec.rb +204 -0
- data/spec/active_record/multiparameter_attributes_spec.rb +48 -0
- data/spec/core_ext/dummy_time_spec.rb +31 -0
- data/spec/formats_spec.rb +274 -0
- data/spec/ginger_scenarios.rb +19 -0
- data/spec/resources/application.rb +2 -0
- data/spec/resources/person.rb +3 -0
- data/spec/resources/schema.rb +10 -0
- data/spec/resources/sqlite_patch.rb +19 -0
- data/spec/spec/rails/matchers/validate_timeliness_spec.rb +178 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/time_travel/MIT-LICENSE +20 -0
- data/spec/time_travel/time_extensions.rb +33 -0
- data/spec/time_travel/time_travel.rb +12 -0
- data/spec/validation_methods_spec.rb +61 -0
- data/spec/validator_spec.rb +438 -0
- metadata +105 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
en:
|
2
|
+
activerecord:
|
3
|
+
errors:
|
4
|
+
messages:
|
5
|
+
invalid_date: "is not a valid date"
|
6
|
+
invalid_time: "is not a valid time"
|
7
|
+
invalid_datetime: "is not a valid datetime"
|
8
|
+
before: "must be before {{restriction}}"
|
9
|
+
on_or_before: "must be on or before {{restriction}}"
|
10
|
+
after: "must be after {{restriction}}"
|
11
|
+
on_or_after: "must be on or after {{restriction}}"
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Spec
|
2
|
+
module Rails
|
3
|
+
module Matchers
|
4
|
+
class ValidateTimeliness
|
5
|
+
cattr_accessor :test_values
|
6
|
+
|
7
|
+
@@test_values = {
|
8
|
+
:date => {:pass => '2000-01-01', :fail => '2000-01-32'},
|
9
|
+
:time => {:pass => '12:00', :fail => '25:00'},
|
10
|
+
:datetime => {:pass => '2000-01-01 00:00:00', :fail => '2000-01-32 00:00:00'}
|
11
|
+
}
|
12
|
+
|
13
|
+
def initialize(attribute, options)
|
14
|
+
@expected, @options = attribute, options
|
15
|
+
@validator = ValidatesTimeliness::Validator.new(options)
|
16
|
+
compile_error_messages
|
17
|
+
end
|
18
|
+
|
19
|
+
def compile_error_messages
|
20
|
+
messages = validator.send(:error_messages)
|
21
|
+
@messages = messages.inject({}) {|h, (k, v)| h[k] = v.gsub(/ (\%s|\{\{\w*\}\})/, ''); h }
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches?(record)
|
25
|
+
@record = record
|
26
|
+
type = options[:type]
|
27
|
+
|
28
|
+
invalid_value = @@test_values[type][:fail]
|
29
|
+
valid_value = parse_and_cast(@@test_values[type][:pass])
|
30
|
+
valid = error_matching(invalid_value, /#{messages["invalid_#{type}".to_sym]}/) &&
|
31
|
+
no_error_matching(valid_value, /#{messages["invalid_#{type}".to_sym]}/)
|
32
|
+
|
33
|
+
valid = test_option(:before, :-) if options[:before] && valid
|
34
|
+
valid = test_option(:after, :+) if options[:after] && valid
|
35
|
+
|
36
|
+
valid = test_option(:on_or_before, :+, :modify_on => :invalid) if options[:on_or_before] && valid
|
37
|
+
valid = test_option(:on_or_after, :-, :modify_on => :invalid) if options[:on_or_after] && valid
|
38
|
+
|
39
|
+
return valid
|
40
|
+
end
|
41
|
+
|
42
|
+
def failure_message
|
43
|
+
"expected model to validate #{options[:type]} attribute #{expected.inspect} with #{last_failure}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def negative_failure_message
|
47
|
+
"expected not to validate #{options[:type]} attribute #{expected.inspect}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
"have validated #{options[:type]} attribute #{expected.inspect}"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
attr_reader :actual, :expected, :record, :options, :messages, :last_failure, :validator
|
56
|
+
|
57
|
+
def test_option(option, modifier, settings={})
|
58
|
+
settings.reverse_merge!(:modify_on => :valid)
|
59
|
+
boundary = parse_and_cast(options[option])
|
60
|
+
|
61
|
+
valid_value, invalid_value = if settings[:modify_on] == :valid
|
62
|
+
[ boundary.send(modifier, 1), boundary ]
|
63
|
+
else
|
64
|
+
[ boundary, boundary.send(modifier, 1) ]
|
65
|
+
end
|
66
|
+
|
67
|
+
message = messages[option]
|
68
|
+
error_matching(invalid_value, /#{message}/) &&
|
69
|
+
no_error_matching(valid_value, /#{message}/)
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_and_cast(value)
|
73
|
+
value = validator.send(:restriction_value, value, record)
|
74
|
+
validator.send(:type_cast_value, value)
|
75
|
+
end
|
76
|
+
|
77
|
+
def error_matching(value, match)
|
78
|
+
record.send("#{expected}=", value)
|
79
|
+
record.valid?
|
80
|
+
errors = record.errors.on(expected)
|
81
|
+
pass = [ errors ].flatten.any? {|error| match === error }
|
82
|
+
@last_failure = "error matching #{match.inspect} when value is #{format_value(value)}" unless pass
|
83
|
+
pass
|
84
|
+
end
|
85
|
+
|
86
|
+
def no_error_matching(value, match)
|
87
|
+
pass = !error_matching(value, match)
|
88
|
+
@last_failure = "no error matching #{match.inspect} when value is #{format_value(value)}" unless pass
|
89
|
+
pass
|
90
|
+
end
|
91
|
+
|
92
|
+
def format_value(value)
|
93
|
+
return value if value.is_a?(String)
|
94
|
+
value.strftime(ValidatesTimeliness::Validator.error_value_formats[options[:type]])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_date(attribute, options={})
|
99
|
+
options[:type] = :date
|
100
|
+
validate_timeliness_of(attribute, options)
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate_time(attribute, options={})
|
104
|
+
options[:type] = :time
|
105
|
+
validate_timeliness_of(attribute, options)
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_datetime(attribute, options={})
|
109
|
+
options[:type] = :datetime
|
110
|
+
validate_timeliness_of(attribute, options)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def validate_timeliness_of(attribute, options={})
|
116
|
+
ValidateTimeliness.new(attribute, options)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
module ValidationMethods
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
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
|
+
def validates_time(*attr_names)
|
32
|
+
configuration = attr_names.extract_options!
|
33
|
+
configuration[:type] = :time
|
34
|
+
validates_timeliness_of(attr_names, configuration)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validates_date(*attr_names)
|
38
|
+
configuration = attr_names.extract_options!
|
39
|
+
configuration[:type] = :date
|
40
|
+
validates_timeliness_of(attr_names, configuration)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validates_datetime(*attr_names)
|
44
|
+
configuration = attr_names.extract_options!
|
45
|
+
configuration[:type] = :datetime
|
46
|
+
validates_timeliness_of(attr_names, configuration)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def validates_timeliness_of(attr_names, configuration)
|
52
|
+
validator = ValidatesTimeliness::Validator.new(configuration)
|
53
|
+
|
54
|
+
# bypass handling of allow_nil and allow_blank to validate raw value
|
55
|
+
configuration.delete(:allow_nil)
|
56
|
+
configuration.delete(:allow_blank)
|
57
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
58
|
+
validator.call(record, attr_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
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
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
ActiveRecord::Base.send(:include, ValidatesTimeliness::ValidationMethods)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module ValidatesTimeliness
|
2
|
+
|
3
|
+
class Validator
|
4
|
+
cattr_accessor :ignore_restriction_errors
|
5
|
+
cattr_accessor :error_value_formats
|
6
|
+
|
7
|
+
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
|
+
|
14
|
+
attr_reader :configuration, :type
|
15
|
+
|
16
|
+
def initialize(configuration)
|
17
|
+
defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false }
|
18
|
+
@configuration = defaults.merge(configuration)
|
19
|
+
@type = @configuration.delete(:type)
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(record, attr_name)
|
23
|
+
value = record.send(attr_name)
|
24
|
+
value = record.class.parse_date_time(value, type, false) if value.is_a?(String)
|
25
|
+
raw_value = raw_value(record, attr_name)
|
26
|
+
|
27
|
+
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
28
|
+
|
29
|
+
add_error(record, attr_name, :blank) and return if raw_value.blank?
|
30
|
+
|
31
|
+
add_error(record, attr_name, "invalid_#{type}".to_sym) and return unless value
|
32
|
+
|
33
|
+
validate_restrictions(record, attr_name, value)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def raw_value(record, attr_name)
|
39
|
+
record.send("#{attr_name}_before_type_cast")
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_restrictions(record, attr_name, value)
|
43
|
+
restriction_methods = {:before => '<', :after => '>', :on_or_before => '<=', :on_or_after => '>='}
|
44
|
+
|
45
|
+
display = self.class.error_value_formats[type]
|
46
|
+
|
47
|
+
value = type_cast_value(value)
|
48
|
+
|
49
|
+
restriction_methods.each do |option, method|
|
50
|
+
next unless restriction = configuration[option]
|
51
|
+
begin
|
52
|
+
compare = restriction_value(restriction, record)
|
53
|
+
next if compare.nil?
|
54
|
+
compare = type_cast_value(compare)
|
55
|
+
|
56
|
+
unless value.send(method, compare)
|
57
|
+
add_error(record, attr_name, option, :restriction => compare.strftime(display))
|
58
|
+
end
|
59
|
+
rescue
|
60
|
+
unless self.class.ignore_restriction_errors
|
61
|
+
add_error(record, attr_name, "restriction '#{option}' value was invalid")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_error(record, attr_name, message, interpolate={})
|
68
|
+
if Rails::VERSION::STRING < '2.2'
|
69
|
+
message = error_messages[message] if message.is_a?(Symbol)
|
70
|
+
message = message % interpolate.values unless interpolate.empty?
|
71
|
+
record.errors.add(attr_name, message)
|
72
|
+
else
|
73
|
+
# use i18n support in AR for message or use custom message passed to validation method
|
74
|
+
custom = custom_error_messages[message]
|
75
|
+
record.errors.add(attr_name, custom || message, interpolate)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def error_messages
|
80
|
+
return @error_messages if defined?(@error_messages)
|
81
|
+
@error_messages = ValidatesTimeliness.default_error_messages.merge(custom_error_messages)
|
82
|
+
end
|
83
|
+
|
84
|
+
def custom_error_messages
|
85
|
+
return @custom_error_messages if defined?(@custom_error_messages)
|
86
|
+
@custom_error_messages = configuration.inject({}) {|h, (k, v)| h[$1.to_sym] = v if k.to_s =~ /(.*)_message$/;h }
|
87
|
+
end
|
88
|
+
|
89
|
+
def restriction_value(restriction, record)
|
90
|
+
case restriction
|
91
|
+
when Time, Date, DateTime
|
92
|
+
restriction
|
93
|
+
when Symbol
|
94
|
+
restriction_value(record.send(restriction), record)
|
95
|
+
when Proc
|
96
|
+
restriction_value(restriction.call(record), record)
|
97
|
+
else
|
98
|
+
record.class.parse_date_time(restriction, type, false)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def type_cast_value(value)
|
103
|
+
case type
|
104
|
+
when :time
|
105
|
+
value.to_dummy_time
|
106
|
+
when :date
|
107
|
+
value.to_date
|
108
|
+
when :datetime
|
109
|
+
if value.is_a?(DateTime) || value.is_a?(Time)
|
110
|
+
value.to_time
|
111
|
+
else
|
112
|
+
value.to_time(ValidatesTimelines.default_timezone)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ValidatesTimeliness::ActionView::InstanceTag, :type => :helper do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@person = Person.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should display invalid datetime as datetime_select values" do
|
10
|
+
@person.birth_date_and_time = "2008-02-30 12:00:22"
|
11
|
+
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
|
12
|
+
|
13
|
+
output.should have_tag('select[id=person_birth_date_and_time_1i]') do
|
14
|
+
with_tag('option[selected=selected]', '2008')
|
15
|
+
end
|
16
|
+
output.should have_tag('select[id=person_birth_date_and_time_2i]') do
|
17
|
+
with_tag('option[selected=selected]', 'February')
|
18
|
+
end
|
19
|
+
output.should have_tag('select[id=person_birth_date_and_time_3i]') do
|
20
|
+
with_tag('option[selected=selected]', '30')
|
21
|
+
end
|
22
|
+
output.should have_tag('select[id=person_birth_date_and_time_4i]') do
|
23
|
+
with_tag('option[selected=selected]', '12')
|
24
|
+
end
|
25
|
+
output.should have_tag('select[id=person_birth_date_and_time_5i]') do
|
26
|
+
with_tag('option[selected=selected]', '00')
|
27
|
+
end
|
28
|
+
output.should have_tag('select[id=person_birth_date_and_time_6i]') do
|
29
|
+
with_tag('option[selected=selected]', '22')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should display datetime_select when datetime value is nil" do
|
34
|
+
@person.birth_date_and_time = nil
|
35
|
+
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
|
36
|
+
output.should have_tag('select', 6)
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
4
|
+
include ValidatesTimeliness::ActiveRecord::AttributeMethods
|
5
|
+
include ValidatesTimeliness::ValidationMethods
|
6
|
+
|
7
|
+
before do
|
8
|
+
@person = Person.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should call write_date_time_attribute when date attribute assigned value" do
|
12
|
+
@person.should_receive(:write_date_time_attribute)
|
13
|
+
@person.birth_date = "2000-01-01"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should call write_date_time_attribute when time attribute assigned value" do
|
17
|
+
@person.should_receive(:write_date_time_attribute)
|
18
|
+
@person.birth_time = "12:00"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should call write_date_time_attribute when datetime attribute assigned value" do
|
22
|
+
@person.should_receive(:write_date_time_attribute)
|
23
|
+
@person.birth_date_and_time = "2000-01-01 12:00"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should call parser on write for datetime attribute" do
|
27
|
+
@person.class.should_receive(:parse_date_time).once
|
28
|
+
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should call parser on write for date attribute" do
|
32
|
+
@person.class.should_receive(:parse_date_time).once
|
33
|
+
@person.birth_date = "2000-01-01"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should call parser on write for time attribute" do
|
37
|
+
@person.class.should_receive(:parse_date_time).once
|
38
|
+
@person.birth_time = "12:00"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return raw string value for attribute_before_type_cast when written as string" do
|
42
|
+
time_string = "2000-01-01 02:03:04"
|
43
|
+
@person.birth_date_and_time = time_string
|
44
|
+
@person.birth_date_and_time_before_type_cast.should == time_string
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return Time object for attribute_before_type_cast when written as Time" do
|
48
|
+
@person.birth_date_and_time = Time.mktime(2000, 1, 1, 2, 3, 4)
|
49
|
+
@person.birth_date_and_time_before_type_cast.should be_kind_of(Time)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should return Time object for datetime attribute read method when assigned Time object" do
|
53
|
+
@person.birth_date_and_time = Time.now
|
54
|
+
@person.birth_date_and_time.should be_kind_of(Time)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should return Time object for datetime attribute read method when assigned string" do
|
58
|
+
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
59
|
+
@person.birth_date_and_time.should be_kind_of(Time)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should return Date object for date attribute read method when assigned Date object" do
|
63
|
+
@person.birth_date = Date.today
|
64
|
+
@person.birth_date.should be_kind_of(Date)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return Date object for date attribute read method when assigned string" do
|
68
|
+
@person.birth_date = '2000-01-01'
|
69
|
+
@person.birth_date.should be_kind_of(Date)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should return nil when time is invalid" do
|
73
|
+
@person.birth_date_and_time = "2000-01-32 02:03:04"
|
74
|
+
@person.birth_date_and_time.should be_nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not save invalid date value to database" do
|
78
|
+
time_string = "2000-01-32 02:03:04"
|
79
|
+
@person = Person.new
|
80
|
+
@person.birth_date_and_time = time_string
|
81
|
+
@person.save
|
82
|
+
@person.reload
|
83
|
+
@person.birth_date_and_time_before_type_cast.should be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
unless RAILS_VER < '2.1'
|
87
|
+
it "should return stored time string as Time with correct timezone" do
|
88
|
+
Time.zone = 'Melbourne'
|
89
|
+
time_string = "2000-06-01 02:03:04"
|
90
|
+
@person.birth_date_and_time = time_string
|
91
|
+
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000'
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should return time object from database in correct timezone" do
|
95
|
+
Time.zone = 'Melbourne'
|
96
|
+
time_string = "2000-06-01 09:00:00"
|
97
|
+
@person = Person.new
|
98
|
+
@person.birth_date_and_time = time_string
|
99
|
+
@person.save
|
100
|
+
@person.reload
|
101
|
+
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000'
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "dirty attributes" do
|
105
|
+
|
106
|
+
it "should return true for attribute changed? when value updated" do
|
107
|
+
time_string = "2000-01-01 02:03:04"
|
108
|
+
@person.birth_date_and_time = time_string
|
109
|
+
@person.birth_date_and_time_changed?.should be_true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should show changes when time attribute changed from nil to Time object" do
|
113
|
+
time_string = "2000-01-01 02:03:04"
|
114
|
+
@person.birth_date_and_time = time_string
|
115
|
+
time = @person.birth_date_and_time
|
116
|
+
@person.changes.should == {"birth_date_and_time" => [nil, time]}
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should show changes when time attribute changed from Time object to nil" do
|
120
|
+
time_string = "2020-01-01 02:03:04"
|
121
|
+
@person.birth_date_and_time = time_string
|
122
|
+
@person.save false
|
123
|
+
@person.reload
|
124
|
+
time = @person.birth_date_and_time
|
125
|
+
@person.birth_date_and_time = nil
|
126
|
+
@person.changes.should == {"birth_date_and_time" => [time, nil]}
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should show no changes when assigned same value as Time object" do
|
130
|
+
time_string = "2020-01-01 02:03:04"
|
131
|
+
@person.birth_date_and_time = time_string
|
132
|
+
@person.save false
|
133
|
+
@person.reload
|
134
|
+
time = @person.birth_date_and_time
|
135
|
+
@person.birth_date_and_time = time
|
136
|
+
@person.changes.should == {}
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should show no changes when assigned same value as time string" 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
|
+
@person.birth_date_and_time = time_string
|
145
|
+
@person.changes.should == {}
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
else
|
150
|
+
|
151
|
+
it "should return time object from database in default timezone" do
|
152
|
+
ActiveRecord::Base.default_timezone = :utc
|
153
|
+
time_string = "2000-01-01 09:00:00"
|
154
|
+
@person = Person.new
|
155
|
+
@person.birth_date_and_time = time_string
|
156
|
+
@person.save
|
157
|
+
@person.reload
|
158
|
+
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should return same time object on repeat reads on existing object" do
|
164
|
+
Time.zone = 'Melbourne' unless RAILS_VER < '2.1'
|
165
|
+
time_string = "2000-01-01 09:00:00"
|
166
|
+
@person = Person.new
|
167
|
+
@person.birth_date_and_time = time_string
|
168
|
+
@person.save!
|
169
|
+
@person.reload
|
170
|
+
time = @person.birth_date_and_time
|
171
|
+
@person.birth_date_and_time.should == time
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return same date object on repeat reads on existing object" do
|
175
|
+
date_string = Date.today
|
176
|
+
@person = Person.new
|
177
|
+
@person.birth_date = date_string
|
178
|
+
@person.save!
|
179
|
+
@person.reload
|
180
|
+
date = @person.birth_date
|
181
|
+
@person.birth_date.should == date
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should return correct date value after new value assigned" do
|
185
|
+
today = Date.today
|
186
|
+
tomorrow = Date.today + 1.day
|
187
|
+
@person = Person.new
|
188
|
+
@person.birth_date = today
|
189
|
+
@person.birth_date.should == today
|
190
|
+
@person.birth_date = tomorrow
|
191
|
+
@person.birth_date.should == tomorrow
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should update date attribute on existing object" do
|
195
|
+
today = Date.today
|
196
|
+
tomorrow = Date.today + 1.day
|
197
|
+
@person = Person.create(:birth_date => today)
|
198
|
+
@person.birth_date = tomorrow
|
199
|
+
@person.save!
|
200
|
+
@person.reload
|
201
|
+
@person.birth_date.should == tomorrow
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|