Compare commits

...

43 Commits
2.2.1 ... 2.3.2

Author SHA1 Message Date
Adam Meehan
e711941744 version 2.3.2 2010-11-07 11:04:42 +11:00
Adam Meehan
5bed56e180 fixes for latest I18n interpolation token detection 2010-11-07 10:59:16 +11:00
Adam Meehan
c3a591a5d8 fix bad spec 2010-11-06 19:08:25 +11:00
Adam Meehan
6265b2d804 fix parsing of datetimes with timezone (thanks sigi) 2010-11-06 19:05:50 +11:00
Adam Meehan
341a481231 install instructions for v2 2010-09-17 15:34:03 +10:00
Adam Meehan
d6ddae9977 don't allow 0 or > 12 for meridian am hour 2010-09-17 14:52:39 +10:00
Adam Meehan
0ea0201051 put install plugin below gem 2010-07-27 07:32:42 +10:00
Adam Meehan
b874e786b6 version 2.3.1 2010-03-19 14:24:19 +11:00
Adam Meehan
b356363791 fix bug preventing custom class methods on date/times 2010-03-19 14:11:13 +11:00
Adam Meehan
e9eb812c9e fix misbehaving spec 2010-03-19 13:39:47 +11:00
Adam Meehan
353f154330 tiny spec clean 2010-02-26 11:23:55 +11:00
Adam Meehan
ccfad007d1 version 2.3.0 gemspec 2010-02-04 13:23:26 +11:00
Adam Meehan
f3c0dd6144 removed most of old gem builder guff 2010-02-04 13:22:50 +11:00
Adam Meehan
6349990243 version 2.3.0 2010-02-04 13:19:57 +11:00
Adam Meehan
220b58ab14 some todos for v3 2010-02-04 13:19:22 +11:00
Adam Meehan
f98e503e25 readme tweaks 2010-02-04 13:18:55 +11:00
Adam Meehan
3e1a9fc107 fix locale spec by adding time formats into dummy locale 2010-02-04 13:08:00 +11:00
Adam Meehan
99d742e3d0 instance_tag time_select spec fix 2010-02-04 13:06:24 +11:00
Adam Meehan
77f338d97c refactored action view extension to use params
this is instead of value before type to avoid issues with partial value parsing
will be extracted in v3
2010-02-04 11:45:28 +11:00
Adam Meehan
d255bbfccf deprecation warning example for :equal_to with messages silenced 2010-01-18 12:03:10 +11:00
Adam Meehan
4df1974524 tweak instance tag spec 2010-01-12 22:13:43 +11:00
Adam Meehan
82c0e1bcd3 changed format of string used in multi param for invalid or partial values 2010-01-12 22:05:14 +11:00
Adam Meehan
f1a0016bf7 added deprecation notice for :equal_to 2010-01-12 22:02:15 +11:00
Adam Meehan
59d9957ab6 fix generated_methods bug where read instead of write method stored
existing spec failed unless run alone as attribute methods already
primed. fixed spec to undef method and empty generated_methods set
2009-12-29 11:13:15 +11:00
Adam Meehan
694c3b0ce3 method rename and words 2009-12-23 14:52:41 +11:00
Adam Meehan
a859827af4 fix regexp for ISO 8601 datetimes (thanks costan) 2009-12-23 13:06:32 +11:00
Adam Meehan
f8b9f72693 geez, fix readme typo 2009-12-11 16:36:35 +11:00
Adam Meehan
7f1ed79a89 change a couple of readme equal_to 2009-12-11 15:33:50 +11:00
Adam Meehan
f3c119e191 change equal_to to is_at to fix overlap with default rails message 2009-12-11 15:20:34 +11:00
Adam Meehan
3bfc7b748f more rails versions in ginger scenarios 2009-12-11 15:19:07 +11:00
Adam Meehan
40369efdff GMT to UTC in spec for 1.8.7 2009-12-11 15:18:22 +11:00
Adam Meehan
f41016af93 spec for missing translation 2009-12-11 15:17:46 +11:00
Adam Meehan
5258256d5e reworked i18n messages, interpolation to behave on missing translation 2009-12-11 15:15:16 +11:00
Adam Meehan
9cfbb2a458 fix for I18n formats lookup in Rails <= 2.3.3 vendored I18n 2009-12-11 15:12:49 +11:00
Adam Meehan
78baa7a3cc move default validator options to constant 2009-12-11 11:13:35 +11:00
Adam Meehan
7d6967da90 tiny cleanup 2009-12-11 11:10:33 +11:00
Adam Meehan
0c38b6abd1 added all I18n error messages in README example 2009-12-11 11:08:52 +11:00
Adam Meehan
a6712de5ff use global error value formats if missing in locale
always loads globals value formats into class accessor
replace old format accessor methods class accessor
2009-12-11 11:06:21 +11:00
Adam Meehan
c580c3e682 use generated_methods method rather ivar 2009-09-19 11:59:08 +10:00
Adam Meehan
9c5db44500 update gemspec 2009-09-19 10:16:49 +10:00
Adam Meehan
f109443fb7 version 2.2.2 2009-09-19 08:19:27 +10:00
Adam Meehan
96bf4bf184 fix dummy to respect timezones by using make_time 2009-09-19 07:30:33 +10:00
Adam Meehan
90be6a6db5 change to github issues for bug reports 2009-09-12 14:39:46 +10:00
25 changed files with 856 additions and 461 deletions

View File

@@ -1,3 +1,23 @@
= 2.3.2 [2010-11-07]
- Fixed parser for string with timezone offset (thanks sigi)
- Support for latest I18n interpolation tokens in locale file
- Fixed parser allowing an hour over 12 for AM meridian
= 2.3.1 [2010-03-19]
- Fixed bug where custom attribute writer method for date/times were being overriden
= 2.3.0 [2010-02-04]
- Backwards incompatible change to :equal_to option. Fixed error message clash with :equal_to option which exists in Rails already. Option is now :is_at.
- Fixed I18n support so it returns missing translation message instead of error
- Fixed attribute method bug. Write method was bypassed when method was first generated and used Rails default parser.
- Fixed date/time selects when using enable_datetime_select_extension! when some values empty
- Fixed ISO8601 datetime format which is now split into two formats
- Changed I18n error value format to fallback to global default if missing in locale
- Refactored date/time select invalid value extension to use param values. Functionality will be extracted from plugin for v3.
= 2.2.2 [2009-09-19]
- Fixed dummy_time using make_time to respect timezone. Fixes 1.9.1 bug.
= 2.2.1 [2009-09-12]
- Fixed dummy date part for time types in Validator.type_cast_value
- No more core extensions! Removed dummy_time methods.

View File

