Compare commits

...

14 Commits
2.0.0 ... 2.1.0

Author SHA1 Message Date
Adam Meehan
14c55e3292 version 2.1 for real 2009-06-20 22:55:59 +10:00
Adam Meehan
75a3b2bd83 version 2.1 2009-06-20 22:30:08 +10:00
Adam Meehan
9b300e084b change to spec/autorun 2009-06-20 19:43:13 +10:00
Adam Meehan
d61dddfbc6 fix i18n locale load order to allow customisation
fix interpolation values for i18n
2009-06-19 12:06:36 +10:00
Adam Meehan
4ac4281c8b rename named local var 2009-06-07 08:28:20 +10:00
Adam Meehan
a56cf674b2 removed reload alias by attributes_cache for before_type_cast value 2009-06-07 08:21:46 +10:00
Adam Meehan
7f4d7b38d7 update changelog 2009-06-06 16:56:14 +10:00
Adam Meehan
34c0f25225 dramatically simplify the before_type_cast hack and remove read method override
simplified write method with no dirty attributes hackery
2009-06-06 16:37:06 +10:00
Adam Meehan
57c3fdca88 set RAILS_ENV spec_helper 2009-06-06 11:36:03 +10:00
Adam Meehan
805bb5d7fd added another datetime format 2009-06-06 11:17:35 +10:00
Adam Meehan
7aac14c874 added ambiguous year threshold setting 2009-06-06 11:16:19 +10:00
Adam Meehan
7c0c1afe1c Merge branch 'master' of git@github.com:adzap/validates_timeliness 2009-05-26 12:01:27 +10:00
Adam Meehan
5295a494e9 token typo 2009-05-26 12:00:49 +10:00
adzap
572d80d227 doc fix 2009-04-19 05:40:36 -07:00
14 changed files with 173 additions and 270 deletions

View File

@@ -1,3 +1,9 @@
= 2.1.0 [2009-06-20]
- Added ambiguous year threshold setting in Formats class to customize the threshold for 2 digit years (See README)
- Fixed interpolation values in custom error message for Rails 2.2+
- Fixed custom I18n local override of en locale
- Dramatically simplified ActiveRecord monkey patching and hackery
= 2.0.0 [2009-04-12] = 2.0.0 [2009-04-12]
- Error value formats are now specified in the i18n locale file instead of updating plugin hash. See OTHER CUSTOMISATION section in README. - Error value formats are now specified in the i18n locale file instead of updating plugin hash. See OTHER CUSTOMISATION section in README.
- Date/time select helper extension is disabled by default. To enable see DISPLAY INVALID VALUES IN DATE HELPERS section in README to enable. - Date/time select helper extension is disabled by default. To enable see DISPLAY INVALID VALUES IN DATE HELPERS section in README to enable.

View File

@@ -190,7 +190,7 @@ Here is what each format token means:
Special Cases: Special Cases:
yy = 2 or 4 digit year yy = 2 or 4 digit year
yyyyy = exactly 4 digit year yyyy = exactly 4 digit year
mmm = month long name (e.g. 'Jul' or 'July') mmm = month long name (e.g. 'Jul' or 'July')
ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday) ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
u = microseconds matches 1 to 3 digits u = microseconds matches 1 to 3 digits
@@ -206,7 +206,7 @@ To see all defined formats look in lib/validates_timeliness/formats.rb.
The perenial problem for non-US developers or applications not primarily for the The perenial problem for non-US developers or applications not primarily for the
US, is the US date format of m/d/yy. This is ambiguous with the European format US, is the US date format of m/d/yy. This is ambiguous with the European format
of d/my/yy. By default the plugin uses the US formats as this is the Ruby default of d/m/yy. By default the plugin uses the US formats as this is the Ruby default
when it does date interpretation, and is in keeping PoLS (principle of least when it does date interpretation, and is in keeping PoLS (principle of least
surprise). surprise).
@@ -250,6 +250,19 @@ Now a time of '59:30:23' will be interpreted as 11:30:59 pm. This option saves
you adding a new one and deleting an old one to get it to work. you adding a new one and deleting an old one to get it to work.
=== AMBIGUOUS YEAR THRESHOLD
When dealing with 2 digit year values, by default a year is interpreted as being
in the last century at or above 30. You can customize this however
ValidatesTimeliness::Formats.ambiguous_year_threshold = 20
Now you get:
year of 19 is considered 2019
year of 20 is considered 1920
=== TEMPORAL RESTRICTION ERRORS: === TEMPORAL RESTRICTION ERRORS:
When using the validation temporal restrictions there are times when the restriction When using the validation temporal restrictions there are times when the restriction

