validates_timeliness 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,6 @@
1
+ = 1.1.3 [2009-01-13]
2
+ - Fixed bug where time and date attributes still being parsed on read using Rails default parser [reported by Brad (pvjq)]
3
+
1
4
  = 1.1.2 [2009-01-12]
2
5
  - Fixed bugs
3
6
  - matcher failing for custom error message without interpolation keys using I18n
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 = "1.1.2"
8
+ GEM_VERSION = "1.1.3"
9
9
  AUTHOR = "Adam Meehan"
10
10
  EMAIL = "adam.meehan@gmail.com"
11
11
  HOMEPAGE = "http://github.com/adzap/validates_timeliness"
@@ -14,24 +14,10 @@ module ValidatesTimeliness
14
14
  # will not be in the attribute cache on first read so will be considered in default
15
15
  # timezone and converted to local time. It is then stored back in the attributes
16
16
  # hash and cached to avoid the need for any subsequent differentiation.
17
- #
18
- # The wholesale replacement of the Rails time type casting is not done to
19
- # preserve the quickest conversion for timestamp columns and also any value
20
- # which is never changed during the life of the record object.
21
17
  module AttributeMethods
22
18
 
23
19
  def self.included(base)
24
20
  base.extend ClassMethods
25
-
26
- if Rails::VERSION::STRING < '2.1'
27
- base.class_eval do
28
- class << self
29
- def create_time_zone_conversion_attribute?(name, column)
30
- false
31
- end
32
- end
33
- end
34
- end
35
21
  end
36
22
 
37
23
  # Adds check for cached date/time attributes which have been type cast already
@@ -57,25 +43,19 @@ module ValidatesTimeliness
57
43
  end
58
44
  end
59
45
 
60
- # Writes attribute value by storing raw value in attributes hash,
61
- # then convert it with parser and cache it.
62
- #
63
46
  # If Rails dirty attributes is enabled then the value is added to
64
47
  # changed attributes if changed. Can't use the default dirty checking
65
48
  # implementation as it chains the write_attribute method which deletes
66
49
  # the attribute from the cache.
67
- def write_date_time_attribute(attr_name, value)
68
- column = column_for_attribute(attr_name)
50
+ def write_date_time_attribute(attr_name, value, type, time_zone_aware)
69
51
  old = read_attribute(attr_name) if defined?(::ActiveRecord::Dirty)
70
- new = self.class.parse_date_time(value, column.type)
52
+ new = self.class.parse_date_time(value, type)
71
53
 
72
- unless column.type == :date || new.nil?
54
+ unless type == :date || new.nil?
73
55
  new = new.to_time rescue new
74
56
  end
75
57
 
76
- if self.class.send(:create_time_zone_conversion_attribute?, attr_name, column)
77
- new = new.in_time_zone rescue nil
78
- end
58
+ new = new.in_time_zone if new && time_zone_aware
79
59
  @attributes_cache[attr_name] = new
80
60
 
81
61
  if defined?(::ActiveRecord::Dirty) && !changed_attributes.include?(attr_name) && old != new
@@ -84,6 +64,24 @@ module ValidatesTimeliness
84
64
  @attributes[attr_name] = value
85
65
  end
86
66
 
67
+ # If reloading then check if cached, which means its in local time.
68
+ # If local, convert with parser as local timezone, otherwise use
69
+ # read_attribute method for quick default type cast of values from
70
+ # database using default timezone.
71
+ def read_date_time_attribute(attr_name, type, time_zone_aware, reload = false)
72
+ cached = @attributes_cache[attr_name]
73
+ return cached if @attributes_cache.has_key?(attr_name) && !reload
74
+
75
+ if @attributes_cache.has_key?(attr_name)
76
+ time = read_attribute_before_type_cast(attr_name)
77
+ time = self.class.parse_date_time(date, type)
78
+ else
79
+ time = read_attribute(attr_name)
80
+ @attributes[attr_name] = time && time_zone_aware ? time.in_time_zone : time
81
+ end
82
+ @attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
83
+ end
84
+
87
85
  module ClassMethods
88
86
 
89
87
  # Override AR method to define attribute reader and writer method for
@@ -94,8 +92,9 @@ module ValidatesTimeliness
94
92
  unless instance_method_already_implemented?(name)
95
93
  if self.serialized_attributes[name]
96
94
  define_read_method_for_serialized_attribute(name)
97
- elsif create_time_zone_conversion_attribute?(name, column)
98
- define_read_method_for_time_zone_conversion(name)
95
+ elsif [:date, :time, :datetime].include?(column.type)
96
+ time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
97
+ define_read_method_for_dates_and_times(name, column.type, time_zone_aware)
99
98
  else
100
99
  define_read_method(name.to_sym, name, column)
101
100
  end
@@ -103,7 +102,8 @@ module ValidatesTimeliness
103
102
 
104
103
  unless instance_method_already_implemented?("#{name}=")
105
104
  if [:date, :time, :datetime].include?(column.type)
106
- define_write_method_for_dates_and_times(name)
105
+ time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
106
+ define_write_method_for_dates_and_times(name, column.type, time_zone_aware)
107
107
  else
108
108
  define_write_method(name.to_sym)
109
109
  end
@@ -116,32 +116,19 @@ module ValidatesTimeliness
116
116
  end