@@ -1,14 +1,14 @@
= validates_timeliness
* Source: http://github.com/adzap/validates_timeliness
* Bugs: http://adzap.lighthouseapp.com/projects/14111-validates_timeliness
* Bugs: http://github.com/adzap/validates_timeliness/issues
== DESCRIPTION:
Validate dates, times and datetimes for Rails 2.x. Plays nicely with new Rails 2.1
features such as automatic timezone handling and dirty attributes. Allows you to
add custom formats or remove defaults easily. This allows you to control what you
think should be a valid date or time string.
Validate dates, times and datetimes for Rails 2.x. Plays nicely with Rails
automatic timezone handling. Allows you to add custom formats or remove defaults
easily. This allows you to control what you think should be a valid date or
time string.
== FEATURES:
@@ -31,18 +31,17 @@ think should be a valid date or time string.
== INSTALLATION:
As plugin (from master)
./script/plugin install git://github.com/adzap/validates_timeliness.git
As gem
sudo gem install validates_timeliness
gem install validates_timeliness -v '~> 2.3'
# in environment.rb
config.gem 'validates_timeliness'
config.gem 'validates_timeliness', :version => '~> 2.3'
As plugin (from master)
./script/plugin install git://github.com/adzap/validates_timeliness.git -r v2.3
== USAGE:
@@ -63,7 +62,7 @@ The validation methods take the usual options plus some specific ones to restric
the valid range of dates or times allowed
Temporal options (or restrictions):
:equal_to - Attribute must be equal to value to be valid
:is_at - Attribute must be equal to value to be valid
:before - Attribute must be before this value to be valid
:on_or_before - Attribute must be equal to or before this value to be valid
:after - Attribute must be after this value to be valid
@@ -86,7 +85,7 @@ Message options: - Use these to override the default error messages
:invalid_date_message
:invalid_time_message
:invalid_datetime_message
:equal_to_message
:is_at_message
:before_message
:on_or_before_message
:after_message
@@ -165,7 +164,8 @@ NOTE: To use non-US date formats see US/EURO FORMATS section
yyyy-mm-dd hh:nn:ss
yyyy-mm-dd h:nn
ddd mmm d hh:nn:ss zo yyyy # Ruby time string
yyyy-mm-ddThh:nn:ss(?:Z|zo) # ISO 8601
yyyy-mm-ddThh:nn:ssZ # ISO 8601 without zone offset
yyyy-mm-ddThh:nn:sszo # ISO 8601 with zone offset
NOTE: To use non-US date formats see US/EURO FORMATS section
@@ -305,6 +305,7 @@ To activate it, put this in an initializer:
ValidatesTimeliness.enable_datetime_select_extension!
This will be removed from v3 as it adds too little to maintain.
=== OTHER CUSTOMISATION:
@@ -317,6 +318,7 @@ For Rails 2.0/2.1:
:invalid_date => "is not a valid date",
:invalid_time => "is not a valid time",
:invalid_datetime => "is not a valid datetime",
:is_at => "must be at %s",
:before => "must be before %s",
:on_or_before => "must be on or before %s",
:after => "must be after %s",
@@ -332,8 +334,14 @@ Rails 2.2+ using the I18n system to define new defaults:
activerecord:
errors:
messages:
on_or_before: "must be equal to or before {{restriction}}"
on_or_after: "must be equal to or after {{restriction}}"
invalid_date: "is not a valid date"
invalid_time: "is not a valid time"
invalid_datetime: "is not a valid datetime"
is_at: "must be at {{restriction}}"
before: "must be before {{restriction}}"
on_or_before: "must be on or before {{restriction}}"
after: "must be after {{restriction}}"
on_or_after: "must be on or after {{restriction}}"
between: "must be between {{earliest}} and {{latest}}"
The {{restriction}} signifies where the interpolation value for the restriction
@@ -391,4 +399,4 @@ The matcher names are just the singular of the validation methods.
== LICENSE:
Copyright (c) 2008 Adam Meehan, released under the MIT license
Copyright (c) 2008-2010 Adam Meehan, released under the MIT license

View File

@@ -7,10 +7,6 @@ require 'lib/validates_timeliness/version'
GEM = "validates_timeliness"
GEM_VERSION = ValidatesTimeliness::VERSION
AUTHOR = "Adam Meehan"
EMAIL = "adam.meehan@gmail.com"
HOMEPAGE = "http://github.com/adzap/validates_timeliness"
SUMMARY = "Date and time validation plugin for Rails 2.x which allows custom formats"
spec = Gem::Specification.new do |s|
s.name = GEM
@@ -19,11 +15,11 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "validatestime"
s.has_rdoc = true
s.extra_rdoc_files = ["README.rdoc", "LICENSE", "TODO", "CHANGELOG"]
s.summary = SUMMARY
s.summary = "Date and time validation plugin for Rails 2.x which allows custom formats"
s.description = s.summary
s.author = AUTHOR
s.email = EMAIL
s.homepage = HOMEPAGE
s.author = "Adam Meehan"
s.email = "adam.meehan@gmail.com"
s.homepage = "http://github.com/adzap/validates_timeliness"
s.require_path = 'lib'
s.autorequire = GEM

3
TODO
View File

@@ -3,3 +3,6 @@
- array of values for all temporal options
- use tz and zo value from time string?
- filter valid formats rather than remove for hot swapping without recompilation
- config generator
- move all config into top namespace
- remove action_view stuff

View File

@@ -2,10 +2,18 @@ require 'validates_timeliness/formats'
require 'validates_timeliness/parser'
require 'validates_timeliness/validator'
require 'validates_timeliness/validation_methods'
require 'validates_timeliness/active_record/attribute_methods'
require 'validates_timeliness/active_record/multiparameter_attributes'
require 'validates_timeliness/action_view/instance_tag'
begin
i18n_path = $:.grep(/active_support\/vendor\/i18n-/)
if i18n_path.empty?
require 'i18n/version'
else
require i18n_path[0] + '/version'
end
rescue LoadError
end if defined?(I18n)
module ValidatesTimeliness
@@ -15,7 +23,9 @@ module ValidatesTimeliness
mattr_accessor :use_time_zones
self.use_time_zones = false
LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/validates_timeliness/locale/en.yml')
I18N_LATEST = defined?(I18n::VERSION) && I18n::VERSION >= '0.4.0'
locale_file = I18N_LATEST ? 'en.new.yml' : 'en.old.yml'
LOCALE_PATH = File.expand_path(File.join(File.dirname(__FILE__),'validates_timeliness','locale',locale_file))
class << self
@@ -25,15 +35,15 @@ module ValidatesTimeliness
end
def load_error_messages
defaults = YAML::load(IO.read(LOCALE_PATH))['en']
ValidatesTimeliness::Validator.error_value_formats = defaults['validates_timeliness']['error_value_formats'].symbolize_keys
if defined?(I18n)
I18n.load_path.unshift(LOCALE_PATH)
I18n.reload!
else
defaults = YAML::load(IO.read(LOCALE_PATH))['en']
errors = defaults['activerecord']['errors']['messages'].inject({}) {|h,(k,v)| h[k.to_sym] = v.gsub(/\{\{\w*\}\}/, '%s');h }
::ActiveRecord::Errors.default_error_messages.update(errors)
ValidatesTimeliness::Validator.error_value_formats = defaults['validates_timeliness']['error_value_formats'].symbolize_keys
end
end

View File