View File

@@ -5,7 +5,7 @@ require 'date'
require 'spec/rake/spectask' require 'spec/rake/spectask'
GEM = "validates_timeliness" GEM = "validates_timeliness"
GEM_VERSION = "2.0.0" GEM_VERSION = "2.1.0"
AUTHOR = "Adam Meehan" AUTHOR = "Adam Meehan"
EMAIL = "adam.meehan@gmail.com" EMAIL = "adam.meehan@gmail.com"
HOMEPAGE = "http://github.com/adzap/validates_timeliness" HOMEPAGE = "http://github.com/adzap/validates_timeliness"
@@ -34,7 +34,7 @@ task :default => :spec
desc "Run specs" desc "Run specs"
Spec::Rake::SpecTask.new do |t| Spec::Rake::SpecTask.new do |t|
t.spec_files = FileList['spec/**/*_spec.rb'] t.spec_files = FileList['spec/**/*_spec.rb']
t.spec_opts = %w(-fs --color) t.spec_opts = %w(--color)
end end

View File

@@ -31,7 +31,7 @@ module ValidatesTimeliness
def load_error_messages def load_error_messages
if defined?(I18n) if defined?(I18n)
I18n.load_path += [ LOCALE_PATH ] I18n.load_path.unshift(LOCALE_PATH)
I18n.reload! I18n.reload!
else else
defaults = YAML::load(IO.read(LOCALE_PATH))['en'] defaults = YAML::load(IO.read(LOCALE_PATH))['en']
@@ -45,6 +45,7 @@ module ValidatesTimeliness
def setup_for_rails def setup_for_rails
self.default_timezone = ::ActiveRecord::Base.default_timezone self.default_timezone = ::ActiveRecord::Base.default_timezone
self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
self.enable_active_record_datetime_parser!
load_error_messages load_error_messages
end end
end end

View File

