mirror of
https://github.com/ditkrg/validates_timeliness.git
synced 2026-01-25 15:22:58 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88fce1d679 | ||
|
|
ffd8476f74 | ||
|
|
728c5ccda5 | ||
|
|
4f0c81b6f8 | ||
|
|
11e643c0fe | ||
|
|
d1ee94248b | ||
|
|
eecef62de4 | ||
|
|
fae38cfecd | ||
|
|
515a1b3126 | ||
|
|
736b1b582f | ||
|
|
903850bc23 | ||
|
|
c3f3edf324 | ||
|
|
1dbac5190b | ||
|
|
497a97e0b0 | ||
|
|
9dd3282a81 | ||
|
|
e7e9a8b238 | ||
|
|
cb962d1157 | ||
|
|
403a91addf | ||
|
|
7ca84f3116 | ||
|
|
19457a6c1d | ||
|
|
5e85649a34 | ||
|
|
e76c53a295 | ||
|
|
f93720177b | ||
|
|
1181b725d0 | ||
|
|
12aa78271e | ||
|
|
862b41f903 | ||
|
|
904c202fb4 | ||
|
|
1001d29c01 | ||
|
|
29c23a7a26 | ||
|
|
7ef9078369 | ||
|
|
a1ae5f9313 | ||
|
|
b3e235a8a1 | ||
|
|
71583805c8 | ||
|
|
2ee971623c | ||
|
|
817e49940c | ||
|
|
a76fc112e7 | ||
|
|
575ff85346 | ||
|
|
7ed76b5161 | ||
|
|
65ed8a657e | ||
|
|
360108c39f | ||
|
|
0ad8ace335 | ||
|
|
7c9ec695f4 |
22
CHANGELOG
22
CHANGELOG
@@ -1,3 +1,25 @@
|
|||||||
|
= 1.1.7 [2009-03-26]
|
||||||
|
- Minor change to multiparameter attributes which I had not properly implemented for chaining
|
||||||
|
|
||||||
|
= 1.1.6 [2009-03-19]
|
||||||
|
- Rail 2.3 support
|
||||||
|
- Added :with_date and :with_time options. They allow an attribute to be combined with another attribute or value to make a datetime value for validation against the temporal restrictions
|
||||||
|
- Added :equal_to option
|
||||||
|
- Option key validation
|
||||||
|
- Better behaviour with other plugins using alias_method_chain on read_attribute and define_attribute_methods
|
||||||
|
- Added option to enable datetime_select extension for future use to optionally enable. Enabled by default until version 2.
|
||||||
|
- Added :ignore_usec option for datetime restrictions to be compared without microsecond
|
||||||
|
- some refactoring
|
||||||
|
|
||||||
|
= 1.1.5 [2009-01-21]
|
||||||
|
- Fixed regex for 'yy' format token which wasn't greedy enough for date formats ending with year when a datetime string parsed as date with a 4 digit year
|
||||||
|
|
||||||
|
= 1.1.4 [2009-01-13]
|
||||||
|
- Make months names respect i18n in Formats
|
||||||
|
|
||||||
|
= 1.1.3 [2009-01-13]
|
||||||
|
- Fixed bug where time and date attributes still being parsed on read using Rails default parser [reported by Brad (pvjq)]
|
||||||
|
|
||||||
= 1.1.2 [2009-01-12]
|
= 1.1.2 [2009-01-12]
|
||||||
- Fixed bugs
|
- Fixed bugs
|
||||||
- matcher failing for custom error message without interpolation keys using I18n
|
- matcher failing for custom error message without interpolation keys using I18n
|
||||||
|
|||||||
157
README.rdoc
157
README.rdoc
@@ -33,12 +33,16 @@ think should be a valid date or time string.
|
|||||||
|
|
||||||
As plugin (from master)
|
As plugin (from master)
|
||||||
|
|
||||||
./script/plugin git://github.com/adzap/validates_timeliness.git
|
./script/plugin install git://github.com/adzap/validates_timeliness.git
|
||||||
|
|
||||||
As gem
|
As gem
|
||||||
|
|
||||||
sudo gem install validates_timeliness
|
sudo gem install validates_timeliness
|
||||||
|
|
||||||
|
# in environment.rb
|
||||||
|
|
||||||
|
config.gem 'validates_timeliness'
|
||||||
|
|
||||||
|
|
||||||
== USAGE:
|
== USAGE:
|
||||||
|
|
||||||
@@ -51,50 +55,54 @@ validation method
|
|||||||
end
|
end
|
||||||
|
|
||||||
The list of validation methods available are as follows:
|
The list of validation methods available are as follows:
|
||||||
|
validates_date - validate value as date
|
||||||
* validates_date - validate value as date
|
validates_time - validate value as time only i.e. '12:20pm'
|
||||||
|
validates_datetime - validate value as a full date and time
|
||||||
* validates_time - validate value as time only i.e. '12:20pm'
|
|
||||||
|
|
||||||
* validates_datetime - validate value as a full date and time
|
|
||||||
|
|
||||||
The validation methods take the usual options plus some specific ones to restrict
|
The validation methods take the usual options plus some specific ones to restrict
|
||||||
the valid range of dates or times allowed
|
the valid range of dates or times allowed
|
||||||
|
|
||||||
Temporal options (or restrictions):
|
Temporal options (or restrictions):
|
||||||
:before - Attribute must be before this value to be valid
|
:equal_to - Attribute must be equal to value to be valid
|
||||||
:on_or_before - Attribute must be equal to or before this value to be valid
|
:before - Attribute must be before this value to be valid
|
||||||
:after - Attribute must be after this value to be valid
|
:on_or_before - Attribute must be equal to or before this value to be valid
|
||||||
:on_or_after - Attribute must be equal to or after this value to be valid
|
:after - Attribute must be after this value to be valid
|
||||||
:between - Attribute must be between the values to be valid
|
:on_or_after - Attribute must be equal to or after this value to be valid
|
||||||
|
:between - Attribute must be between the values to be valid. Takes an array of two values or a range
|
||||||
|
|
||||||
Regular validation options:
|
Regular validation options:
|
||||||
:allow_nil - Allow a nil value to be valid
|
:allow_nil - Allow a nil value to be valid
|
||||||
:allow_blank - Allows a nil or empty string value to be valid
|
:allow_blank - Allows a nil or empty string value to be valid
|
||||||
:if - Execute validation when :if evaluates true
|
:if - Execute validation when :if evaluates true
|
||||||
:unless - Execute validation when :unless evaluates false
|
:unless - Execute validation when :unless evaluates false
|
||||||
|
|
||||||
Message options: - Use these to override the default error messages
|
Special options:
|
||||||
:invalid_date_message
|
:with_time - Validate a date attribute value combined with a time value against any temporal restrictions
|
||||||
:invalid_time_message
|
:with_date - Validate a time attribute value combined with a date value against any temporal restrictions
|
||||||
:invalid_datetime_message
|
:ignore_usec - Ignores microsecond value on datetime restrictions
|
||||||
:before_message
|
|
||||||
:on_or_before_message
|
Message options: - Use these to override the default error messages
|
||||||
:after_message
|
:invalid_date_message
|
||||||
:on_or_after_message
|
:invalid_time_message
|
||||||
:between_message
|
:invalid_datetime_message
|
||||||
|
:equal_to_message
|
||||||
|
:before_message
|
||||||
|
:on_or_before_message
|
||||||
|
:after_message
|
||||||
|
:on_or_after_message
|
||||||
|
:between_message
|
||||||
|
|
||||||
The temporal restrictions can take 4 different value types:
|
The temporal restrictions, with_date and with_time can take 4 different value types:
|
||||||
|
* String value
|
||||||
* String value
|
* Date, Time, or DateTime object value
|
||||||
* Date, Time, or DateTime object value
|
* Proc or lambda object which may take an optional parameter being the record object
|
||||||
* Proc or lambda object
|
* A symbol matching the method name in the model
|
||||||
* A symbol matching the method name in the model
|
|
||||||
* Between option takes an array of two values or a range
|
|
||||||
|
|
||||||
When an attribute value is compared to temporal restrictions, they are compared as
|
When an attribute value is compared to temporal restrictions, they are compared as
|
||||||
the same type as the validation method type. So using validates_date means all
|
the same type as the validation method type. So using validates_date means all
|
||||||
values are compared as dates.
|
values are compared as dates. This is except in the case of with_time and with_date
|
||||||
|
options which effectively force the value to validated as a datetime against the
|
||||||
|
temporal options.
|
||||||
|
|
||||||
== EXAMPLES:
|
== EXAMPLES:
|
||||||
|
|
||||||
@@ -107,6 +115,8 @@ values are compared as dates.
|
|||||||
:allow_nil => true
|
:allow_nil => true
|
||||||
|
|
||||||
validates_datetime :appointment_date, :before => Proc.new { 1.week.from_now }
|
validates_datetime :appointment_date, :before => Proc.new { 1.week.from_now }
|
||||||
|
|
||||||
|
validates_date :entry_date, :with_time => '17:00', :on_or_before => :competition_closing
|
||||||
|
|
||||||
|
|
||||||
=== DATE/TIME FORMATS:
|
=== DATE/TIME FORMATS:
|
||||||
@@ -120,44 +130,43 @@ be happy to know that is exactly the format you can use to define your own if
|
|||||||
you want. No complex regular expressions or duck punching (monkey patching) the
|
you want. No complex regular expressions or duck punching (monkey patching) the
|
||||||
plugin is needed.
|
plugin is needed.
|
||||||
|
|
||||||
Time formats:
|
==== Time formats:
|
||||||
hh:nn:ss
|
hh:nn:ss
|
||||||
hh-nn-ss
|
hh-nn-ss
|
||||||
h:nn
|
h:nn
|
||||||
h.nn
|
h.nn
|
||||||
h nn
|
h nn
|
||||||
h-nn
|
h-nn
|
||||||
h:nn_ampm
|
h:nn_ampm
|
||||||
h.nn_ampm
|
h.nn_ampm
|
||||||
h nn_ampm
|
h nn_ampm
|
||||||
h-nn_ampm
|
h-nn_ampm
|
||||||
h_ampm
|
h_ampm
|
||||||
|
|
||||||
NOTE: Any time format without a meridian token (the 'ampm' token) is considered
|
|
||||||
in 24 hour time.
|
|
||||||
|
|
||||||
Date formats:
|
|
||||||
yyyy/mm/dd
|
|
||||||
yyyy-mm-dd
|
|
||||||
yyyy.mm.dd
|
|
||||||
m/d/yy OR d/m/yy
|
|
||||||
m\d\yy OR d\m\yy
|
|
||||||
d-m-yy
|
|
||||||
d.m.yy
|
|
||||||
d mmm yy
|
|
||||||
|
|
||||||
NOTE: To use non-US date formats see US/EURO FORMATS section
|
|
||||||
|
|
||||||
Datetime formats:
|
|
||||||
m/d/yy h:nn:ss OR d/m/yy hh:nn:ss
|
|
||||||
m/d/yy h:nn OR d/m/yy h:nn
|
|
||||||
m/d/yy h:nn_ampm OR d/m/yy h:nn_ampm
|
|
||||||
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
|
|
||||||
|
|
||||||
NOTE: To use non-US date formats see US/EURO FORMATS section
|
NOTE: Any time format without a meridian token (the 'ampm' token) is considered in 24 hour time.
|
||||||
|
|
||||||
|
==== Date formats:
|
||||||
|
yyyy/mm/dd
|
||||||
|
yyyy-mm-dd
|
||||||
|
yyyy.mm.dd
|
||||||
|
m/d/yy OR d/m/yy
|
||||||
|
m\d\yy OR d\m\yy
|
||||||
|
d-m-yy
|
||||||
|
d.m.yy
|
||||||
|
d mmm yy
|
||||||
|
|
||||||
|
NOTE: To use non-US date formats see US/EURO FORMATS section
|
||||||
|
|
||||||
|
==== Datetime formats:
|
||||||
|
m/d/yy h:nn:ss OR d/m/yy hh:nn:ss
|
||||||
|
m/d/yy h:nn OR d/m/yy h:nn
|
||||||
|
m/d/yy h:nn_ampm OR d/m/yy h:nn_ampm
|
||||||
|
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
|
||||||
|
|
||||||
|
NOTE: To use non-US date formats see US/EURO FORMATS section
|
||||||
|
|
||||||
Here is what each format token means:
|
Here is what each format token means:
|
||||||
|
|
||||||
@@ -219,7 +228,7 @@ Done! That format is no longer considered valid. Easy!
|
|||||||
Ok, now I hear you say "Well I have format that I want to use but you don't have it".
|
Ok, now I hear you say "Well I have format that I want to use but you don't have it".
|
||||||
Ahh, then add it yourself. Again stick this in an initializer file
|
Ahh, then add it yourself. Again stick this in an initializer file
|
||||||
|
|
||||||
ValidatesTimeliness::Formats.add_formats(:time, "d o'clock")
|
ValidatesTimeliness::Formats.add_formats(:time, "d o'clock")
|
||||||
|
|
||||||
Now "10 o'clock" will be a valid value. So easy, no more whingeing!
|
Now "10 o'clock" will be a valid value. So easy, no more whingeing!
|
||||||
|
|
||||||
@@ -234,7 +243,7 @@ with an existing format, will mean your format is ignored. If you need to make
|
|||||||
your new format higher precedence than an existing format, you can include the
|
your new format higher precedence than an existing format, you can include the
|
||||||
before option like so
|
before option like so
|
||||||
|
|
||||||
ValidatesTimeliness::Formats.add_formats(:time, 'ss:nn:hh', :before => 'hh:nn:ss')
|
ValidatesTimeliness::Formats.add_formats(:time, 'ss:nn:hh', :before => 'hh:nn:ss')
|
||||||
|
|
||||||
Now a time of '59:30:23' will be interpreted as 11:30:59 pm. This option saves
|
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.
|
||||||
|
|||||||
5
Rakefile
5
Rakefile
@@ -5,7 +5,7 @@ require 'date'
|
|||||||
require 'spec/rake/spectask'
|
require 'spec/rake/spectask'
|
||||||
|
|
||||||
GEM = "validates_timeliness"
|
GEM = "validates_timeliness"
|
||||||
GEM_VERSION = "1.1.2"
|
GEM_VERSION = "1.1.7"
|
||||||
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"
|
||||||
@@ -24,9 +24,6 @@ spec = Gem::Specification.new do |s|
|
|||||||
s.email = EMAIL
|
s.email = EMAIL
|
||||||
s.homepage = HOMEPAGE
|
s.homepage = HOMEPAGE
|
||||||
|
|
||||||
# Uncomment this to add a dependency
|
|
||||||
# s.add_dependency "foo"
|
|
||||||
|
|
||||||
s.require_path = 'lib'
|
s.require_path = 'lib'
|
||||||
s.autorequire = GEM
|
s.autorequire = GEM
|
||||||
s.files = %w(LICENSE README.rdoc Rakefile TODO CHANGELOG) + Dir.glob("{lib,spec}/**/*")
|
s.files = %w(LICENSE README.rdoc Rakefile TODO CHANGELOG) + Dir.glob("{lib,spec}/**/*")
|
||||||
|
|||||||
2
TODO
2
TODO
@@ -1,5 +1,3 @@
|
|||||||
- :format option
|
- :format option
|
||||||
- :with_date and :with_time options
|
|
||||||
- valid formats could come from locale file
|
- valid formats could come from locale file
|
||||||
- formats to use month and day names from i18n
|
|
||||||
- add replace_formats instead add_formats :before
|
- add replace_formats instead add_formats :before
|
||||||
|
|||||||
@@ -21,14 +21,20 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def load_error_messages_with_i18n
|
def enable_datetime_select_extension!
|
||||||
I18n.load_path += [ LOCALE_PATH ]
|
enable_datetime_select_invalid_value_extension!
|
||||||
|
enable_multiparameter_attributes_extension!
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_error_messages_without_i18n
|
def load_error_messages
|
||||||
messages = YAML::load(IO.read(LOCALE_PATH))
|
if defined?(I18n)
|
||||||
errors = messages['en']['activerecord']['errors']['messages'].inject({}) {|h,(k,v)| h[k.to_sym] = v.gsub(/\{\{\w*\}\}/, '%s');h }
|
I18n.load_path += [ LOCALE_PATH ]
|
||||||
::ActiveRecord::Errors.default_error_messages.update(errors)
|
I18n.reload!
|
||||||
|
else
|
||||||
|
messages = YAML::load(IO.read(LOCALE_PATH))
|
||||||
|
errors = messages['en']['activerecord']['errors']['messages'].inject({}) {|h,(k,v)| h[k.to_sym] = v.gsub(/\{\{\w*\}\}/, '%s');h }
|
||||||
|
::ActiveRecord::Errors.default_error_messages.update(errors)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_error_messages
|
def default_error_messages
|
||||||
@@ -39,29 +45,13 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_for_rails_2_0
|
|
||||||
load_error_messages_without_i18n
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_for_rails_2_1
|
|
||||||
load_error_messages_without_i18n
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_for_rails_2_2
|
|
||||||
load_error_messages_with_i18n
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_for_rails
|
def setup_for_rails
|
||||||
major, minor = Rails::VERSION::MAJOR, Rails::VERSION::MINOR
|
major, minor = Rails::VERSION::MAJOR, Rails::VERSION::MINOR
|
||||||
self.send("setup_for_rails_#{major}_#{minor}")
|
|
||||||
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
||||||
rescue
|
self.enable_datetime_select_extension!
|
||||||
puts "Rails version #{Rails::VERSION::STRING} not explicitly supported by validates_timeliness plugin. You may encounter some problems."
|
self.load_error_messages
|
||||||
resume
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ValidatesTimeliness.setup_for_rails
|
ValidatesTimeliness.setup_for_rails
|
||||||
|
|
||||||
ValidatesTimeliness::Formats.compile_format_expressions
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
module ValidatesTimeliness
|
module ValidatesTimeliness
|
||||||
|
|
||||||
|
def self.enable_datetime_select_invalid_value_extension!
|
||||||
|
::ActionView::Helpers::InstanceTag.send(:include, ValidatesTimeliness::ActionView::InstanceTag)
|
||||||
|
end
|
||||||
|
|
||||||
module ActionView
|
module ActionView
|
||||||
|
|
||||||
# Intercepts the date and time select helpers to allow the
|
# Intercepts the date and time select helpers to allow the
|
||||||
@@ -41,5 +46,3 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActionView::Helpers::InstanceTag.send(:include, ValidatesTimeliness::ActionView::InstanceTag)
|
|
||||||
|
|||||||
@@ -14,22 +14,14 @@ module ValidatesTimeliness
|
|||||||
# will not be in the attribute cache on first read so will be considered in default
|
# 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
|
# 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.
|
# hash and cached to avoid the need for any subsequent differentiation.
|
||||||
#
|
|
||||||
# The wholesale replacement of the Rails time type casting is not done to
|
|
||||||
# preserve the quickest conversion for timestamp columns and also any value
|
|
||||||
# which is never changed during the life of the record object.
|
|
||||||
module AttributeMethods
|
module AttributeMethods
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.extend ClassMethods
|
base.extend ClassMethods
|
||||||
|
base.class_eval do
|
||||||
if Rails::VERSION::STRING < '2.1'
|
alias_method_chain :read_attribute, :timeliness
|
||||||
base.class_eval do
|
class << self
|
||||||
class << self
|
alias_method_chain :define_attribute_methods, :timeliness
|
||||||
def create_time_zone_conversion_attribute?(name, column)
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -38,110 +30,95 @@ module ValidatesTimeliness
|
|||||||
# and value can be used from cache. This prevents the raw date/time value from
|
# 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
|
# being type cast using default Rails type casting when writing values
|
||||||
# to the database.
|
# to the database.
|
||||||
def read_attribute(attr_name)
|
def read_attribute_with_timeliness(attr_name)
|
||||||
attr_name = attr_name.to_s
|
attr_name = attr_name.to_s
|
||||||
if !(value = @attributes[attr_name]).nil?
|
if !(value = @attributes[attr_name]).nil?
|
||||||
if column = column_for_attribute(attr_name)
|
column = column_for_attribute(attr_name)
|
||||||
if unserializable_attribute?(attr_name, column)
|
if column && [:date, :time, :datetime].include?(column.type) && @attributes_cache.has_key?(attr_name)
|
||||||
unserialize_attribute(attr_name)
|
return @attributes_cache[attr_name]
|
||||||
elsif [:date, :time, :datetime].include?(column.type) && @attributes_cache.has_key?(attr_name)
|
|
||||||
@attributes_cache[attr_name]
|
|
||||||
else
|
|
||||||
column.type_cast(value)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
value
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
|
read_attribute_without_timeliness(attr_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Writes attribute value by storing raw value in attributes hash,
|
|
||||||
# then convert it with parser and cache it.
|
|
||||||
#
|
|
||||||
# If Rails dirty attributes is enabled then the value is added to
|
# If Rails dirty attributes is enabled then the value is added to
|
||||||
# changed attributes if changed. Can't use the default dirty checking
|
# changed attributes if changed. Can't use the default dirty checking
|
||||||
# implementation as it chains the write_attribute method which deletes
|
# implementation as it chains the write_attribute method which deletes
|
||||||
# the attribute from the cache.
|
# the attribute from the cache.
|
||||||
def write_date_time_attribute(attr_name, value)
|
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
||||||
column = column_for_attribute(attr_name)
|
new = self.class.parse_date_time(value, type)
|
||||||
old = read_attribute(attr_name) if defined?(::ActiveRecord::Dirty)
|
|
||||||
new = self.class.parse_date_time(value, column.type)
|
|
||||||
|
|
||||||
unless column.type == :date || new.nil?
|
if new && type != :date
|
||||||
new = new.to_time rescue new
|
new = new.to_time
|
||||||
|
new = new.in_time_zone if time_zone_aware
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.class.send(:create_time_zone_conversion_attribute?, attr_name, column)
|
if defined?(::ActiveRecord::Dirty) && !changed_attributes.include?(attr_name)
|
||||||
new = new.in_time_zone rescue nil
|
old = read_attribute(attr_name)
|
||||||
|
if old != new
|
||||||
|
changed_attributes[attr_name] = (old.duplicable? ? old.clone : old)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@attributes_cache[attr_name] = new
|
@attributes_cache[attr_name] = new
|
||||||
|
|
||||||
if defined?(::ActiveRecord::Dirty) && !changed_attributes.include?(attr_name) && old != new
|
|
||||||
changed_attributes[attr_name] = (old.duplicable? ? old.clone : old)
|
|
||||||
end
|
|
||||||
@attributes[attr_name] = value
|
@attributes[attr_name] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If reloading then check if cached, which means its in local time.
|
||||||
|
# If local, convert with parser as local timezone, otherwise use
|
||||||
|
# read_attribute method for quick default type cast of values from
|
||||||
|
# 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 = self.class.parse_date_time(time, type)
|
||||||
|
else
|
||||||
|
time = read_attribute(attr_name)
|
||||||
|
@attributes[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
||||||
|
end
|
||||||
|
@attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
||||||
|
end
|
||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
# Override AR method to define attribute reader and writer method for
|
# Define attribute reader and writer method for date, time and
|
||||||
# date, time and datetime attributes to use plugin parser.
|
# datetime attributes to use plugin parser.
|
||||||
def define_attribute_methods
|
def define_attribute_methods_with_timeliness
|
||||||
return if generated_methods?
|
return if generated_methods?
|
||||||
columns_hash.each do |name, column|
|
columns_hash.each do |name, column|
|
||||||
unless instance_method_already_implemented?(name)
|
unless instance_method_already_implemented?(name)
|
||||||
if self.serialized_attributes[name]
|
if [:date, :time, :datetime].include?(column.type)
|
||||||
define_read_method_for_serialized_attribute(name)
|
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
||||||
elsif create_time_zone_conversion_attribute?(name, column)
|
define_read_method_for_dates_and_times(name, column.type, time_zone_aware)
|
||||||
define_read_method_for_time_zone_conversion(name)
|
|
||||||
else
|
|
||||||
define_read_method(name.to_sym, name, column)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
unless instance_method_already_implemented?("#{name}=")
|
unless instance_method_already_implemented?("#{name}=")
|
||||||
if [:date, :time, :datetime].include?(column.type)
|
if [:date, :time, :datetime].include?(column.type)
|
||||||
define_write_method_for_dates_and_times(name)
|
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
||||||
else
|
define_write_method_for_dates_and_times(name, column.type, time_zone_aware)
|
||||||
define_write_method(name.to_sym)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
unless instance_method_already_implemented?("#{name}?")
|
|
||||||
define_question_method(name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
define_attribute_methods_without_timeliness
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define write method for date, time and datetime columns
|
# Define write method for date, time and datetime columns
|
||||||
def define_write_method_for_dates_and_times(attr_name)
|
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
||||||
method_body = <<-EOV
|
method_body = <<-EOV
|
||||||
def #{attr_name}=(value)
|
def #{attr_name}=(value)
|
||||||
write_date_time_attribute('#{attr_name}', value)
|
write_date_time_attribute('#{attr_name}', value, #{type.inspect}, #{time_zone_aware})
|
||||||
end
|
end
|
||||||
EOV
|
EOV
|
||||||
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define time attribute reader. If reloading then check if cached,
|
def define_read_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
||||||
# which means its in local time. If local, convert with parser as local
|
|
||||||
# timezone, otherwise use read_attribute method for quick default type
|
|
||||||
# cast of values from database using default timezone.
|
|
||||||
def define_read_method_for_time_zone_conversion(attr_name)
|
|
||||||
method_body = <<-EOV
|
method_body = <<-EOV
|
||||||
def #{attr_name}(reload = false)
|
def #{attr_name}(reload = false)
|
||||||
cached = @attributes_cache['#{attr_name}']
|
read_date_time_attribute('#{attr_name}', #{type.inspect}, #{time_zone_aware}, reload)
|
||||||
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 = self.class.parse_date_time(date, :datetime)
|
|
||||||
else
|
|
||||||
time = read_attribute('#{attr_name}')
|
|
||||||
@attributes['#{attr_name}'] = time.in_time_zone rescue nil
|
|
||||||
end
|
|
||||||
@attributes_cache['#{attr_name}'] = time.in_time_zone rescue nil
|
|
||||||
end
|
end
|
||||||
EOV
|
EOV
|
||||||
evaluate_attribute_method attr_name, method_body
|
evaluate_attribute_method attr_name, method_body
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
module ValidatesTimeliness
|
module ValidatesTimeliness
|
||||||
module ActiveRecord
|
|
||||||
|
|
||||||
|
def self.enable_multiparameter_attributes_extension!
|
||||||
|
::ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::MultiparameterAttributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ActiveRecord
|
||||||
module MultiparameterAttributes
|
module MultiparameterAttributes
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
@@ -12,18 +16,16 @@ module ValidatesTimeliness
|
|||||||
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|
|
||||||
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
|
column = column_for_attribute(name)
|
||||||
if values.empty?
|
if column && [:date, :time, :datetime].include?(column.type)
|
||||||
send(name + "=", nil)
|
|
||||||
else
|
|
||||||
column = column_for_attribute(name)
|
|
||||||
begin
|
begin
|
||||||
value = if [:date, :time, :datetime].include?(column.type)
|
callstack.delete(name)
|
||||||
time_array_to_string(values, column.type)
|
if values.empty?
|
||||||
|
send("#{name}=", nil)
|
||||||
else
|
else
|
||||||
klass.new(*values)
|
value = time_array_to_string(values, column.type)
|
||||||
|
send("#{name}=", value)
|
||||||
end
|
end
|
||||||
send(name + "=", value)
|
|
||||||
rescue => ex
|
rescue => ex
|
||||||
errors << ::ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
errors << ::ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
|
||||||
end
|
end
|
||||||
@@ -32,19 +34,20 @@ module ValidatesTimeliness
|
|||||||
unless errors.empty?
|
unless errors.empty?
|
||||||
raise ::ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
|
raise ::ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
|
||||||
end
|
end
|
||||||
|
execute_callstack_for_multiparameter_attributes_without_timeliness(callstack)
|
||||||
end
|
end
|
||||||
|
|
||||||
def time_array_to_string(values, type)
|
def time_array_to_string(values, type)
|
||||||
values = values.map(&:to_s)
|
values = values.map {|v| v.to_s }
|
||||||
|
|
||||||
case type
|
case type
|
||||||
when :date
|
when :date
|
||||||
extract_date_from_multiparameter_attributes(values)
|
extract_date_from_multiparameter_attributes(values)
|
||||||
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
|
date_values, time_values = values.slice!(0, 3), values
|
||||||
extract_date_from_multiparameter_attributes(date_values) + " " + extract_time_from_multiparameter_attributes(time_values)
|
extract_date_from_multiparameter_attributes(date_values) + " " + extract_time_from_multiparameter_attributes(time_values)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -53,12 +56,11 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def extract_time_from_multiparameter_attributes(values)
|
def extract_time_from_multiparameter_attributes(values)
|
||||||
values.last(3).map { |s| s.rjust(2, "0") }.join(":")
|
values = values.size > 3 ? values[3..5] : values
|
||||||
|
values.map { |s| s.rjust(2, "0") }.join(":")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::MultiparameterAttributes)
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
require 'date'
|
||||||
|
|
||||||
module ValidatesTimeliness
|
module ValidatesTimeliness
|
||||||
|
|
||||||
# A date and time format regular expression generator. Allows you to
|
# A date and time format regular expression generator. Allows you to
|
||||||
@@ -10,16 +12,14 @@ module ValidatesTimeliness
|
|||||||
# string values.
|
# string values.
|
||||||
#
|
#
|
||||||
class Formats
|
class Formats
|
||||||
cattr_accessor :time_formats
|
cattr_accessor :time_formats,
|
||||||
cattr_accessor :date_formats
|
:date_formats,
|
||||||
cattr_accessor :datetime_formats
|
:datetime_formats,
|
||||||
|
:time_expressions,
|
||||||
cattr_accessor :time_expressions
|
:date_expressions,
|
||||||
cattr_accessor :date_expressions
|
:datetime_expressions,
|
||||||
cattr_accessor :datetime_expressions
|
:format_tokens,
|
||||||
|
:format_proc_args
|
||||||
cattr_accessor :format_tokens
|
|
||||||
cattr_accessor :format_proc_args
|
|
||||||
|
|
||||||
# Format tokens:
|
# Format tokens:
|
||||||
# y = year
|
# y = year
|
||||||
@@ -115,7 +115,7 @@ module ValidatesTimeliness
|
|||||||
{ 'mm' => [ /m{2}/, '(\d{2})', :month ] },
|
{ 'mm' => [ /m{2}/, '(\d{2})', :month ] },
|
||||||
{ 'm' => [ /(\A|[^ap])m{1}/, '(\d{1,2})', :month ] },
|
{ 'm' => [ /(\A|[^ap])m{1}/, '(\d{1,2})', :month ] },
|
||||||
{ 'yyyy' => [ /y{4,}/, '(\d{4})', :year ] },
|
{ 'yyyy' => [ /y{4,}/, '(\d{4})', :year ] },
|
||||||
{ 'yy' => [ /y{2,}/, '(\d{2}|\d{4})', :year ] },
|
{ 'yy' => [ /y{2,}/, '(\d{4}|\d{2})', :year ] },
|
||||||
{ 'hh' => [ /h{2,}/, '(\d{2})', :hour ] },
|
{ 'hh' => [ /h{2,}/, '(\d{2})', :hour ] },
|
||||||
{ 'h' => [ /h{1}/, '(\d{1,2})', :hour ] },
|
{ 'h' => [ /h{1}/, '(\d{1,2})', :hour ] },
|
||||||
{ 'nn' => [ /n{2,}/, '(\d{2})', :min ] },
|
{ 'nn' => [ /n{2,}/, '(\d{2})', :min ] },
|
||||||
@@ -139,13 +139,13 @@ module ValidatesTimeliness
|
|||||||
# should just be the arg name.
|
# should just be the arg name.
|
||||||
#
|
#
|
||||||
@@format_proc_args = {
|
@@format_proc_args = {
|
||||||
:year => [0, 'y', 'unambiguous_year(y)'],
|
:year => [0, 'y', 'unambiguous_year(y)'],
|
||||||
:month => [1, 'm', 'month_index(m)'],
|
:month => [1, 'm', 'month_index(m)'],
|
||||||
:day => [2, 'd', 'd'],
|
:day => [2, 'd', 'd'],
|
||||||
:hour => [3, 'h', 'full_hour(h,md)'],
|
:hour => [3, 'h', 'full_hour(h,md)'],
|
||||||
:min => [4, 'n', 'n'],
|
:min => [4, 'n', 'n'],
|
||||||
:sec => [5, 's', 's'],
|
:sec => [5, 's', 's'],
|
||||||
:usec => [6, 'u', 'microseconds(u)'],
|
:usec => [6, 'u', 'microseconds(u)'],
|
||||||
:meridian => [nil, 'md', nil]
|
:meridian => [nil, 'md', nil]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,17 +163,18 @@ module ValidatesTimeliness
|
|||||||
# Returns 7 part time array.
|
# Returns 7 part time array.
|
||||||
def parse(string, type, strict=true)
|
def parse(string, type, strict=true)
|
||||||
return string unless string.is_a?(String)
|
return string unless string.is_a?(String)
|
||||||
|
|
||||||
expressions = expression_set(type, string)
|
matches = nil
|
||||||
time_array = nil
|
exp, processor = expression_set(type, string).find do |regexp, proc|
|
||||||
expressions.each do |(regexp, processor)|
|
full = /\A#{regexp}\Z/ if strict
|
||||||
regexp = strict || type == :datetime ? /\A#{regexp}\Z/ : (type == :date ? /\A#{regexp}/ : /#{regexp}\Z/)
|
full ||= case type
|
||||||
if matches = regexp.match(string.strip)
|
when :date then /\A#{regexp}/
|
||||||
time_array = processor.call(*matches[1..7])
|
when :time then /#{regexp}\Z/
|
||||||
break
|
when :datetime then /\A#{regexp}\Z/
|
||||||
end
|
end
|
||||||
|
matches = full.match(string.strip)
|
||||||
end
|
end
|
||||||
return time_array
|
processor.call(*matches[1..7]) if matches
|
||||||
end
|
end
|
||||||
|
|
||||||
# Delete formats of specified type. Error raised if format not found.
|
# Delete formats of specified type. Error raised if format not found.
|
||||||
@@ -223,7 +224,7 @@ module ValidatesTimeliness
|
|||||||
def format_expression_generator(string_format)
|
def format_expression_generator(string_format)
|
||||||
regexp = string_format.dup
|
regexp = string_format.dup
|
||||||
order = {}
|
order = {}
|
||||||
regexp.gsub!(/([\.\\])/, '\\\\\1') # escapes dots and backslashes ]/
|
regexp.gsub!(/([\.\\])/, '\\\\\1') # escapes dots and backslashes
|
||||||
|
|
||||||
format_tokens.each do |token|
|
format_tokens.each do |token|
|
||||||
token_name = token.keys.first
|
token_name = token.keys.first
|
||||||
@@ -260,24 +261,24 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def compile_formats(formats)
|
def compile_formats(formats)
|
||||||
formats.collect { |format| regexp, format_proc = format_expression_generator(format) }
|
formats.map { |format| regexp, format_proc = format_expression_generator(format) }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pick expression set and combine date and datetimes for
|
# Pick expression set and combine date and datetimes for
|
||||||
# datetime attributes to allow date string as datetime
|
# datetime attributes to allow date string as datetime
|
||||||
def expression_set(type, string)
|
def expression_set(type, string)
|
||||||
case type
|
case type
|
||||||
when :date
|
when :date
|
||||||
date_expressions
|
date_expressions
|
||||||
when :time
|
when :time
|
||||||
time_expressions
|
time_expressions
|
||||||
when :datetime
|
when :datetime
|
||||||
# gives a speed-up for date string as datetime attributes
|
# gives a speed-up for date string as datetime attributes
|
||||||
if string.length < 11
|
if string.length < 11
|
||||||
date_expressions + datetime_expressions
|
date_expressions + datetime_expressions
|
||||||
else
|
else
|
||||||
datetime_expressions + date_expressions
|
datetime_expressions + date_expressions
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -298,12 +299,22 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
def month_index(month)
|
def month_index(month)
|
||||||
return month.to_i if month.to_i.nonzero?
|
return month.to_i if month.to_i.nonzero?
|
||||||
Date::ABBR_MONTHNAMES.index(month.capitalize) || Date::MONTHNAMES.index(month.capitalize)
|
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
|
||||||
end
|
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)
|
def microseconds(usec)
|
||||||
(".#{usec}".to_f * 1_000_000).to_i
|
(".#{usec}".to_f * 1_000_000).to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ValidatesTimeliness::Formats.compile_format_expressions
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ en:
|
|||||||
invalid_date: "is not a valid date"
|
invalid_date: "is not a valid date"
|
||||||
invalid_time: "is not a valid time"
|
invalid_time: "is not a valid time"
|
||||||
invalid_datetime: "is not a valid datetime"
|
invalid_datetime: "is not a valid datetime"
|
||||||
|
equal_to: "must be equal to {{restriction}}"
|
||||||
before: "must be before {{restriction}}"
|
before: "must be before {{restriction}}"
|
||||||
on_or_before: "must be on or before {{restriction}}"
|
on_or_before: "must be on or before {{restriction}}"
|
||||||
after: "must be after {{restriction}}"
|
after: "must be after {{restriction}}"
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ module Spec
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_and_cast(value)
|
def parse_and_cast(value)
|
||||||
value = @validator.send(:restriction_value, value, @record)
|
value = @validator.class.send(:evaluate_option_value, value, @type, @record)
|
||||||
@validator.send(:type_cast_value, value)
|
@validator.class.send(:type_cast_value, value, @type)
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_matching(value, option)
|
def error_matching(value, option)
|
||||||
@@ -117,11 +117,11 @@ module Spec
|
|||||||
|
|
||||||
def error_message_for(option)
|
def error_message_for(option)
|
||||||
msg = @validator.send(:error_messages)[option]
|
msg = @validator.send(:error_messages)[option]
|
||||||
restriction = @validator.send(:restriction_value, @validator.configuration[option], @record)
|
restriction = @validator.class.send(:evaluate_option_value, @validator.configuration[option], @type, @record)
|
||||||
|
|
||||||
if restriction
|
if restriction
|
||||||
restriction = [restriction] unless restriction.is_a?(Array)
|
restriction = [restriction] unless restriction.is_a?(Array)
|
||||||
restriction.map! {|r| @validator.send(:type_cast_value, r) }
|
restriction.map! {|r| @validator.class.send(:type_cast_value, r, @type) }
|
||||||
interpolate = @validator.send(:interpolation_values, option, restriction )
|
interpolate = @validator.send(:interpolation_values, option, restriction )
|
||||||
# get I18n message if defined and has interpolation keys in msg
|
# get I18n message if defined and has interpolation keys in msg
|
||||||
if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option)
|
if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option)
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ module ValidatesTimeliness
|
|||||||
private
|
private
|
||||||
|
|
||||||
def validates_timeliness_of(attr_names, configuration)
|
def validates_timeliness_of(attr_names, configuration)
|
||||||
validator = ValidatesTimeliness::Validator.new(configuration)
|
validator = ValidatesTimeliness::Validator.new(configuration.symbolize_keys)
|
||||||
|
|
||||||
# bypass handling of allow_nil and allow_blank to validate raw value
|
# bypass handling of allow_nil and allow_blank to validate raw value
|
||||||
configuration.delete(:allow_nil)
|
configuration.delete(:allow_nil)
|
||||||
configuration.delete(:allow_blank)
|
configuration.delete(:allow_blank)
|
||||||
validates_each(attr_names, configuration) do |record, attr_name, value|
|
validates_each(attr_names, configuration) do |record, attr_name, value|
|
||||||
validator.call(record, attr_name)
|
validator.call(record, attr_name, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ module ValidatesTimeliness
|
|||||||
}
|
}
|
||||||
|
|
||||||
RESTRICTION_METHODS = {
|
RESTRICTION_METHODS = {
|
||||||
|
:equal_to => :==,
|
||||||
:before => :<,
|
:before => :<,
|
||||||
:after => :>,
|
:after => :>,
|
||||||
:on_or_before => :<=,
|
:on_or_before => :<=,
|
||||||
@@ -19,18 +20,24 @@ module ValidatesTimeliness
|
|||||||
:between => lambda {|v, r| (r.first..r.last).include?(v) }
|
:between => lambda {|v, r| (r.first..r.last).include?(v) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VALID_OPTIONS = [
|
||||||
|
:on, :if, :unless, :allow_nil, :empty, :allow_blank, :blank,
|
||||||
|
:with_time, :with_date, :ignore_usec,
|
||||||
|
:invalid_time_message, :invalid_date_message, :invalid_datetime_message
|
||||||
|
] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten
|
||||||
|
|
||||||
attr_reader :configuration, :type
|
attr_reader :configuration, :type
|
||||||
|
|
||||||
def initialize(configuration)
|
def initialize(configuration)
|
||||||
defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false }
|
defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false, :ignore_usec => false }
|
||||||
@configuration = defaults.merge(configuration)
|
@configuration = defaults.merge(configuration)
|
||||||
@type = @configuration.delete(:type)
|
@type = @configuration.delete(:type)
|
||||||
|
validate_options(@configuration)
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(record, attr_name)
|
def call(record, attr_name, value)
|
||||||
value = record.send(attr_name)
|
|
||||||
value = record.class.parse_date_time(value, type, false) if value.is_a?(String)
|
value = record.class.parse_date_time(value, type, false) if value.is_a?(String)
|
||||||
raw_value = raw_value(record, attr_name)
|
raw_value = raw_value(record, attr_name) || value
|
||||||
|
|
||||||
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
||||||
|
|
||||||
@@ -44,18 +51,25 @@ module ValidatesTimeliness
|
|||||||
private
|
private
|
||||||
|
|
||||||
def raw_value(record, attr_name)
|
def raw_value(record, attr_name)
|
||||||
record.send("#{attr_name}_before_type_cast")
|
record.send("#{attr_name}_before_type_cast") rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_restrictions(record, attr_name, value)
|
def validate_restrictions(record, attr_name, value)
|
||||||
value = type_cast_value(value)
|
value = if @configuration[:with_time] || @configuration[:with_date]
|
||||||
|
restriction_type = :datetime
|
||||||
|
combine_date_and_time(value, record)
|
||||||
|
else
|
||||||
|
restriction_type = type
|
||||||
|
self.class.type_cast_value(value, type, @configuration[:ignore_usec])
|
||||||
|
end
|
||||||
|
return if value.nil?
|
||||||
|
|
||||||
RESTRICTION_METHODS.each do |option, method|
|
RESTRICTION_METHODS.each do |option, method|
|
||||||
next unless restriction = configuration[option]
|
next unless restriction = configuration[option]
|
||||||
begin
|
begin
|
||||||
restriction = restriction_value(restriction, record)
|
restriction = self.class.evaluate_option_value(restriction, restriction_type, record)
|
||||||
next if restriction.nil?
|
next if restriction.nil?
|
||||||
restriction = type_cast_value(restriction)
|
restriction = self.class.type_cast_value(restriction, restriction_type, @configuration[:ignore_usec])
|
||||||
|
|
||||||
unless evaluate_restriction(restriction, value, method)
|
unless evaluate_restriction(restriction, value, method)
|
||||||
add_error(record, attr_name, option, interpolation_values(option, restriction))
|
add_error(record, attr_name, option, interpolation_values(option, restriction))
|
||||||
@@ -87,10 +101,10 @@ module ValidatesTimeliness
|
|||||||
return true if restriction.nil?
|
return true if restriction.nil?
|
||||||
|
|
||||||
case comparator
|
case comparator
|
||||||
when Symbol
|
when Symbol
|
||||||
value.send(comparator, restriction)
|
value.send(comparator, restriction)
|
||||||
when Proc
|
when Proc
|
||||||
comparator.call(value, restriction)
|
comparator.call(value, restriction)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -107,13 +121,11 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def error_messages
|
def error_messages
|
||||||
return @error_messages if defined?(@error_messages)
|
@error_messages ||= ValidatesTimeliness.default_error_messages.merge(custom_error_messages)
|
||||||
@error_messages = ValidatesTimeliness.default_error_messages.merge(custom_error_messages)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def custom_error_messages
|
def custom_error_messages
|
||||||
return @custom_error_messages if defined?(@custom_error_messages)
|
@custom_error_messages ||= configuration.inject({}) {|msgs, (k, v)|
|
||||||
@custom_error_messages = configuration.inject({}) {|msgs, (k, v)|
|
|
||||||
if md = /(.*)_message$/.match(k.to_s)
|
if md = /(.*)_message$/.match(k.to_s)
|
||||||
msgs[md[1].to_sym] = v
|
msgs[md[1].to_sym] = v
|
||||||
end
|
end
|
||||||
@@ -121,42 +133,72 @@ module ValidatesTimeliness
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def restriction_value(restriction, record)
|
def combine_date_and_time(value, record)
|
||||||
case restriction
|
if type == :date
|
||||||
when Time, Date, DateTime
|
date = value
|
||||||
restriction
|
time = @configuration[:with_time]
|
||||||
when Symbol
|
else
|
||||||
restriction_value(record.send(restriction), record)
|
date = @configuration[:with_date]
|
||||||
when Proc
|
time = value
|
||||||
restriction_value(restriction.call(record), record)
|
|
||||||
when Array
|
|
||||||
restriction.map {|r| restriction_value(r, record) }.sort
|
|
||||||
when Range
|
|
||||||
restriction_value([restriction.first, restriction.last], record)
|
|
||||||
else
|
|
||||||
record.class.parse_date_time(restriction, type, false)
|
|
||||||
end
|
end
|
||||||
|
date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record)
|
||||||
|
return if date.nil? || time.nil?
|
||||||
|
record.class.send(:make_time, [date.year, date.month, date.day, time.hour, time.min, time.sec, time.usec])
|
||||||
end
|
end
|
||||||
|
|
||||||
def type_cast_value(value)
|
def validate_options(options)
|
||||||
if value.is_a?(Array)
|
invalid_for_type = ([:time, :date, :datetime] - [@type]).map {|k| "invalid_#{k}_message".to_sym }
|
||||||
value.map {|v| type_cast_value(v) }
|
invalid_for_type << :with_date unless @type == :time
|
||||||
else
|
invalid_for_type << :with_time unless @type == :date
|
||||||
case type
|
options.assert_valid_keys(VALID_OPTIONS - invalid_for_type)
|
||||||
when :time
|
end
|
||||||
value.to_dummy_time
|
|
||||||
when :date
|
# class methods
|
||||||
value.to_date
|
class << self
|
||||||
when :datetime
|
|
||||||
if value.is_a?(DateTime) || value.is_a?(Time)
|
def evaluate_option_value(value, type, record)
|
||||||
value.to_time
|
case value
|
||||||
else
|
when Time, Date, DateTime
|
||||||
value.to_time(ValidatesTimeliness.default_timezone)
|
value
|
||||||
end
|
when Symbol
|
||||||
|
evaluate_option_value(record.send(value), type, record)
|
||||||
|
when Proc
|
||||||
|
evaluate_option_value(value.call(record), type, record)
|
||||||
|
when Array
|
||||||
|
value.map {|r| evaluate_option_value(r, type, record) }.sort
|
||||||
|
when Range
|
||||||
|
evaluate_option_value([value.first, value.last], type, record)
|
||||||
else
|
else
|
||||||
nil
|
record.class.parse_date_time(value, type, false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def type_cast_value(value, type, ignore_usec=false)
|
||||||
|
if value.is_a?(Array)
|
||||||
|
value.map {|v| type_cast_value(v, type, ignore_usec) }
|
||||||
|
else
|
||||||
|
value = case type
|
||||||
|
when :time
|
||||||
|
value.to_dummy_time
|
||||||
|
when :date
|
||||||
|
value.to_date
|
||||||
|
when :datetime
|
||||||
|
if value.is_a?(DateTime) || value.is_a?(Time)
|
||||||
|
value.to_time
|
||||||
|
else
|
||||||
|
value.to_time(ValidatesTimeliness.default_timezone)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
if ignore_usec && value.is_a?(Time)
|
||||||
|
::ActiveRecord::Base.send(:make_time, Array(value).reverse[4..9])
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||||
|
|
||||||
describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
||||||
include ValidatesTimeliness::ActiveRecord::AttributeMethods
|
|
||||||
include ValidatesTimeliness::ValidationMethods
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@person = Person.new
|
@person = Person.new
|
||||||
end
|
end
|
||||||
@@ -23,6 +20,24 @@ 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
|
||||||
@person.class.should_receive(:parse_date_time).once
|
@person.class.should_receive(:parse_date_time).once
|
||||||
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
||||||
@@ -54,6 +69,11 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|||||||
@person.birth_date_and_time.should be_kind_of(Time)
|
@person.birth_date_and_time.should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should return Time object for datetime attribute read method when assigned Date object" do
|
||||||
|
@person.birth_date_and_time = Date.today
|
||||||
|
@person.birth_date_and_time.should be_kind_of(Time)
|
||||||
|
end
|
||||||
|
|
||||||
it "should return Time object for datetime attribute read method when assigned string" do
|
it "should return Time object for datetime attribute read method when assigned string" do
|
||||||
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
||||||
@person.birth_date_and_time.should be_kind_of(Time)
|
@person.birth_date_and_time.should be_kind_of(Time)
|
||||||
|
|||||||
@@ -157,11 +157,16 @@ describe ValidatesTimeliness::Formats do
|
|||||||
time_array = formats.parse('2000-02-01', :datetime, false)
|
time_array = formats.parse('2000-02-01', :datetime, false)
|
||||||
time_array.should == [2000,2,1,0,0,0,0]
|
time_array.should == [2000,2,1,0,0,0,0]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should ignore time when extracting date and strict is false" do
|
it "should ignore time when extracting date and strict is false" do
|
||||||
time_array = formats.parse('2000-02-01 12:12', :date, false)
|
time_array = formats.parse('2000-02-01 12:12', :date, false)
|
||||||
time_array.should == [2000,2,1,0,0,0,0]
|
time_array.should == [2000,2,1,0,0,0,0]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should ignore time when extracting date from format with trailing year and strict is false" do
|
||||||
|
time_array = formats.parse('01-02-2000 12:12', :date, false)
|
||||||
|
time_array.should == [2000,2,1,0,0,0,0]
|
||||||
|
end
|
||||||
|
|
||||||
it "should ignore date when extracting time and strict is false" do
|
it "should ignore date when extracting time and strict is false" do
|
||||||
time_array = formats.parse('2000-02-01 12:12', :time, false)
|
time_array = formats.parse('2000-02-01 12:12', :time, false)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
# ginger spec
|
# ginger spec
|
||||||
#
|
#
|
||||||
Ginger.configure do |config|
|
Ginger.configure do |config|
|
||||||
rails_versions = ['2.0.2', '2.1.2', '2.2.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
|
||||||
|
|||||||
@@ -16,55 +16,71 @@ describe ValidatesTimeliness::Validator do
|
|||||||
@person = Person.new
|
@person = Person.new
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "restriction_value" do
|
describe "option keys validation" do
|
||||||
|
before do
|
||||||
|
keys = ValidatesTimeliness::Validator::VALID_OPTIONS - [:invalid_date_message, :invalid_time_message, :with_date, :with_time]
|
||||||
|
@valid_options = keys.inject({}) {|hash, opt| hash[opt] = nil; hash }
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise error if invalid option key passed" do
|
||||||
|
@valid_options.update(:invalid_key => 'will not open lock')
|
||||||
|
lambda { Person.validates_datetime(@valid_options) }.should raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not raise error if option keys are valid" do
|
||||||
|
lambda { Person.validates_datetime(@valid_options) }.should_not raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "evaluate_option_value" do
|
||||||
it "should return Time object when restriction is Time object" do
|
it "should return Time object when restriction is Time object" do
|
||||||
restriction_value(Time.now, :datetime).should be_kind_of(Time)
|
evaluate_option_value(Time.now, :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return Time object when restriction is string" do
|
it "should return Time object when restriction is string" do
|
||||||
restriction_value("2007-01-01 12:00", :datetime).should be_kind_of(Time)
|
evaluate_option_value("2007-01-01 12:00", :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return Time object when restriction is method and method returns Time object" do
|
it "should return Time object when restriction is method and method returns Time object" do
|
||||||
person.stub!(:datetime_attr).and_return(Time.now)
|
person.stub!(:datetime_attr).and_return(Time.now)
|
||||||
restriction_value(:datetime_attr, :datetime).should be_kind_of(Time)
|
evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return Time object when restriction is method and method returns string" do
|
it "should return Time object when restriction is method and method returns string" do
|
||||||
person.stub!(:datetime_attr).and_return("2007-01-01 12:00")
|
person.stub!(:datetime_attr).and_return("2007-01-01 12:00")
|
||||||
restriction_value(:datetime_attr, :datetime).should be_kind_of(Time)
|
evaluate_option_value(:datetime_attr, :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return Time object when restriction is proc which returns Time object" do
|
it "should return Time object when restriction is proc which returns Time object" do
|
||||||
restriction_value(lambda { Time.now }, :datetime).should be_kind_of(Time)
|
evaluate_option_value(lambda { Time.now }, :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return Time object when restriction is proc which returns string" do
|
it "should return Time object when restriction is proc which returns string" do
|
||||||
restriction_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
|
evaluate_option_value(lambda {"2007-01-01 12:00"}, :datetime).should be_kind_of(Time)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return array of Time objects when restriction is array of Time objects" do
|
it "should return array of Time objects when restriction is array of Time objects" do
|
||||||
time1, time2 = Time.now, 1.day.ago
|
time1, time2 = Time.now, 1.day.ago
|
||||||
restriction_value([time1, time2], :datetime).should == [time2, time1]
|
evaluate_option_value([time1, time2], :datetime).should == [time2, time1]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return array of Time objects when restriction is array of strings" do
|
it "should return array of Time objects when restriction is array of strings" do
|
||||||
time1, time2 = "2000-01-02", "2000-01-01"
|
time1, time2 = "2000-01-02", "2000-01-01"
|
||||||
restriction_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
evaluate_option_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return array of Time objects when restriction is Range of Time objects" do
|
it "should return array of Time objects when restriction is Range of Time objects" do
|
||||||
time1, time2 = Time.now, 1.day.ago
|
time1, time2 = Time.now, 1.day.ago
|
||||||
restriction_value(time1..time2, :datetime).should == [time2, time1]
|
evaluate_option_value(time1..time2, :datetime).should == [time2, time1]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return array of Time objects when restriction is Range of time strings" do
|
it "should return array of Time objects when restriction is Range of time strings" do
|
||||||
time1, time2 = "2000-01-02", "2000-01-01"
|
time1, time2 = "2000-01-02", "2000-01-01"
|
||||||
restriction_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
evaluate_option_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
||||||
end
|
end
|
||||||
def restriction_value(restriction, type)
|
def evaluate_option_value(restriction, type)
|
||||||
configure_validator(:type => type)
|
configure_validator(:type => type)
|
||||||
validator.send(:restriction_value, restriction, person)
|
validator.class.send(:evaluate_option_value, restriction, type, person)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -330,6 +346,100 @@ describe ValidatesTimeliness::Validator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "instance with :equal_to restriction" do
|
||||||
|
|
||||||
|
describe "for datetime type" do
|
||||||
|
before do
|
||||||
|
configure_validator(:equal_to => Time.now)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have error when value not equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_date_and_time, Time.now + 1)
|
||||||
|
should_have_error(:birth_date_and_time, :equal_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have error when value is equal to :equal_to restriction for all values except microscond, and microsecond is not ignored" do
|
||||||
|
configure_validator(:equal_to => Time.utc(2000, 1, 1, 0, 0, 0, 0), :ignore_usec => false)
|
||||||
|
validate_with(:birth_date_and_time, Time.utc(2000, 1, 1, 0, 0, 0, 500))
|
||||||
|
should_have_error(:birth_date_and_time, :equal_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid when value is equal to :equal_to restriction for all values except microscond, and microsecond is ignored" do
|
||||||
|
configure_validator(:equal_to => 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)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid when value is equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_date_and_time, Time.now)
|
||||||
|
should_have_no_error(:birth_date_and_time, :equal_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "for date type" do
|
||||||
|
before do
|
||||||
|
configure_validator(:type => :date, :equal_to => Date.today)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have error when value is not equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_date, Date.today + 1)
|
||||||
|
should_have_error(:birth_date, :equal_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid when value is equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_date, Date.today)
|
||||||
|
should_have_no_error(:birth_date, :equal_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "for time type" do
|
||||||
|
before do
|
||||||
|
configure_validator(:type => :time, :equal_to => "09:00:00")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should have error when value is not equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_time, "09:00:01")
|
||||||
|
should_have_error(:birth_time, :equal_to)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid when value is equal to :equal_to restriction" do
|
||||||
|
validate_with(:birth_time, "09:00:00")
|
||||||
|
should_have_no_error(:birth_time, :equal_to)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "instance with :with_time option" do
|
||||||
|
|
||||||
|
it "should validate date attribute as datetime combining value of :with_time against restrictions " do
|
||||||
|
configure_validator(:type => :date, :with_time => '12:31', :on_or_before => Time.mktime(2000,1,1,12,30))
|
||||||
|
validate_with(:birth_date, "2000-01-01")
|
||||||
|
should_have_error(:birth_date, :on_or_before)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should skip restriction validation if :with_time value is nil" do
|
||||||
|
configure_validator(:type => :date, :with_time => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
|
||||||
|
validate_with(:birth_date, "2000-01-01")
|
||||||
|
should_have_no_error(:birth_date, :on_or_before)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "instance with :with_date option" do
|
||||||
|
|
||||||
|
it "should validate time attribute as datetime combining value of :with_date against restrictions " do
|
||||||
|
configure_validator(:type => :time, :with_date => '2009-01-01', :on_or_before => Time.mktime(2000,1,1,12,30))
|
||||||
|
validate_with(:birth_date, "12:30")
|
||||||
|
should_have_error(:birth_date, :on_or_before)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should skip restriction validation if :with_date value is nil" do
|
||||||
|
configure_validator(:type => :time, :with_date => nil, :on_or_before => Time.mktime(2000,1,1,12,30))
|
||||||
|
validate_with(:birth_date, "12:30")
|
||||||
|
should_have_no_error(:birth_date, :on_or_before)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "instance with mixed value and restriction types" do
|
describe "instance with mixed value and restriction types" do
|
||||||
|
|
||||||
it "should validate datetime attribute with Date restriction" do
|
it "should validate datetime attribute with Date restriction" do
|
||||||
@@ -483,7 +593,7 @@ describe ValidatesTimeliness::Validator do
|
|||||||
|
|
||||||
def validate_with(attr_name, value)
|
def validate_with(attr_name, value)
|
||||||
person.send("#{attr_name}=", value)
|
person.send("#{attr_name}=", value)
|
||||||
validator.call(person, attr_name)
|
validator.call(person, attr_name, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def should_have_error(attr_name, error)
|
def should_have_error(attr_name, error)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = %q{validates_timeliness}
|
s.name = %q{validates_timeliness}
|
||||||
s.version = "1.1.2"
|
s.version = "1.1.7"
|
||||||
|
|
||||||
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-01-12}
|
s.date = %q{2009-03-26}
|
||||||
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"]
|
||||||
|
|||||||
Reference in New Issue
Block a user