@@ -1,3 +1,4 @@
# TODO remove this from the plugin for v3.
module ValidatesTimeliness
def self.enable_datetime_select_invalid_value_extension!
@@ -6,14 +7,15 @@ module ValidatesTimeliness
module ActionView
# Intercepts the date and time select helpers to allow the
# attribute value before type cast to be used as in the select helpers.
# This means that an invalid date or time will be redisplayed rather than the
# type cast value which would be nil if invalid.
# Intercepts the date and time select helpers to reuse the values from the
# the params rather than the parsed value. This allows invalid date/time
# values to be redisplayed instead of blanks to aid correction by the user.
# Its a minor usability improvement which is rarely an issue for the user.
#
module InstanceTag
def self.included(base)
selector_method = Rails::VERSION::STRING < '2.2' ? :date_or_time_select : :datetime_selector
selector_method = Rails::VERSION::STRING.to_f < 2.2 ? :date_or_time_select : :datetime_selector
base.class_eval do
alias_method :datetime_selector_without_timeliness, selector_method
alias_method selector_method, :datetime_selector_with_timeliness
@@ -29,17 +31,19 @@ module ValidatesTimeliness
end
def value_with_timeliness(object)
return value_without_timeliness(object) unless @timeliness_date_or_time_tag
raw_value = value_before_type_cast(object)
if raw_value.nil? || raw_value.acts_like?(:time) || raw_value.is_a?(Date)
unless @timeliness_date_or_time_tag && @template_object.params[@object_name]
return value_without_timeliness(object)
end
time_array = ValidatesTimeliness::Formats.parse(raw_value, :datetime)
pairs = @template_object.params[@object_name].select {|k,v| k =~ /^#{@method_name}\(/ }
return value_without_timeliness(object) if pairs.empty?
TimelinessDateTime.new(*time_array[0..5])
values = pairs.map do |(param, value)|
position = param.scan(/\(([0-9]*).*\)/).first.first
[position, value]
end.sort {|a,b| a[0] <=> b[0] }.map {|v| v[1] }
TimelinessDateTime.new(*values)
end
end

View File

@@ -24,7 +24,6 @@ module ValidatesTimeliness
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
@attributes_cache["_#{attr_name}_before_type_cast"] = value
value = ValidatesTimeliness::Parser.parse(value, type)
if value && type != :date
@@ -49,17 +48,25 @@ module ValidatesTimeliness
columns_hash.each do |name, column|
if [:date, :time, :datetime].include?(column.type)
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
method_name = "#{name}="
next if instance_method_already_implemented?(method_name)
define_method("#{name}=") do |value|
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
define_method(method_name) do |value|
write_date_time_attribute(name, value, column.type, time_zone_aware)
end
timeliness_methods << name
timeliness_methods << method_name
end
end
# Hack to get around instance_method_already_implemented? caching the
# methods in the ivar. It then appears to subsequent calls that the
# methods defined here, have not been and get defined again.
@_defined_class_methods = nil
define_attribute_methods_without_timeliness
@generated_methods += timeliness_methods
# add generated methods which is a Set object hence no += method
timeliness_methods.each {|attr| generated_methods << attr }
end
end

View File

@@ -22,12 +22,12 @@ module ValidatesTimeliness
end
def extract_date_from_multiparameter_attributes(values)
year = ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0"))
[year, *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-")
year = values[0].blank? ? nil : ValidatesTimeliness::Formats.unambiguous_year(values[0].rjust(2, "0"))
[year, *values.slice(1, 2).map { |s| s.blank? ? nil : s.rjust(2, "0") }].join("-")
end
def extract_time_from_multiparameter_attributes(values)
values[3..5].map { |s| s.rjust(2, "0") }.join(":")
values[3..5].map { |s| s.blank? ? nil : s.rjust(2, "0") }.join(":")
end
end
@@ -39,7 +39,6 @@ module ValidatesTimeliness
end
# Assign dates and times as formatted strings to force the use of the plugin parser
# and store a before_type_cast value for attribute
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
errors = []
callstack.each do |name, values|
@@ -47,7 +46,7 @@ module ValidatesTimeliness
if column && [:date, :time, :datetime].include?(column.type)
begin
callstack.delete(name)
if values.empty?
if values.empty? || values.all?(&:nil?)
send("#{name}=", nil)
else
value = ValidatesTimeliness::ActiveRecord.time_array_to_string(values, column.type)

View File

@@ -2,11 +2,9 @@ require 'date'
module ValidatesTimeliness
# A date and time format regular expression generator. Allows you to
# construct a date, time or datetime format using predefined tokens in
# a string. This makes it much easier to catalogue and customize the formats
# rather than dealing directly with regular expressions. The formats are then
# compiled into regular expressions for use validating date or time strings.
# A date and time parsing library which allows you to add custom formats using
# simple predefined tokens. This makes it much easier to catalogue and customize
# the formats rather than dealing directly with regular expressions.
#
# Formats can be added or removed to customize the set of valid date or time
# string values.
@@ -115,7 +113,8 @@ module ValidatesTimeliness
'd/m/yy h:nn',
'ddd, dd mmm yyyy hh:nn:ss (zo|tz)', # RFC 822
'ddd mmm d hh:nn:ss zo yyyy', # Ruby time string
'yyyy-mm-ddThh:nn:ss(?:Z|zo)' # iso 8601
'yyyy-mm-ddThh:nn:ssZ', # iso 8601 without zone offset
'yyyy-mm-ddThh:nn:sszo' # iso 8601 with zone offset
]
@@ -210,6 +209,8 @@ module ValidatesTimeliness
values[0..2] = dummy_date_for_time_type if type == :time
return values
end
rescue
nil
end
# Delete formats of specified type. Error raised if format not found.
@@ -256,6 +257,7 @@ module ValidatesTimeliness
hour = hour.to_i
return hour if meridian.nil?
if meridian.delete('.').downcase == 'am'
raise if hour == 0 || hour > 12
hour == 12 ? 0 : hour
else
hour == 12 ? hour : hour + 12
@@ -297,8 +299,8 @@ module ValidatesTimeliness
private
# Compile formats into validation regexps and format procs
def format_expression_generator(string_format)
# Generate regular expression and processor from format string
def generate_format_expression(string_format)
regexp = string_format.dup
order = {}
regexp.gsub!(/([\.\\])/, '\\\\\1') # escapes dots and backslashes
@@ -338,7 +340,7 @@ module ValidatesTimeliness
end
def compile_formats(formats)
formats.map { |format| [ format, *format_expression_generator(format) ] }
formats.map { |format| [ format, *generate_format_expression(format) ] }
end
# Pick expression set and combine date and datetimes for

View File

@@ -0,0 +1,18 @@
en:
activerecord:
errors:
messages:
invalid_date: "is not a valid date"
invalid_time: "is not a valid time"
invalid_datetime: "is not a valid datetime"
is_at: "must be at %{restriction}"
before: "must be before %{restriction}"
on_or_before: "must be on or before %{restriction}"
after: "must be after %{restriction}"
on_or_after: "must be on or after %{restriction}"
between: "must be between %{earliest} and %{latest}"
validates_timeliness:
error_value_formats:
date: '%Y-%m-%d'
time: '%H:%M:%S'
datetime: '%Y-%m-%d %H:%M:%S'

View File

@@ -5,7 +5,7 @@ en:
invalid_date: "is not a valid date"
invalid_time: "is not a valid time"
invalid_datetime: "is not a valid datetime"
equal_to: "must be equal to {{restriction}}"
is_at: "must be at {{restriction}}"
before: "must be before {{restriction}}"
on_or_before: "must be on or before {{restriction}}"
after: "must be after {{restriction}}"

View File

@@ -13,7 +13,7 @@ module ValidatesTimeliness
if type == :date
Date.new(*time_array[0..2]) rescue nil
else
make_time(time_array[0..7])
make_time(time_array[0..6])
end
end

View File

@@ -10,7 +10,7 @@ module Spec
}
OPTION_TEST_SETTINGS = {
:equal_to => { :method => :+, :modify_on => :invalid },
:is_at => { :method => :+, :modify_on => :invalid },
:before => { :method => :-, :modify_on => :valid },
:after => { :method => :+, :modify_on => :valid },
:on_or_before => { :method => :+, :modify_on => :invalid },
@@ -28,7 +28,7 @@ module Spec
valid = test_validity
valid = test_option(:equal_to) if valid && @options[:equal_to]
valid = test_option(:is_at) if valid && @options[:is_at]
valid = test_option(:before) if valid && @options[:before]
valid = test_option(:after) if valid && @options[:after]
valid = test_option(:on_or_before) if valid && @options[:on_or_before]
@@ -115,28 +115,27 @@ module Spec
pass
end
def error_message_for(option)
msg = @validator.error_messages[option]
restriction = @validator.class.send(:evaluate_option_value, @validator.configuration[option], @type, @record)
def error_message_for(message)
restriction = @validator.class.send(:evaluate_option_value, @validator.configuration[message], @type, @record)
if restriction
restriction = [restriction] unless restriction.is_a?(Array)
restriction.map! {|r| @validator.class.send(:type_cast_value, r, @type) }
interpolate = @validator.send(:interpolation_values, option, restriction )
restriction = @validator.class.send(:type_cast_value, restriction, @type)
interpolate = @validator.send(:interpolation_values, message, restriction)
end
# get I18n message if defined and has interpolation keys in msg
if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option)
msg = if defined?(ActiveRecord::Error)
ActiveRecord::Error.new(@record, @expected, option, interpolate).message
if defined?(I18n)
interpolate ||= {}
options = interpolate.merge(:default => @validator.send(:custom_error_messages)[message])
if defined?(ActiveRecord::Error)
ActiveRecord::Error.new(@record, @expected, message, options).message
else
@record.errors.generate_message(@expected, option, interpolate)
@record.errors.generate_message(@expected, message, options)
end
else
msg = msg % interpolate
interpolate ||= nil
@validator.error_messages[message] % interpolate
end
end
msg
end
def format_value(value)
return value if value.is_a?(String)