@@ -1,124 +1,66 @@
module ValidatesTimeliness module ValidatesTimeliness
def self.enable_active_record_datetime_parser!
::ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)
end
module ActiveRecord module ActiveRecord
# Rails 2.1 removed the ability to retrieve the raw value of a time or datetime # Overrides write method for date, time and datetime columns
# attribute. The raw value is necessary to properly validate a string time or # to use plugin parser. Also adds mechanism to store value
# datetime value instead of the internal Rails type casting which is very limited # before type cast.
# and does not allow custom formats. These methods restore that ability while
# respecting the automatic timezone handling.
# #
# The automatic timezone handling sets the assigned attribute value to the current
# zone in Time.zone. To preserve this localised value and capture the raw value
# we cache the localised value on write and store the raw value in the attributes
# hash for later retrieval and possibly validation. Any value from the database
# will not be in the attribute cache on first read so will be considered in default
# timezone and converted to local time. It is then stored back in the attributes
# hash and cached to avoid the need for any subsequent differentiation.
module AttributeMethods module AttributeMethods
def self.included(base) def self.included(base)
base.extend ClassMethods base.extend ClassMethods
base.class_eval do base.class_eval do
alias_method_chain :read_attribute, :timeliness alias_method_chain :read_attribute_before_type_cast, :timeliness
class << self class << self
alias_method_chain :define_attribute_methods, :timeliness alias_method_chain :define_attribute_methods, :timeliness
end end
end end
end end
# Adds check for cached date/time attributes which have been type cast already
# and value can be used from cache. This prevents the raw date/time value from
# being type cast using default Rails type casting when writing values
# to the database.
def read_attribute_with_timeliness(attr_name)
attr_name = attr_name.to_s
if !(value = @attributes[attr_name]).nil?
column = column_for_attribute(attr_name)
if column && [:date, :time, :datetime].include?(column.type) && @attributes_cache.has_key?(attr_name)
return @attributes_cache[attr_name]
end
end
read_attribute_without_timeliness(attr_name)
end
# If Rails dirty attributes is enabled then the value is added to
# changed attributes if changed. Can't use the default dirty checking
# implementation as it chains the write_attribute method which deletes
# the attribute from the cache.
def write_date_time_attribute(attr_name, value, type, time_zone_aware) def write_date_time_attribute(attr_name, value, type, time_zone_aware)
new = ValidatesTimeliness::Parser.parse(value, type) @attributes_cache["_#{attr_name}_before_type_cast"] = value
if new && type != :date value = ValidatesTimeliness::Parser.parse(value, type)
new = new.to_time
new = new.in_time_zone if time_zone_aware if value && type != :date
value = value.to_time
value = value.in_time_zone if time_zone_aware
end end
if defined?(::ActiveRecord::Dirty) && !changed_attributes.include?(attr_name) write_attribute(attr_name.to_sym, value)
old = read_attribute(attr_name)
if old != new
changed_attributes[attr_name] = (old.duplicable? ? old.clone : old)
end
end
@attributes_cache[attr_name] = new
@attributes[attr_name] = value
end end
# If reloading then check if cached, which means its in local time. def read_attribute_before_type_cast_with_timeliness(attr_name)
# If local, convert with parser as local timezone, otherwise use return @attributes_cache["_#{attr_name}_before_type_cast"] if @attributes_cache.has_key?("_#{attr_name}_before_type_cast")
# read_attribute method for quick default type cast of values from read_attribute_before_type_cast_without_timeliness(attr_name)
# database using default timezone.
def read_date_time_attribute(attr_name, type, time_zone_aware, reload = false)
cached = @attributes_cache[attr_name]
return cached if @attributes_cache.has_key?(attr_name) && !reload
if @attributes_cache.has_key?(attr_name)
time = read_attribute_before_type_cast(attr_name)
time = ValidatesTimeliness::Parser.parse(time, type)
else
time = read_attribute(attr_name)
@attributes[attr_name] = (time && time_zone_aware ? time.in_time_zone : time) unless frozen?
end
@attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
end end
module ClassMethods module ClassMethods
def define_attribute_methods_with_timeliness def define_attribute_methods_with_timeliness
return if generated_methods? return if generated_methods?
columns_hash.each do |name, column| timeliness_methods = []
unless instance_method_already_implemented?(name)
if [:date, :time, :datetime].include?(column.type)
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
define_read_method_for_dates_and_times(name, column.type, time_zone_aware)
end
end
unless instance_method_already_implemented?("#{name}=") columns_hash.each do |name, column|
if [:date, :time, :datetime].include?(column.type) if [:date, :time, :datetime].include?(column.type)
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
define_write_method_for_dates_and_times(name, column.type, time_zone_aware)
end class_eval <<-EOV
def #{name}=(value)
write_date_time_attribute('#{name}', value, #{column.type.inspect}, #{time_zone_aware})
end
EOV
timeliness_methods << name
end end
end end
define_attribute_methods_without_timeliness define_attribute_methods_without_timeliness
end @generated_methods += timeliness_methods
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
method_body = <<-EOV
def #{attr_name}=(value)
write_date_time_attribute('#{attr_name}', value, #{type.inspect}, #{time_zone_aware})
end
EOV
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
end
def define_read_method_for_dates_and_times(attr_name, type, time_zone_aware)
method_body = <<-EOV
def #{attr_name}(reload = false)
read_date_time_attribute('#{attr_name}', #{type.inspect}, #{time_zone_aware}, reload)
end
EOV
evaluate_attribute_method attr_name, method_body
end end
end end
@@ -127,5 +69,3 @@ module ValidatesTimeliness
end end
end end
ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)

View File

@@ -10,9 +10,9 @@ module ValidatesTimeliness
def self.included(base) def self.included(base)
base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness
end end
# Overrides AR method to store multiparameter time and dates as string # Assign dates and times as formatted strings to force the use of the plugin parser
# allowing validation later. # and store a before_type_cast value for attribute
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
errors = [] errors = []
callstack.each do |name, values| callstack.each do |name, values|
@@ -46,18 +46,17 @@ module ValidatesTimeliness
when :time when :time
extract_time_from_multiparameter_attributes(values) extract_time_from_multiparameter_attributes(values)
when :datetime when :datetime
date_values, time_values = values.slice!(0, 3), values extract_date_from_multiparameter_attributes(values) + " " + extract_time_from_multiparameter_attributes(values)
extract_date_from_multiparameter_attributes(date_values) + " " + extract_time_from_multiparameter_attributes(time_values)
end end
end end
def extract_date_from_multiparameter_attributes(values) def extract_date_from_multiparameter_attributes(values)
[values[0], *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-") year = ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0"))
[year, *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-")
end end
def extract_time_from_multiparameter_attributes(values) def extract_time_from_multiparameter_attributes(values)
values = values.size > 3 ? values[3..5] : values values[3..5].map { |s| s.rjust(2, "0") }.join(":")
values.map { |s| s.rjust(2, "0") }.join(":")
end end
end end

View File

@@ -21,6 +21,17 @@ module ValidatesTimeliness
:format_tokens, :format_tokens,
:format_proc_args :format_proc_args
# Set the threshold value for a two digit year to be considered last century
# Default: 30
#
# Example:
# year = '29' is considered 2029
# year = '30' is considered 1930
#
cattr_accessor :ambiguous_year_threshold
self.ambiguous_year_threshold = 30
# Format tokens: # Format tokens:
# y = year # y = year
# m = month # m = month
@@ -46,7 +57,7 @@ module ValidatesTimeliness
# #
# Special Cases: # Special Cases:
# yy = 2 or 4 digit year # yy = 2 or 4 digit year
# yyyyy = exactly 4 digit year # yyyy = exactly 4 digit year
# mmm = month long name (e.g. 'Jul' or 'July') # mmm = month long name (e.g. 'Jul' or 'July')
# ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday) # ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
# u = microseconds matches 1 to 6 digits # u = microseconds matches 1 to 6 digits
@@ -85,6 +96,7 @@ module ValidatesTimeliness
@@datetime_formats = [ @@datetime_formats = [
'yyyy-mm-dd hh:nn:ss', 'yyyy-mm-dd hh:nn:ss',
'yyyy-mm-dd h:nn', 'yyyy-mm-dd h:nn',
'yyyy-mm-dd h:nn_ampm',
'yyyy-mm-dd hh:nn:ss.u', 'yyyy-mm-dd hh:nn:ss.u',
'm/d/yy h:nn:ss', 'm/d/yy h:nn:ss',
'm/d/yy h:nn_ampm', 'm/d/yy h:nn_ampm',
@@ -226,6 +238,49 @@ module ValidatesTimeliness
compile_format_expressions compile_format_expressions
end end
def full_hour(hour, meridian)
hour = hour.to_i
return hour if meridian.nil?
if meridian.delete('.').downcase == 'am'
hour == 12 ? 0 : hour
else
hour == 12 ? hour : hour + 12
end
end
def unambiguous_year(year)
if year.length <= 2
century = Time.now.year.to_s[0..1].to_i
century -= 1 if year.to_i >= ambiguous_year_threshold
year = "#{century}#{year.rjust(2,'0')}"
end
year.to_i
end
def month_index(month)
return month.to_i if month.to_i.nonzero?
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
end
def month_names
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
end
def abbr_month_names
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
end
def microseconds(usec)
(".#{usec}".to_f * 1_000_000).to_i
end
def offset_in_seconds(offset)
sign = offset =~ /^-/ ? -1 : 1
parts = offset.scan(/\d\d/).map {|p| p.to_f }
parts[1] = parts[1].to_f / 60
(parts[0] + parts[1]) * sign * 3600
end
private private
# Compile formats into validation regexps and format procs # Compile formats into validation regexps and format procs
@@ -259,7 +314,12 @@ module ValidatesTimeliness
args = order.invert.sort.map {|p| arg_map[p[1]][1] } args = order.invert.sort.map {|p| arg_map[p[1]][1] }
arr = [nil] * 7 arr = [nil] * 7
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? } order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i } }" proc_string = <<-EOL
lambda {|#{args.join(',')}|
md ||= nil
[#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i }
}
EOL
eval proc_string eval proc_string
end end
@@ -285,44 +345,6 @@ module ValidatesTimeliness
end end
end end
def full_hour(hour, meridian)
hour = hour.to_i
return hour if meridian.nil?
if meridian.delete('.').downcase == 'am'
hour == 12 ? 0 : hour
else
hour == 12 ? hour : hour + 12
end
end
def unambiguous_year(year, threshold=30)
year = "#{year.to_i < threshold ? '20' : '19'}#{year}" if year.length == 2
year.to_i
end
def month_index(month)
return month.to_i if month.to_i.nonzero?
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
end
def month_names
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
end
def abbr_month_names
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
end
def microseconds(usec)
(".#{usec}".to_f * 1_000_000).to_i
end
def offset_in_seconds(offset)
sign = offset =~ /^-/ ? -1 : 1
parts = offset.scan(/\d\d/).map {|p| p.to_f }
parts[1] = parts[1].to_f / 60
(parts[0] + parts[1]) * sign * 3600
end
end end
end end
end end

View File

@@ -94,7 +94,7 @@ module ValidatesTimeliness
restriction = [restriction] unless restriction.is_a?(Array) restriction = [restriction] unless restriction.is_a?(Array)
if defined?(I18n) if defined?(I18n)
message = custom_error_messages[option] || I18n.t('activerecord.errors.messages')[option] message = I18n.t('activerecord.errors.messages')[option]
subs = message.scan(/\{\{([^\}]*)\}\}/) subs = message.scan(/\{\{([^\}]*)\}\}/)
interpolations = {} interpolations = {}
subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) } subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
@@ -118,7 +118,7 @@ module ValidatesTimeliness
def add_error(record, attr_name, message, interpolate=nil) def add_error(record, attr_name, message, interpolate=nil)
if defined?(I18n) if defined?(I18n)
custom = custom_error_messages[message] custom = custom_error_messages[message]
record.errors.add(attr_name, custom || message, interpolate || {}) record.errors.add(attr_name, message, { :default => custom }.merge(interpolate || {}))
else else
message = error_messages[message] if message.is_a?(Symbol) message = error_messages[message] if message.is_a?(Symbol)
message = message % interpolate message = message % interpolate