117
117
 
118
118
  # Define write method for date, time and datetime columns
119
- def define_write_method_for_dates_and_times(attr_name)
119
+ def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
120
120
  method_body = <<-EOV
121
121
  def #{attr_name}=(value)
122
- write_date_time_attribute('#{attr_name}', value)
122
+ write_date_time_attribute('#{attr_name}', value, #{type.inspect}, #{time_zone_aware})
123
123
  end
124
124
  EOV
125
125
  evaluate_attribute_method attr_name, method_body, "#{attr_name}="
126
126
  end
127
127
 
128
- # Define time attribute reader. If reloading then check if cached,
129
- # which means its in local time. If local, convert with parser as local
130
- # timezone, otherwise use read_attribute method for quick default type
131
- # cast of values from database using default timezone.
132
- def define_read_method_for_time_zone_conversion(attr_name)
128
+ def define_read_method_for_dates_and_times(attr_name, type, time_zone_aware)
133
129
  method_body = <<-EOV
134
130
  def #{attr_name}(reload = false)
135
- cached = @attributes_cache['#{attr_name}']
136
- return cached if @attributes_cache.has_key?('#{attr_name}') && !reload
137
- if @attributes_cache.has_key?('#{attr_name}')
138
- time = read_attribute_before_type_cast('#{attr_name}')
139
- time = self.class.parse_date_time(date, :datetime)
140
- else
141
- time = read_attribute('#{attr_name}')
142
- @attributes['#{attr_name}'] = time.in_time_zone rescue nil
143
- end
144
- @attributes_cache['#{attr_name}'] = time.in_time_zone rescue nil
131
+ read_date_time_attribute('#{attr_name}', #{type.inspect}, #{time_zone_aware}, reload)
145
132
  end
146
133
  EOV
147
134
  evaluate_attribute_method attr_name, method_body
@@ -161,18 +161,18 @@ module ValidatesTimeliness
161
161
  # Returns 7 part time array.
162
162
  def parse(string, type, strict=true)
163
163
  return string unless string.is_a?(String)
164
-
165
- expressions = expression_set(type, string)
166
- # TODO cleanup using select
167
- time_array = nil
168
- expressions.each do |(regexp, processor)|
169
- regexp = strict || type == :datetime ? /\A#{regexp}\Z/ : (type == :date ? /\A#{regexp}/ : /#{regexp}\Z/)
170
- if matches = regexp.match(string.strip)
171
- time_array = processor.call(*matches[1..7])
172
- break
164
+
165
+ matches = nil
166
+ exp, processor = expression_set(type, string).find do |regexp, proc|
167
+ full = /\A#{regexp}\Z/ if strict
168
+ full ||= case type
169
+ when :datetime then /\A#{regexp}\Z/
170
+ when :date then /\A#{regexp}/
171
+ else /#{regexp}\Z/
173
172
  end
173
+ matches = full.match(string.strip)
174
174
  end
175
- time_array
175
+ processor.call(*matches[1..7]) if matches
176
176
  end
177
177
 
178
178
  # Delete formats of specified type. Error raised if format not found.
@@ -299,22 +299,6 @@ module ValidatesTimeliness
299
299
  return month.to_i if month.to_i.nonzero?
300
300
  Date::ABBR_MONTHNAMES.index(month.capitalize) || Date::MONTHNAMES.index(month.capitalize)
301
301
  end
302
-
303
- def month_names
304
- @@month_names = if defined?(I18n)
305
- I18n('dates.months')
306
- else
307
- Date::MONTHNAMES
308
- end
309
- end
310
-
311
- def abbreviated_month_names
312
- @@abbreviated_month_names = if defined?(I18n)
313
- I18n('dates.months')
314
- else
315
- Date::ABBR_MONTHNAMES
316
- end
317
- end
318
302
 
319
303
  def microseconds(usec)
320
304
  (".#{usec}".to_f * 1_000_000).to_i
@@ -23,6 +23,24 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
23
23
  @person.birth_date_and_time = "2000-01-01 12:00"
24
24
  end
25
25
 
26
+ it "should call read_date_time_attribute when date attribute is retrieved" do
27
+ @person.should_receive(:read_date_time_attribute)
28
+ @person.birth_date = "2000-01-01"
29
+ @person.birth_date
30
+ end
31
+
32
+ it "should call read_date_time_attribute when time attribute is retrieved" do
33
+ @person.should_receive(:read_date_time_attribute)
34
+ @person.birth_time = "12:00"
35
+ @person.birth_time
36
+ end
37
+
38
+ it "should call rea_date_time_attribute when datetime attribute is retrieved" do
39
+ @person.should_receive(:read_date_time_attribute)
40
+ @person.birth_date_and_time = "2000-01-01 12:00"
41
+ @person.birth_date_and_time
42
+ end
43
+
26
44
  it "should call parser on write for datetime attribute" do
27
45
  @person.class.should_receive(:parse_date_time).once
28
46
  @person.birth_date_and_time = "2000-01-01 02:03:04"
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: 1.1.2
4
+ version: 1.1.3
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-01-12 00:00:00 +11:00
12
+ date: 2009-01-13 00:00:00 +11:00
13
13
  default_executable:
14
14
  dependencies: []
15
15