View File

@@ -1,10 +1,13 @@
#TODO remove deprecated option :equal_to
module ValidatesTimeliness
class Validator
cattr_accessor :error_value_formats
cattr_accessor :ignore_restriction_errors
self.ignore_restriction_errors = false
RESTRICTION_METHODS = {
:is_at => :==,
:equal_to => :==,
:before => :<,
:after => :>,
@@ -13,17 +16,18 @@ module ValidatesTimeliness
:between => lambda {|v, r| (r.first..r.last).include?(v) }
}
VALID_OPTIONS = [
VALID_OPTION_KEYS = [
:on, :if, :unless, :allow_nil, :empty, :allow_blank,
:with_time, :with_date, :ignore_usec, :format,
:invalid_time_message, :invalid_date_message, :invalid_datetime_message
] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten
DEFAULT_OPTIONS = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false, :ignore_usec => false }
attr_reader :configuration, :type
def initialize(configuration)
defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false, :ignore_usec => false }
@configuration = defaults.merge(configuration)
@configuration = DEFAULT_OPTIONS.merge(configuration)
@type = @configuration.delete(:type)
validate_options(@configuration)
end
@@ -44,7 +48,7 @@ module ValidatesTimeliness
end
def error_messages
@error_messages ||= self.class.default_error_messages.merge(custom_error_messages)
@error_messages ||= ::ActiveRecord::Errors.default_error_messages.merge(custom_error_messages)
end
private
@@ -81,14 +85,13 @@ module ValidatesTimeliness
end
def interpolation_values(option, restriction)
format = self.class.error_value_formats[type]
format = self.class.error_value_format_for(type)
restriction = [restriction] unless restriction.is_a?(Array)
if defined?(I18n)
message = I18n.t('activerecord.errors.messages')[option]
subs = message.scan(/\{\{([^\}]*)\}\}/)
interpolations = {}
subs.each_with_index {|s, i| interpolations[s[0].to_sym] = restriction[i].strftime(format) }
keys = restriction.size == 1 ? [:restriction] : [:earliest, :latest]
keys.each_with_index {|key, i| interpolations[key] = restriction[i].strftime(format) }
interpolations
else
restriction.map {|r| r.strftime(format) }
@@ -112,8 +115,7 @@ module ValidatesTimeliness
record.errors.add(attr_name, message, { :default => custom }.merge(interpolate || {}))
else
message = error_messages[message] if message.is_a?(Symbol)
message = message % interpolate
record.errors.add(attr_name, message)
record.errors.add(attr_name, message % interpolate)
end
end
@@ -140,10 +142,16 @@ module ValidatesTimeliness
end
def validate_options(options)
if options.key?(:equal_to)
::ActiveSupport::Deprecation.warn("ValidatesTimeliness :equal_to option is deprecated due to clash with a default Rails option. Use :is_at instead. You will need to fix any I18n error message references to this option date/time attributes now.")
options[:is_at] = options.delete(:equal_to)
options[:is_at_message] = options.delete(:equal_to_message)
end
invalid_for_type = ([:time, :date, :datetime] - [type]).map {|k| "invalid_#{k}_message".to_sym }
invalid_for_type << :with_date unless type == :time
invalid_for_type << :with_time unless type == :date
options.assert_valid_keys(VALID_OPTIONS - invalid_for_type)
options.assert_valid_keys(VALID_OPTION_KEYS - invalid_for_type)
end
def implied_type
@@ -153,26 +161,15 @@ module ValidatesTimeliness
# class methods
class << self
def default_error_messages
def error_value_format_for(type)
if defined?(I18n)
I18n.t('activerecord.errors.messages')
# work around for syntax check in vendored I18n for Rails <= 2.3.3
I18n.t('validates_timeliness.error_value_formats')[type] || error_value_formats[type]
else
::ActiveRecord::Errors.default_error_messages
error_value_formats[type]
end
end
def error_value_formats
if defined?(I18n)
I18n.t('validates_timeliness.error_value_formats')
else
@@error_value_formats
end
end
def error_value_formats=(formats)
@@error_value_formats = formats
end
def evaluate_option_value(value, type, record)
case value
when Time, Date
@@ -224,7 +221,7 @@ module ValidatesTimeliness
time = [0,0,0]
end
dummy_date = ValidatesTimeliness::Formats.dummy_date_for_time_type
Time.send(ValidatesTimeliness.default_timezone, *(dummy_date + time))
ValidatesTimeliness::Parser.make_time(dummy_date + time)
end
end