View File

@@ -20,24 +20,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
@person.birth_date_and_time = "2000-01-01 12:00" @person.birth_date_and_time = "2000-01-01 12:00"
end end
it "should call read_date_time_attribute when date attribute is retrieved" do
@person.should_receive(:read_date_time_attribute)
@person.birth_date = "2000-01-01"
@person.birth_date
end
it "should call read_date_time_attribute when time attribute is retrieved" do
@person.should_receive(:read_date_time_attribute)
@person.birth_time = "12:00"
@person.birth_time
end
it "should call read_date_time_attribute when datetime attribute is retrieved" do
@person.should_receive(:read_date_time_attribute)
@person.birth_date_and_time = "2000-01-01 12:00"
@person.birth_date_and_time
end
it "should call parser on write for datetime attribute" do it "should call parser on write for datetime attribute" do
ValidatesTimeliness::Parser.should_receive(:parse).once ValidatesTimeliness::Parser.should_receive(:parse).once
@person.birth_date_and_time = "2000-01-01 02:03:04" @person.birth_date_and_time = "2000-01-01 02:03:04"
@@ -103,7 +85,20 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
@person.birth_date_and_time_before_type_cast.should be_nil @person.birth_date_and_time_before_type_cast.should be_nil
end end
unless RAILS_VER < '2.1' if RAILS_VER < '2.1'
it "should return time object from database in default timezone" do
ActiveRecord::Base.default_timezone = :utc
time_string = "2000-01-01 09:00:00"
@person = Person.new
@person.birth_date_and_time = time_string
@person.save
@person.reload
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
end
else
it "should return stored time string as Time with correct timezone" do it "should return stored time string as Time with correct timezone" do
Time.zone = 'Melbourne' Time.zone = 'Melbourne'
time_string = "2000-06-01 02:03:04" time_string = "2000-06-01 02:03:04"
@@ -121,84 +116,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000' @person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000'
end end
describe "dirty attributes" do
it "should return true for attribute changed? when value updated" do
time_string = "2000-01-01 02:03:04"
@person.birth_date_and_time = time_string
@person.birth_date_and_time_changed?.should be_true
end
it "should show changes when time attribute changed from nil to Time object" do
time_string = "2000-01-01 02:03:04"
@person.birth_date_and_time = time_string
time = @person.birth_date_and_time
@person.changes.should == {"birth_date_and_time" => [nil, time]}
end
it "should show changes when time attribute changed from Time object to nil" do
time_string = "2020-01-01 02:03:04"
@person.birth_date_and_time = time_string
@person.save false
@person.reload
time = @person.birth_date_and_time
@person.birth_date_and_time = nil
@person.changes.should == {"birth_date_and_time" => [time, nil]}
end
it "should show no changes when assigned same value as Time object" do
time_string = "2020-01-01 02:03:04"
@person.birth_date_and_time = time_string
@person.save false
@person.reload
time = @person.birth_date_and_time
@person.birth_date_and_time = time
@person.changes.should == {}
end
it "should show no changes when assigned same value as time string" do
time_string = "2020-01-01 02:03:04"
@person.birth_date_and_time = time_string
@person.save false
@person.reload
@person.birth_date_and_time = time_string
@person.changes.should == {}
end
end
else
it "should return time object from database in default timezone" do
ActiveRecord::Base.default_timezone = :utc
time_string = "2000-01-01 09:00:00"
@person = Person.new
@person.birth_date_and_time = time_string
@person.save
@person.reload
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
end
end
it "should return same time object on repeat reads on existing object" do
Time.zone = 'Melbourne' unless RAILS_VER < '2.1'
time_string = "2000-01-01 09:00:00"
@person = Person.new
@person.birth_date_and_time = time_string
@person.save!
@person.reload
time = @person.birth_date_and_time
@person.birth_date_and_time.should == time
end
it "should return same date object on repeat reads on existing object" do
date_string = Date.today
@person = Person.new
@person.birth_date = date_string
@person.save!
@person.reload
date = @person.birth_date
@person.birth_date.should == date
end end
it "should return correct date value after new value assigned" do it "should return correct date value after new value assigned" do
@@ -220,15 +137,5 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
@person.reload @person.reload
@person.birth_date.should == tomorrow @person.birth_date.should == tomorrow
end end
it "should skip storing value in attributes hash on read if record frozen" do
@person = Person.new
@person.birth_date = Date.today
@person.save!
@person.reload
@person.freeze
@person.frozen?.should be_true
lambda { @person.birth_date }.should_not raise_error
@person.birth_date.should == Date.today
end
end end

