adzap-validates_timeliness 1.1.2 → 1.1.3

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 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
@@ -10,16 +10,14 @@ module ValidatesTimeliness
10
10
  # string values.
11
11
  #
12
12
  class Formats
13
- cattr_accessor :time_formats
14
- cattr_accessor :date_formats
15
- cattr_accessor :datetime_formats
16
-
17
- cattr_accessor :time_expressions
18
- cattr_accessor :date_expressions
19
- cattr_accessor :datetime_expressions
20
-
21
- cattr_accessor :format_tokens
22
- cattr_accessor :format_proc_args
13
+ cattr_accessor :time_formats,
14
+ :date_formats,
15
+ :datetime_formats,
16
+ :time_expressions,
17
+ :date_expressions,
18
+ :datetime_expressions,
19
+ :format_tokens,
20
+ :format_proc_args
23
21
 
24
22
  # Format tokens:
25
23
  # y = year
@@ -139,13 +137,13 @@ module ValidatesTimeliness
139
137
  # should just be the arg name.
140
138
  #
141
139
  @@format_proc_args = {
142
- :year => [0, 'y', 'unambiguous_year(y)'],
143
- :month => [1, 'm', 'month_index(m)'],
144
- :day => [2, 'd', 'd'],
145
- :hour => [3, 'h', 'full_hour(h,md)'],
146
- :min => [4, 'n', 'n'],
147
- :sec => [5, 's', 's'],
148
- :usec => [6, 'u', 'microseconds(u)'],
140
+ :year => [0, 'y', 'unambiguous_year(y)'],
141
+ :month => [1, 'm', 'month_index(m)'],
142
+ :day => [2, 'd', 'd'],
143
+ :hour => [3, 'h', 'full_hour(h,md)'],
144
+ :min => [4, 'n', 'n'],
145
+ :sec => [5, 's', 's'],
146
+ :usec => [6, 'u', 'microseconds(u)'],
149
147
  :meridian => [nil, 'md', nil]
150
148
  }
151
149
 
@@ -163,17 +161,18 @@ module ValidatesTimeliness
163
161
  # Returns 7 part time array.
164
162
  def parse(string, type, strict=true)
165
163
  return string unless string.is_a?(String)
166
-
167
- expressions = expression_set(type, string)
168
- time_array = nil
169
- expressions.each do |(regexp, processor)|
170
- regexp = strict || type == :datetime ? /\A#{regexp}\Z/ : (type == :date ? /\A#{regexp}/ : /#{regexp}\Z/)
171
- if matches = regexp.match(string.strip)
172
- time_array = processor.call(*matches[1..7])
173
- 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/
174
172
  end
173
+ matches = full.match(string.strip)
175
174
  end
176
- return time_array
175
+ processor.call(*matches[1..7]) if matches
177
176
  end
178
177
 
179
178
  # Delete formats of specified type. Error raised if format not found.
@@ -223,7 +222,7 @@ module ValidatesTimeliness
223
222
  def format_expression_generator(string_format)
224
223
  regexp = string_format.dup
225
224
  order = {}
226
- regexp.gsub!(/([\.\\])/, '\\\\\1') # escapes dots and backslashes ]/
225
+ regexp.gsub!(/([\.\\])/, '\\\\\1') # escapes dots and backslashes
227
226
 
228
227
  format_tokens.each do |token|
229
228
  token_name = token.keys.first
@@ -260,7 +259,7 @@ module ValidatesTimeliness
260
259
  end
261
260
 
262
261
  def compile_formats(formats)
263
- formats.collect { |format| regexp, format_proc = format_expression_generator(format) }
262
+ formats.map { |format| regexp, format_proc = format_expression_generator(format) }
264
263
  end
265
264
 
266
265
  # Pick expression set and combine date and datetimes for
@@ -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: adzap-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 -08:00
12
+ date: 2009-01-13 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15