View File

@@ -1,3 +1,3 @@
module ValidatesTimeliness
VERSION = "2.2.1"
VERSION = "2.3.2"
end

View File

@@ -1,7 +1,5 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
ValidatesTimeliness.enable_datetime_select_extension!
describe 'ValidatesTimeliness::ActionView::InstanceTag' do
include ActionView::Helpers::DateHelper
include ActionController::Assertions::SelectorAssertions
@@ -10,33 +8,187 @@ describe 'ValidatesTimeliness::ActionView::InstanceTag' do
@person = Person.new
end
it "should display invalid datetime as datetime_select values" do
@person.birth_date_and_time = "2008-02-30 12:00:22"
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i]') do
with_tag('option[selected=selected]', '2008')
end
output.should have_tag('select[id=person_birth_date_and_time_2i]') do
with_tag('option[selected=selected]', 'February')
end
output.should have_tag('select[id=person_birth_date_and_time_3i]') do
with_tag('option[selected=selected]', '30')
end
output.should have_tag('select[id=person_birth_date_and_time_4i]') do
with_tag('option[selected=selected]', '12')
end
output.should have_tag('select[id=person_birth_date_and_time_5i]') do
with_tag('option[selected=selected]', '00')
end
output.should have_tag('select[id=person_birth_date_and_time_6i]') do
with_tag('option[selected=selected]', '22')
end
def params
@params ||= {}
end
it "should display datetime_select when datetime value is nil" do
describe "datetime_select" do
it "should use param values when attribute is nil" do
params["person"] = {
"birth_date_and_time(1i)" => 2009,
"birth_date_and_time(2i)" => 2,
"birth_date_and_time(3i)" => 29,
"birth_date_and_time(4i)" => 12,
"birth_date_and_time(5i)" => 13,
"birth_date_and_time(6i)" => 14,
}
@person.birth_date_and_time = nil
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select', 6)
output.should have_tag('select[id=person_birth_date_and_time_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_and_time_2i] option[selected=selected]', 'February')
output.should have_tag('select[id=person_birth_date_and_time_3i] option[selected=selected]', '29')
output.should have_tag('select[id=person_birth_date_and_time_4i] option[selected=selected]', '12')
output.should have_tag('select[id=person_birth_date_and_time_5i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_date_and_time_6i] option[selected=selected]', '14')
end
it "should override object values and use params if present" do
params["person"] = {
"birth_date_and_time(1i)" => 2009,
"birth_date_and_time(2i)" => 2,
"birth_date_and_time(3i)" => 29,
"birth_date_and_time(4i)" => 13,
"birth_date_and_time(5i)" => 14,
"birth_date_and_time(6i)" => 15,
}
@person.birth_date_and_time = "2009-03-01 13:14:15"
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_and_time_2i] option[selected=selected]', 'February')
output.should have_tag('select[id=person_birth_date_and_time_3i] option[selected=selected]', '29')
output.should have_tag('select[id=person_birth_date_and_time_4i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_date_and_time_5i] option[selected=selected]', '14')
output.should have_tag('select[id=person_birth_date_and_time_6i] option[selected=selected]', '15')
end
it "should select attribute values from object if no params" do
@person.birth_date_and_time = "2009-01-02 13:14:15"
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_and_time_2i] option[selected=selected]', 'January')
output.should have_tag('select[id=person_birth_date_and_time_3i] option[selected=selected]', '2')
output.should have_tag('select[id=person_birth_date_and_time_4i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_date_and_time_5i] option[selected=selected]', '14')
output.should have_tag('select[id=person_birth_date_and_time_6i] option[selected=selected]', '15')
end
it "should select attribute values if params does not contain attribute params" do
@person.birth_date_and_time = "2009-01-02 13:14:15"
params["person"] = { }
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_and_time_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_and_time_2i] option[selected=selected]', 'January')
output.should have_tag('select[id=person_birth_date_and_time_3i] option[selected=selected]', '2')
output.should have_tag('select[id=person_birth_date_and_time_4i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_date_and_time_5i] option[selected=selected]', '14')
output.should have_tag('select[id=person_birth_date_and_time_6i] option[selected=selected]', '15')
end
it "should not select values when attribute value is nil and has no param values" do
@person.birth_date_and_time = nil
output = datetime_select(:person, :birth_date_and_time, :include_blank => true, :include_seconds => true)
output.should_not have_tag('select[id=person_birth_date_and_time_1i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_and_time_2i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_and_time_3i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_and_time_4i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_and_time_5i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_and_time_6i] option[selected=selected]')
end
end
describe "date_select" do
it "should use param values when attribute is nil" do
params["person"] = {
"birth_date(1i)" => 2009,
"birth_date(2i)" => 2,
"birth_date(3i)" => 29,
}
@person.birth_date = nil
output = date_select(:person, :birth_date, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_2i] option[selected=selected]', 'February')
output.should have_tag('select[id=person_birth_date_3i] option[selected=selected]', '29')
end
it "should override object values and use params if present" do
params["person"] = {
"birth_date(1i)" => 2009,
"birth_date(2i)" => 2,
"birth_date(3i)" => 29,
}
@person.birth_date = "2009-03-01"
output = date_select(:person, :birth_date, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_2i] option[selected=selected]', 'February')
output.should have_tag('select[id=person_birth_date_3i] option[selected=selected]', '29')
end
it "should select attribute values from object if no params" do
@person.birth_date = "2009-01-02"
output = date_select(:person, :birth_date, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_2i] option[selected=selected]', 'January')
output.should have_tag('select[id=person_birth_date_3i] option[selected=selected]', '2')
end
it "should select attribute values if params does not contain attribute params" do
@person.birth_date = "2009-01-02"
params["person"] = { }
output = date_select(:person, :birth_date, :include_blank => true, :include_seconds => true)
output.should have_tag('select[id=person_birth_date_1i] option[selected=selected]', '2009')
output.should have_tag('select[id=person_birth_date_2i] option[selected=selected]', 'January')
output.should have_tag('select[id=person_birth_date_3i] option[selected=selected]', '2')
end
it "should not select values when attribute value is nil and has no param values" do
@person.birth_date = nil
output = date_select(:person, :birth_date, :include_blank => true, :include_seconds => true)
output.should_not have_tag('select[id=person_birth_date_1i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_2i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_date_3i] option[selected=selected]')
end
end
describe "time_select" do
before :all do
Time.now = Time.mktime(2009,1,1)
end
it "should use param values when attribute is nil" do
params["person"] = {
"birth_time(1i)" => 2000,
"birth_time(2i)" => 1,
"birth_time(3i)" => 1,
"birth_time(4i)" => 12,
"birth_time(5i)" => 13,
"birth_time(6i)" => 14,
}
@person.birth_time = nil
output = time_select(:person, :birth_time, :include_blank => true, :include_seconds => true)
output.should have_tag('input[id=person_birth_time_1i][value=2000]')
output.should have_tag('input[id=person_birth_time_2i][value=1]')
output.should have_tag('input[id=person_birth_time_3i][value=1]')
output.should have_tag('select[id=person_birth_time_4i] option[selected=selected]', '12')
output.should have_tag('select[id=person_birth_time_5i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_time_6i] option[selected=selected]', '14')
end
it "should select attribute values from object if no params" do
@person.birth_time = "13:14:15"
output = time_select(:person, :birth_time, :include_blank => true, :include_seconds => true)
output.should have_tag('input[id=person_birth_time_1i][value=2000]')
output.should have_tag('input[id=person_birth_time_2i][value=1]')
output.should have_tag('input[id=person_birth_time_3i][value=1]')
output.should have_tag('select[id=person_birth_time_4i] option[selected=selected]', '13')
output.should have_tag('select[id=person_birth_time_5i] option[selected=selected]', '14')
output.should have_tag('select[id=person_birth_time_6i] option[selected=selected]', '15')
end
it "should not select values when attribute value is nil and has no param values" do
@person.birth_time = nil
output = time_select(:person, :birth_time, :include_blank => true, :include_seconds => true)
output.should have_tag('input[id=person_birth_time_1i][value=""]')
# Annoyingly these may or not have value attribute depending on rails version.
# output.should have_tag('input[id=person_birth_time_2i][value=""]')
# output.should have_tag('input[id=person_birth_time_3i][value=""]')
output.should_not have_tag('select[id=person_birth_time_4i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_time_5i] option[selected=selected]')
output.should_not have_tag('select[id=person_birth_time_6i] option[selected=selected]')
end
after :all do
Time.now = nil
end
end
end