View File

@@ -155,6 +155,28 @@ describe ValidatesTimeliness::Formats do
end end
end end
describe "parsing date with ambiguous year" do
it "should return year in current century if year below threshold" do
time_array = formats.parse('01-02-29', :date)
time_array.should == [2029,2,1,0,0,0,0]
end
it "should return year in last century if year at or above threshold" do
time_array = formats.parse('01-02-30', :date)
time_array.should == [1930,2,1,0,0,0,0]
end
it "should allow custom threshold" do
default = ValidatesTimeliness::Formats.ambiguous_year_threshold
ValidatesTimeliness::Formats.ambiguous_year_threshold = 40
time_array = formats.parse('01-02-39', :date)
time_array.should == [2039,2,1,0,0,0,0]
time_array = formats.parse('01-02-40', :date)
time_array.should == [1940,2,1,0,0,0,0]
ValidatesTimeliness::Formats.ambiguous_year_threshold = default
end
end
describe "removing formats" do describe "removing formats" do
it "should remove format from format array" do it "should remove format from format array" do
formats.remove_formats(:time, 'h.nn_ampm') formats.remove_formats(:time, 'h.nn_ampm')

View File

@@ -12,7 +12,7 @@ Ginger.configure do |config|
rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.2'] rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.2']
rails_versions.each do |v| rails_versions.each do |v|
g = Ginger::Scenario.new g = Ginger::Scenario.new("Rails #{v}")
g['rails'] = v g['rails'] = v
config.scenarios << g.dup config.scenarios << g.dup
end end

View File

@@ -2,10 +2,10 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
$:.unshift(File.dirname(__FILE__)) $:.unshift(File.dirname(__FILE__))
$:.unshift(File.dirname(__FILE__) + '/resources') $:.unshift(File.dirname(__FILE__) + '/resources')
ENV['RAILS_ENV'] = 'test' RAILS_ENV = ENV['RAILS_ENV'] = 'test'
require 'rubygems' require 'rubygems'
require 'spec' require 'spec/autorun'
vendored_rails = File.dirname(__FILE__) + '/../../../../vendor/rails' vendored_rails = File.dirname(__FILE__) + '/../../../../vendor/rails'

View File

@@ -506,12 +506,6 @@ describe ValidatesTimeliness::Validator do
configure_validator(:type => :date, :before => before) configure_validator(:type => :date, :before => before)
validator.send(:interpolation_values, :before, before.to_date).should == {:restriction => before} validator.send(:interpolation_values, :before, before.to_date).should == {:restriction => before}
end end
it "should return empty hash if no interpolation keys are in message" do
before = '1900-01-01'
configure_validator(:type => :date, :before => before, :before_message => 'too late')
validator.send(:interpolation_values, :before, before.to_date).should be_empty
end
else else
it "should return array of interpolation values" do it "should return array of interpolation values" do
before = '1900-01-01' before = '1900-01-01'