View File

@@ -5,9 +5,12 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
@person = Person.new
end
it "should call write_date_time_attribute when date attribute assigned value" do
@person.should_receive(:write_date_time_attribute)
@person.birth_date = "2000-01-01"
it "should define and call write method on first assign" do
Person.class_eval { @generated_methods = Set.new; @_defined_class_methods = nil }
Person.send(:undef_method, :birth_date=) if Person.instance_methods.include?('birth_date=')
person = Person.new
person.should_receive(:write_date_time_attribute)
person.birth_date = "2000-01-01"
end
it "should call write_date_time_attribute when time attribute assigned value" do
@@ -78,7 +81,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
it "should not save invalid date value to database" do
time_string = "2000-01-32 02:03:04"
@person = Person.new
@person.birth_date_and_time = time_string
@person.save
@person.reload
@@ -90,11 +92,10 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
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'
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' UTC'
end
else
@@ -109,7 +110,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
it "should return time object from database in correct timezone" do
Time.zone = 'Melbourne'
time_string = "2000-06-01 09:00:00"
@person = Person.new
@person.birth_date_and_time = time_string
@person.save
@person.reload
@@ -121,7 +121,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
it "should return correct date value after new value assigned" do
today = Date.today
tomorrow = Date.today + 1.day
@person = Person.new
@person.birth_date = today
@person.birth_date.should == today
@person.birth_date = tomorrow
@@ -131,11 +130,28 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
it "should update date attribute on existing object" do
today = Date.today
tomorrow = Date.today + 1.day
@person = Person.create(:birth_date => today)
@person.birth_date = tomorrow
@person.save!
@person.reload
@person.birth_date.should == tomorrow
person = Person.create(:birth_date => today)
person.birth_date = tomorrow
person.save!
person.reload
person.birth_date.should == tomorrow
end
describe "attribute writer" do
it "should be able to be overridden in class" do
Person.class_eval { attr_accessor :birth_date }
person = Person.new
person.birth_date = 'overriden'
person.birth_date.should == 'overriden'
end
after do
Person.class_eval do
undef_method(:birth_date)
undef_method(:birth_date=)
end
end
end
end

View File

@@ -21,6 +21,8 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
end
describe "execute_callstack_for_multiparameter_attributes" do
describe "for valid values" do
before do
@callstack = {
'birth_date_and_time' => [2000,2,1,9,10,11],
@@ -45,6 +47,70 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
end
end
describe "for invalid values" do
before do
@callstack = {
'birth_date_and_time' => [2000,13,1,9,10,11],
'birth_date' => [2000,2,41,9,10,11],
'birth_time' => [2000,2,1,25,10,11]
}
end
it "should store invalid datetime string for datetime column" do
obj.should_receive(:birth_date_and_time=).once.with("2000-13-01 09:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store invalid date string for a date column" do
obj.should_receive(:birth_date=).once.with("2000-02-41")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
it "should store invalid time string for a time column" do
obj.should_receive(:birth_time=).once.with("25:10:11")
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
end
describe "for missing values" do
it "should store nil if all datetime values nil" do
obj.should_receive(:birth_date_and_time=).once.with(nil)
callstack = { 'birth_date_and_time' => [nil,nil,nil,nil,nil,nil] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil year as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("-02-01 09:10:11")
callstack = { 'birth_date_and_time' => [nil,2,1,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil month as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000--01 09:10:11")
callstack = { 'birth_date_and_time' => [2000,nil,1,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil day as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02- 09:10:11")
callstack = { 'birth_date_and_time' => [2000,2,nil,9,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil hour as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 :10:11")
callstack = { 'birth_date_and_time' => [2000,2,1,nil,10,11] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
it "should store nil minute as empty value in string" do
obj.should_receive(:birth_date_and_time=).once.with("2000-02-01 09:10:")
callstack = { 'birth_date_and_time' => [2000,2,1,9,10,nil] }
obj.send(:execute_callstack_for_multiparameter_attributes, callstack)
end
end
end
def time_array_to_string(*args)
ValidatesTimeliness::ActiveRecord.time_array_to_string(*args)
end

View File

@@ -32,7 +32,7 @@ describe ValidatesTimeliness::Formats do
end
end
describe "validation regexps" do
describe "validate regexps" do
describe "for time formats" do
format_tests = {
@@ -102,6 +102,13 @@ describe ValidatesTimeliness::Formats do
time_array.should == [2000,1,1,12,13,14,0]
end
it "should return nil if time hour is out of range for AM meridian" do
time_array = formats.parse('13:14 am', :time, :strict => true)
time_array.should == nil
time_array = formats.parse('00:14 am', :time, :strict => true)
time_array.should == nil
end
it "should return date array from time string" do
time_array = formats.parse('2000-02-01', :date, :strict => true)
time_array.should == [2000,2,1,0,0,0,0]
@@ -132,7 +139,7 @@ describe ValidatesTimeliness::Formats do
time_array.should == [2000,1,1,12,13,0,0]
end
it "should return zone offset when :include_offset options is true" do
it "should return zone offset when :include_offset option is true" do
time_array = formats.parse('2000-02-01T12:13:14-10:30', :datetime, :include_offset => true)
time_array.should == [2000,2,1,12,13,14,0,-37800]
end
@@ -188,6 +195,18 @@ describe ValidatesTimeliness::Formats do
end
end
describe "parse ISO8601 datetime" do
it "should return array without zone offset when no offset in string" do
time_array = formats.parse('2000-02-01T12:13:14Z', :datetime, :strict => true)
time_array.should == [2000,2,1,12,13,14,0]
end
it "should return array with zone offset when offset in string" do
time_array = formats.parse('2000-02-01T12:13:14+10:00', :datetime, :strict => true)
time_array.should == [2000,2,1,12,13,14,0,36000]
end
end
describe "removing formats" do
it "should remove format from format array" do
formats.remove_formats(:time, 'h.nn_ampm')
@@ -277,15 +296,15 @@ describe ValidatesTimeliness::Formats do
def generate_regexp(format)
# wrap in line start and end anchors to emulate extract values method
/\A#{formats.send(:format_expression_generator, format)[0]}\Z/
/\A#{formats.send(:generate_format_expression, format)[0]}\Z/
end
def generate_regexp_str(format)
formats.send(:format_expression_generator, format)[0].inspect
formats.send(:generate_format_expression, format)[0].inspect
end
def generate_proc(format)
formats.send(:format_expression_generator, format)[1]
formats.send(:generate_format_expression, format)[1]
end
def delete_format(type, format)

View File

@@ -2,14 +2,14 @@
#
# To use ginger:
#
# sudo gem install freelancing-god-ginger --source=http://gems.github.com
# gem install ginger
#
# Then run
#
# ginger spec
#
Ginger.configure do |config|
rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.3', '2.3.4']
rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.3', '2.3.4', '2.3.5', '2.3.6', '2.3.9']
rails_versions.each do |v|
g = Ginger::Scenario.new("Rails #{v}")

View File

@@ -8,6 +8,10 @@ describe ValidatesTimeliness::Parser do
parse("2000-01-01 12:13:14", :datetime).should be_kind_of(Time)
end
it "should return Time object for ISO 8601 string with time zone" do
parse("2000-01-01T12:23:42+09:00", :datetime).should be_kind_of(Time)
end
it "should return nil for time string with invalid date part" do
parse("2000-02-30 12:13:14", :datetime).should be_nil
end
@@ -23,8 +27,8 @@ describe ValidatesTimeliness::Parser do
if RAILS_VER >= '2.1'
it "should convert time string into current timezone" do
Time.zone = 'Melbourne'
time = parse("2000-01-01 12:13:14", :datetime)
Time.zone.utc_offset.should == 10.hours
time = parse("2000-06-01 12:13:14", :datetime)
time.utc_offset.should == 10.hours
end
end

View File

@@ -6,7 +6,7 @@ end
class WithValidation < Person
validates_date :birth_date,
:equal_to => '2000-01-01',
:is_at => '2000-01-01',
:before => '2000-01-10',
:after => '2000-01-01',
:on_or_before => '2000-01-09',
@@ -14,7 +14,7 @@ class WithValidation < Person
:between => ['2000-01-01', '2000-01-03']
validates_time :birth_time,
:equal_to => '09:00',
:is_at => '09:00',
:before => '23:00',
:after => '09:00',
:on_or_before => '22:00',
@@ -22,7 +22,7 @@ class WithValidation < Person
:between => ['09:00', '17:00']
validates_datetime :birth_date_and_time,
:equal_to => '2000-01-01 09:00',
:is_at => '2000-01-01 09:00',
:before => '2000-01-10 23:00',
:after => '2000-01-01 09:00',
:on_or_before => '2000-01-09 23:00',
@@ -65,7 +65,7 @@ describe "ValidateTimeliness matcher" do
end
end
describe "with equal_to option" do
describe "with is_at option" do
test_values = {
:date => ['2000-01-01', '2000-01-02'],
:time => ['09:00', '09:01'],
@@ -75,15 +75,15 @@ describe "ValidateTimeliness matcher" do
[:date, :time, :datetime].each do |type|
it "should report that #{type} is validated" do
with_validation.should self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][0])
with_validation.should self.send("validate_#{type}", attribute_for_type(type), :is_at => test_values[type][0])
end
it "should report that #{type} is not validated when option value is incorrect" do
with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][1])
with_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :is_at => test_values[type][1])
end
it "should report that #{type} is not validated with option" do
no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :equal_to => test_values[type][0])
no_validation.should_not self.send("validate_#{type}", attribute_for_type(type), :is_at => test_values[type][0])
end
end
end

View File

@@ -47,6 +47,8 @@ end
require 'validates_timeliness'
require 'validates_timeliness/matcher'
ValidatesTimeliness.enable_datetime_select_extension!
ActiveRecord::Migration.verbose = false
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})

View File

@@ -3,6 +3,14 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe ValidatesTimeliness::Validator do
attr_accessor :person, :validator
if ValidatesTimeliness::I18N_LATEST
I18N_REGEXP = /\%\{\w*\}/
I18N_INTERPOLATION = '%{%s}'
else
I18N_REGEXP = /\{\{\w*\}\}/
I18N_INTERPOLATION = '{{%s}}'
end
before :all do
# freezes time using time_travel plugin
Time.now = Time.utc(2000, 1, 1, 0, 0, 0)
@@ -12,13 +20,17 @@ describe ValidatesTimeliness::Validator do
Time.now = nil
end
before :each do
@person = Person.new
def person
@person ||= Person.new
end
describe "option keys validation" do
before(:all) do
ActiveSupport::Deprecation.silenced = true
end
before do
keys = ValidatesTimeliness::Validator::VALID_OPTIONS - [:invalid_date_message, :invalid_time_message, :with_date, :with_time]
keys = ValidatesTimeliness::Validator::VALID_OPTION_KEYS - [:invalid_date_message, :invalid_time_message, :with_date, :with_time]
@valid_options = keys.inject({}) {|hash, opt| hash[opt] = nil; hash }
end
@@ -30,6 +42,15 @@ describe ValidatesTimeliness::Validator do
it "should not raise error if option keys are valid" do
lambda { Person.validates_datetime(@valid_options) }.should_not raise_error(ArgumentError)
end
it "should display deprecation notice for :equal_to" do
::ActiveSupport::Deprecation.should_receive(:warn)
Person.validates_datetime :equal_to => Time.now
end
after(:all) do
ActiveSupport::Deprecation.silenced = false
end
end
describe "evaluate_option_value" do
@@ -346,53 +367,53 @@ describe ValidatesTimeliness::Validator do
end
end
describe "instance with :equal_to restriction" do
describe "instance with :is_at restriction" do
describe "for datetime type" do
before do
configure_validator(:equal_to => Time.now)
configure_validator(:is_at => Time.now)
end
it "should have error when value not equal to :equal_to restriction" do
it "should have error when value not equal to :is_at restriction" do
validate_with(:birth_date_and_time, Time.now + 1)
should_have_error(:birth_date_and_time, :equal_to)
should_have_error(:birth_date_and_time, :is_at)
end
it "should be valid when value is equal to :equal_to restriction" do
it "should be valid when value is equal to :is_at restriction" do
validate_with(:birth_date_and_time, Time.now)
should_have_no_error(:birth_date_and_time, :equal_to)
should_have_no_error(:birth_date_and_time, :is_at)
end
end
describe "for date type" do
before do
configure_validator(:type => :date, :equal_to => Date.today)
configure_validator(:type => :date, :is_at => Date.today)
end
it "should have error when value is not equal to :equal_to restriction" do
it "should have error when value is not equal to :is_at restriction" do
validate_with(:birth_date, Date.today + 1)
should_have_error(:birth_date, :equal_to)
should_have_error(:birth_date, :is_at)
end
it "should be valid when value is equal to :equal_to restriction" do
it "should be valid when value is equal to :is_at restriction" do
validate_with(:birth_date, Date.today)
should_have_no_error(:birth_date, :equal_to)
should_have_no_error(:birth_date, :is_at)
end
end
describe "for time type" do
before do
configure_validator(:type => :time, :equal_to => "09:00:00")
configure_validator(:type => :time, :is_at => "09:00:00")
end
it "should have error when value is not equal to :equal_to restriction" do
it "should have error when value is not equal to :is_at restriction" do
validate_with(:birth_time, "09:00:01")
should_have_error(:birth_time, :equal_to)
should_have_error(:birth_time, :is_at)
end
it "should be valid when value is equal to :equal_to restriction" do
it "should be valid when value is equal to :is_at restriction" do
validate_with(:birth_time, "09:00:00")
should_have_no_error(:birth_time, :equal_to)
should_have_no_error(:birth_time, :is_at)
end
end
end
@@ -400,9 +421,9 @@ describe ValidatesTimeliness::Validator do
describe "instance with :ignore_usec option" do
it "should ignore usec on time values when evaluated" do
configure_validator(:equal_to => Time.utc(2000, 1, 1, 0, 0, 0, 0), :ignore_usec => true)
configure_validator(:is_at => Time.utc(2000, 1, 1, 0, 0, 0, 0), :ignore_usec => true)
validate_with(:birth_date_and_time, Time.utc(2000, 1, 1, 0, 0, 0, 500))
should_have_no_error(:birth_date_and_time, :equal_to)
should_have_no_error(:birth_date_and_time, :is_at)
end
end
@@ -422,9 +443,9 @@ describe ValidatesTimeliness::Validator do
end
it "should should ignore usec value on combined value if :ignore_usec option is true" do
configure_validator(:type => :date, :with_time => Time.mktime(2000,1,1,12,30,0,500), :equal_to => Time.mktime(2000,1,1,12,30), :ignore_usec => true)
configure_validator(:type => :date, :with_time => Time.mktime(2000,1,1,12,30,0,500), :is_at => Time.mktime(2000,1,1,12,30), :ignore_usec => true)
validate_with(:birth_date, "2000-01-01")
should_have_no_error(:birth_date, :equal_to)
should_have_no_error(:birth_date, :is_at)
end
end
@@ -496,6 +517,39 @@ describe ValidatesTimeliness::Validator do
end
end
if defined?(I18n)
describe "localized error messages" do
before(:all) do
message = "retfa #{I18N_INTERPOLATION}" % 'restriction'
translations = {
:activerecord => {:errors => {:messages => { :after => message }}},
:validates_timeliness => {:error_value_formats => {}}
}
I18n.backend.store_translations 'zz', translations
I18n.locale = :zz
end
it "should be used if defined" do
configure_validator(:type => :date, :after => Date.today)
validate_with(:birth_date, 1.day.ago)
person.errors.on(:birth_date).should match(/retfa/)
end
it "should use I18n translation missing message when not defined" do
configure_validator(:type => :date, :on_or_after => Date.today)
validate_with(:birth_date, 1.day.ago)
person.errors.on(:birth_date).should match(/translation missing/)
end
after(:all) do
I18n.locale = :en
end
end
end
describe "custom_error_messages" do
it "should return hash of custom error messages from configuration with _message truncated from keys" do
configure_validator(:type => :date, :invalid_date_message => 'thats no date')
@@ -566,6 +620,25 @@ describe ValidatesTimeliness::Validator do
validate_with(:birth_time, '11:59')
person.errors.on(:birth_time).should match(/after \d{2}:\d{2}:\d{2}\Z/)
end
if defined?(I18n)
describe "I18n" do
it "should use global default if locale format missing" do
message = "after #{I18N_INTERPOLATION}" % 'restriction'
I18n.backend.store_translations 'zz', :activerecord => {:errors => {:messages => { :after => message }}}
I18n.locale = :zz
configure_validator(:type => :datetime, :after => 1.day.from_now)
validate_with(:birth_date_and_time, Time.now)
person.errors.on(:birth_date_and_time).should match(/after \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\Z/)
end
after do
I18n.locale = :en
end
end
end
end
describe "custom formats" do
@@ -644,7 +717,7 @@ describe ValidatesTimeliness::Validator do
def error_messages
return @error_messages if defined?(@error_messages)
messages = validator.send(:error_messages)
@error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.sub(/ (\%s|\{\{\w*\}\}).*/, ''); h }
messages = defined?(I18n) ? I18n.t('activerecord.errors.messages') : validator.send(:error_messages)
@error_messages = messages.inject({}) {|h, (k, v)| h[k] = v.sub(/ (\%s|#{I18N_REGEXP}).*/, ''); h }
end
end

View File

@@ -2,27 +2,27 @@
Gem::Specification.new do |s|
s.name = %q{validates_timeliness}
s.version = "2.2.1"
s.version = "2.3.2"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Adam Meehan"]
s.autorequire = %q{validates_timeliness}
s.date = %q{2009-09-12}
s.date = %q{2010-11-07}
s.description = %q{Date and time validation plugin for Rails 2.x which allows custom formats}
s.email = %q{adam.meehan@gmail.com}
s.extra_rdoc_files = ["README.rdoc", "LICENSE", "TODO", "CHANGELOG"]
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/version.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/matcher.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/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.files = ["LICENSE", "README.rdoc", "Rakefile", "TODO", "CHANGELOG", "lib/validates_timeliness", "lib/validates_timeliness/action_view", "lib/validates_timeliness/action_view/instance_tag.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/formats.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.new.yml", "lib/validates_timeliness/locale/en.old.yml", "lib/validates_timeliness/matcher.rb", "lib/validates_timeliness/parser.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/validation_methods.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/version.rb", "lib/validates_timeliness.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/active_record", "spec/active_record/attribute_methods_spec.rb", "spec/active_record/multiparameter_attributes_spec.rb", "spec/formats_spec.rb", "spec/ginger_scenarios.rb", "spec/parser_spec.rb", "spec/resources", "spec/resources/application.rb", "spec/resources/person.rb", "spec/resources/schema.rb", "spec/resources/sqlite_patch.rb", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/spec_helper.rb", "spec/time_travel", "spec/time_travel/MIT-LICENSE", "spec/time_travel/time_extensions.rb", "spec/time_travel/time_travel.rb", "spec/validator_spec.rb"]
s.homepage = %q{http://github.com/adzap/validates_timeliness}
s.require_paths = ["lib"]
s.rubyforge_project = %q{validatestime}
s.rubygems_version = %q{1.3.3}
s.rubygems_version = %q{1.3.7}
s.summary = %q{Date and time validation plugin for Rails 2.x which allows custom formats}
if s.respond_to? :specification_version then
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
else
end
else