View File

@@ -2,26 +2,25 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = %q{validates_timeliness} s.name = %q{validates_timeliness}
s.version = "2.0.0" s.version = "2.1.0"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Adam Meehan"] s.authors = ["Adam Meehan"]
s.autorequire = %q{validates_timeliness} s.autorequire = %q{validates_timeliness}
s.date = %q{2009-04-12} s.date = %q{2009-06-20}
s.description = %q{Date and time validation plugin for Rails 2.x which allows custom formats} s.description = %q{Date and time validation plugin for Rails 2.x which allows custom formats}
s.email = %q{adam.meehan@gmail.com} s.email = %q{adam.meehan@gmail.com}
s.extra_rdoc_files = ["README.rdoc", "LICENSE", "TODO", "CHANGELOG"] s.extra_rdoc_files = ["README.rdoc", "LICENSE", "TODO", "CHANGELOG"]
s.files = ["LICENSE", "README.rdoc", "Rakefile", "TODO", "CHANGELOG", "lib/validates_timeliness", "lib/validates_timeliness/core_ext", "lib/validates_timeliness/core_ext/date.rb", "lib/validates_timeliness/core_ext/date_time.rb", "lib/validates_timeliness/core_ext/time.rb", "lib/validates_timeliness/action_view", "lib/validates_timeliness/action_view/instance_tag.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.yml", "lib/validates_timeliness/validation_methods.rb", "lib/validates_timeliness/active_record", "lib/validates_timeliness/active_record/attribute_methods.rb", "lib/validates_timeliness/active_record/multiparameter_attributes.rb", "lib/validates_timeliness/parser.rb", "lib/validates_timeliness/formats.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/spec", "lib/validates_timeliness/spec/rails", "lib/validates_timeliness/spec/rails/matchers", "lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb", "lib/validates_timeliness.rb", "spec/core_ext", "spec/core_ext/dummy_time_spec.rb", "spec/validator_spec.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/ginger_scenarios.rb", "spec/spec_helper.rb", "spec/formats_spec.rb", "spec/active_record", "spec/active_record/attribute_methods_spec.rb", "spec/active_record/multiparameter_attributes_spec.rb", "spec/time_travel", "spec/time_travel/time_travel.rb", "spec/time_travel/time_extensions.rb", "spec/time_travel/MIT-LICENSE", "spec/parser_spec.rb", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/resources", "spec/resources/person.rb", "spec/resources/sqlite_patch.rb", "spec/resources/schema.rb", "spec/resources/application.rb"] s.files = ["LICENSE", "README.rdoc", "Rakefile", "TODO", "CHANGELOG", "lib/validates_timeliness", "lib/validates_timeliness/active_record", "lib/validates_timeliness/active_record/multiparameter_attributes.rb", "lib/validates_timeliness/active_record/attribute_methods.rb", "lib/validates_timeliness/parser.rb", "lib/validates_timeliness/core_ext", "lib/validates_timeliness/core_ext/date.rb", "lib/validates_timeliness/core_ext/time.rb", "lib/validates_timeliness/core_ext/date_time.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/validation_methods.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.yml", "lib/validates_timeliness/spec", "lib/validates_timeliness/spec/rails", "lib/validates_timeliness/spec/rails/matchers", "lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb", "lib/validates_timeliness/action_view", "lib/validates_timeliness/action_view/instance_tag.rb", "lib/validates_timeliness/formats.rb", "lib/validates_timeliness.rb", "spec/active_record", "spec/active_record/multiparameter_attributes_spec.rb", "spec/active_record/attribute_methods_spec.rb", "spec/formats_spec.rb", "spec/parser_spec.rb", "spec/core_ext", "spec/core_ext/dummy_time_spec.rb", "spec/spec_helper.rb", "spec/ginger_scenarios.rb", "spec/time_travel", "spec/time_travel/time_extensions.rb", "spec/time_travel/time_travel.rb", "spec/time_travel/MIT-LICENSE", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/validator_spec.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/resources", "spec/resources/schema.rb", "spec/resources/application.rb", "spec/resources/person.rb", "spec/resources/sqlite_patch.rb"]
s.has_rdoc = true
s.homepage = %q{http://github.com/adzap/validates_timeliness} s.homepage = %q{http://github.com/adzap/validates_timeliness}
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.rubyforge_project = %q{validatestime} s.rubyforge_project = %q{validatestime}
s.rubygems_version = %q{1.3.1} s.rubygems_version = %q{1.3.3}
s.summary = %q{Date and time validation plugin for Rails 2.x which allows custom formats} s.summary = %q{Date and time validation plugin for Rails 2.x which allows custom formats}
if s.respond_to? :specification_version then if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 2 s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
else else