mirror of
https://github.com/ditkrg/validates_timeliness.git
synced 2026-01-25 15:22:58 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d5d82ff20 | ||
|
|
b11893eac0 | ||
|
|
76e159b350 | ||
|
|
c762b6d4f8 | ||
|
|
91f9f65bc0 | ||
|
|
6db8b7d908 | ||
|
|
d3c5101f92 | ||
|
|
899e96b880 | ||
|
|
162faf632a | ||
|
|
1b865cc834 | ||
|
|
c29478df45 | ||
|
|
df3283e5a1 | ||
|
|
0e382e15f2 | ||
|
|
9a697b9cab | ||
|
|
7bf7ed0569 | ||
|
|
a969a49ae8 | ||
|
|
687e61a3f2 | ||
|
|
4cc20ae620 | ||
|
|
2d510504e6 | ||
|
|
2028d68b17 | ||
|
|
e399c6b510 | ||
|
|
7aa1a87731 | ||
|
|
1a31e7463d | ||
|
|
14c55e3292 | ||
|
|
75a3b2bd83 | ||
|
|
9b300e084b | ||
|
|
d61dddfbc6 | ||
|
|
4ac4281c8b | ||
|
|
a56cf674b2 | ||
|
|
7f4d7b38d7 | ||
|
|
34c0f25225 | ||
|
|
57c3fdca88 | ||
|
|
805bb5d7fd | ||
|
|
7aac14c874 | ||
|
|
7c0c1afe1c | ||
|
|
5295a494e9 | ||
|
|
572d80d227 |
17
CHANGELOG
17
CHANGELOG
@@ -1,3 +1,20 @@
|
||||
= 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.
|
||||
|
||||
= 2.2.0 [2009-09-12]
|
||||
- Ruby 1.9 support!
|
||||
- Customise dummy date values for time types. See DUMMY DATE FOR TIME TYPES.
|
||||
- Fixed matcher conflict with Shoulda. Load plugin matcher manually now see matcher section in README
|
||||
- Fixed :ignore_usec when used with :with_time or :with_date
|
||||
- Some clean up and refactoring
|
||||
|
||||
= 2.1.0 [2009-06-20]
|
||||
- Added ambiguous year threshold setting in Formats class to customize the threshold for 2 digit years (See README)
|
||||
- Fixed interpolation values in custom error message for Rails 2.2+
|
||||
- Fixed custom I18n local override of en locale
|
||||
- Dramatically simplified ActiveRecord monkey patching and hackery
|
||||
|
||||
= 2.0.0 [2009-04-12]
|
||||
- Error value formats are now specified in the i18n locale file instead of updating plugin hash. See OTHER CUSTOMISATION section in README.
|
||||
- Date/time select helper extension is disabled by default. To enable see DISPLAY INVALID VALUES IN DATE HELPERS section in README to enable.
|
||||
|
||||
47
README.rdoc
47
README.rdoc
@@ -22,9 +22,9 @@ think should be a valid date or time string.
|
||||
* Restores ability to see raw value entered for date/time attributes with
|
||||
_before_type_cast modifier, which was lost in Rails 2.1.
|
||||
|
||||
* Respects new timezone features of Rails 2.1.
|
||||
* Supports Rails timezone handling
|
||||
|
||||
* Supports Rails 2.2 I18n for the error messages
|
||||
* Supports Rails I18n for the error messages
|
||||
|
||||
* Rspec matcher for testing model validation of dates and times
|
||||
|
||||
@@ -107,7 +107,7 @@ temporal options.
|
||||
|
||||
== EXAMPLES:
|
||||
|
||||
validates_date :date_of_birth :before => Proc.new { 18.years.ago },
|
||||
validates_date :date_of_birth :before => lambda { 18.years.ago },
|
||||
:before_message => "must be at least 18 years old"
|
||||
|
||||
validates_time :breakfast_time, :on_or_after => '6:00am',
|
||||
@@ -115,7 +115,7 @@ temporal options.
|
||||
:before => :second_breakfast_time,
|
||||
:allow_nil => true
|
||||
|
||||
validates_datetime :appointment_date, :before => Proc.new { 1.week.from_now }
|
||||
validates_datetime :appointment_date, :before => lambda { 1.week.from_now }
|
||||
|
||||
validates_date :entry_date, :with_time => '17:00', :on_or_before => :competition_closing
|
||||
|
||||
@@ -190,7 +190,7 @@ Here is what each format token means:
|
||||
|
||||
Special Cases:
|
||||
yy = 2 or 4 digit year
|
||||
yyyyy = exactly 4 digit year
|
||||
yyyy = exactly 4 digit year
|
||||
mmm = month long name (e.g. 'Jul' or 'July')
|
||||
ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
|
||||
u = microseconds matches 1 to 3 digits
|
||||
@@ -206,7 +206,7 @@ To see all defined formats look in lib/validates_timeliness/formats.rb.
|
||||
|
||||
The perenial problem for non-US developers or applications not primarily for the
|
||||
US, is the US date format of m/d/yy. This is ambiguous with the European format
|
||||
of d/my/yy. By default the plugin uses the US formats as this is the Ruby default
|
||||
of d/m/yy. By default the plugin uses the US formats as this is the Ruby default
|
||||
when it does date interpretation, and is in keeping PoLS (principle of least
|
||||
surprise).
|
||||
|
||||
@@ -250,6 +250,31 @@ 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.
|
||||
|
||||
|
||||
=== AMBIGUOUS YEAR THRESHOLD
|
||||
|
||||
When dealing with 2 digit year values, by default a year is interpreted as being
|
||||
in the last century at or above 30. You can customize this however
|
||||
|
||||
ValidatesTimeliness::Formats.ambiguous_year_threshold = 20
|
||||
|
||||
Now you get:
|
||||
|
||||
year of 19 is considered 2019
|
||||
year of 20 is considered 1920
|
||||
|
||||
|
||||
=== DUMMY DATE FOR TIME TYPES
|
||||
|
||||
Given that Ruby has no support for a time-only type, all time type columns are evaluated
|
||||
as a regular Time class objects with a dummy date value set. Rails defines the dummy date as
|
||||
2000-01-01. So a time of '12:30' is evaluated as a Time value of '2000-01-01 12:30'. If you
|
||||
need to customize this for some reason you can do so as follows
|
||||
|
||||
ValidatesTimeliness::Formats.dummy_date_for_time_type = [2009, 1, 1]
|
||||
|
||||
The value should be an array of 3 values being year, month and day in that order.
|
||||
|
||||
|
||||
=== TEMPORAL RESTRICTION ERRORS:
|
||||
|
||||
When using the validation temporal restrictions there are times when the restriction
|
||||
@@ -342,9 +367,15 @@ To sweeten the deal that little bit more, you have an Rspec matcher available fo
|
||||
you model specs. Now you can easily test the validations you have just written
|
||||
with the plugin or better yet *before* you write them! You just use the
|
||||
validation options you want as you would with the validation method. Those
|
||||
options are then verified and reported if they fail. Use it like so:
|
||||
options are then verified and reported if they fail.
|
||||
|
||||
@person.should validate_date(:birth_date, :before => Time.now, :before_message => 'should be before today')
|
||||
First require it in your spec_helper.rb
|
||||
|
||||
require 'validates_timeliness/matcher'
|
||||
|
||||
Use it like so:
|
||||
|
||||
@person.should validate_date(:birth_date, :before => Time.now, :before_message => 'should be before today')
|
||||
|
||||
|
||||
The matcher names are just the singular of the validation methods.
|
||||
|
||||
5
Rakefile
5
Rakefile
@@ -3,9 +3,10 @@ require 'rake/gempackagetask'
|
||||
require 'rubygems/specification'
|
||||
require 'date'
|
||||
require 'spec/rake/spectask'
|
||||
require 'lib/validates_timeliness/version'
|
||||
|
||||
GEM = "validates_timeliness"
|
||||
GEM_VERSION = "2.0.0"
|
||||
GEM_VERSION = ValidatesTimeliness::VERSION
|
||||
AUTHOR = "Adam Meehan"
|
||||
EMAIL = "adam.meehan@gmail.com"
|
||||
HOMEPAGE = "http://github.com/adzap/validates_timeliness"
|
||||
@@ -34,7 +35,7 @@ task :default => :spec
|
||||
desc "Run specs"
|
||||
Spec::Rake::SpecTask.new do |t|
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
t.spec_opts = %w(-fs --color)
|
||||
t.spec_opts = %w(--color)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -2,16 +2,11 @@ require 'validates_timeliness/formats'
|
||||
require 'validates_timeliness/parser'
|
||||
require 'validates_timeliness/validator'
|
||||
require 'validates_timeliness/validation_methods'
|
||||
require 'validates_timeliness/spec/rails/matchers/validate_timeliness' if ENV['RAILS_ENV'] == 'test'
|
||||
|
||||
require 'validates_timeliness/active_record/attribute_methods'
|
||||
require 'validates_timeliness/active_record/multiparameter_attributes'
|
||||
require 'validates_timeliness/action_view/instance_tag'
|
||||
|
||||
require 'validates_timeliness/core_ext/time'
|
||||
require 'validates_timeliness/core_ext/date'
|
||||
require 'validates_timeliness/core_ext/date_time'
|
||||
|
||||
module ValidatesTimeliness
|
||||
|
||||
mattr_accessor :default_timezone
|
||||
@@ -31,7 +26,7 @@ module ValidatesTimeliness
|
||||
|
||||
def load_error_messages
|
||||
if defined?(I18n)
|
||||
I18n.load_path += [ LOCALE_PATH ]
|
||||
I18n.load_path.unshift(LOCALE_PATH)
|
||||
I18n.reload!
|
||||
else
|
||||
defaults = YAML::load(IO.read(LOCALE_PATH))['en']
|
||||
@@ -45,6 +40,7 @@ module ValidatesTimeliness
|
||||
def setup_for_rails
|
||||
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
||||
self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
|
||||
self.enable_active_record_datetime_parser!
|
||||
load_error_messages
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,124 +1,65 @@
|
||||
module ValidatesTimeliness
|
||||
|
||||
def self.enable_active_record_datetime_parser!
|
||||
::ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
|
||||
# Rails 2.1 removed the ability to retrieve the raw value of a time or datetime
|
||||
# attribute. The raw value is necessary to properly validate a string time or
|
||||
# datetime value instead of the internal Rails type casting which is very limited
|
||||
# and does not allow custom formats. These methods restore that ability while
|
||||
# respecting the automatic timezone handling.
|
||||
# Overrides write method for date, time and datetime columns
|
||||
# to use plugin parser. Also adds mechanism to store value
|
||||
# before type cast.
|
||||
#
|
||||
# The automatic timezone handling sets the assigned attribute value to the current
|
||||
# zone in Time.zone. To preserve this localised value and capture the raw value
|
||||
# we cache the localised value on write and store the raw value in the attributes
|
||||
# hash for later retrieval and possibly validation. Any value from the database
|
||||
# will not be in the attribute cache on first read so will be considered in default
|
||||
# timezone and converted to local time. It is then stored back in the attributes
|
||||
# hash and cached to avoid the need for any subsequent differentiation.
|
||||
module AttributeMethods
|
||||
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
base.class_eval do
|
||||
alias_method_chain :read_attribute, :timeliness
|
||||
alias_method_chain :read_attribute_before_type_cast, :timeliness
|
||||
class << self
|
||||
alias_method_chain :define_attribute_methods, :timeliness
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Adds check for cached date/time attributes which have been type cast already
|
||||
# and value can be used from cache. This prevents the raw date/time value from
|
||||
# being type cast using default Rails type casting when writing values
|
||||
# to the database.
|
||||
def read_attribute_with_timeliness(attr_name)
|
||||
attr_name = attr_name.to_s
|
||||
if !(value = @attributes[attr_name]).nil?
|
||||
column = column_for_attribute(attr_name)
|
||||
if column && [:date, :time, :datetime].include?(column.type) && @attributes_cache.has_key?(attr_name)
|
||||
return @attributes_cache[attr_name]
|
||||
end
|
||||
end
|
||||
read_attribute_without_timeliness(attr_name)
|
||||
end
|
||||
|
||||
# If Rails dirty attributes is enabled then the value is added to
|
||||
# changed attributes if changed. Can't use the default dirty checking
|
||||
# implementation as it chains the write_attribute method which deletes
|
||||
# the attribute from the cache.
|
||||
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
||||
new = ValidatesTimeliness::Parser.parse(value, type)
|
||||
@attributes_cache["_#{attr_name}_before_type_cast"] = value
|
||||
|
||||
if new && type != :date
|
||||
new = new.to_time
|
||||
new = new.in_time_zone if time_zone_aware
|
||||
value = ValidatesTimeliness::Parser.parse(value, type)
|
||||
|
||||
if value && type != :date
|
||||
value = value.to_time
|
||||
value = value.in_time_zone if time_zone_aware
|
||||
end
|
||||
|
||||
if defined?(::ActiveRecord::Dirty) && !changed_attributes.include?(attr_name)
|
||||
old = read_attribute(attr_name)
|
||||
if old != new
|
||||
changed_attributes[attr_name] = (old.duplicable? ? old.clone : old)
|
||||
end
|
||||
end
|
||||
@attributes_cache[attr_name] = new
|
||||
@attributes[attr_name] = value
|
||||
write_attribute(attr_name.to_sym, value)
|
||||
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 = ValidatesTimeliness::Parser.parse(time, type)
|
||||
else
|
||||
time = read_attribute(attr_name)
|
||||
@attributes[attr_name] = (time && time_zone_aware ? time.in_time_zone : time) unless frozen?
|
||||
end
|
||||
@attributes_cache[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
||||
def read_attribute_before_type_cast_with_timeliness(attr_name)
|
||||
cached_attr = "_#{attr_name}_before_type_cast"
|
||||
return @attributes_cache[cached_attr] if @attributes_cache.has_key?(cached_attr)
|
||||
read_attribute_before_type_cast_without_timeliness(attr_name)
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
def define_attribute_methods_with_timeliness
|
||||
return if generated_methods?
|
||||
columns_hash.each do |name, column|
|
||||
unless instance_method_already_implemented?(name)
|
||||
if [:date, :time, :datetime].include?(column.type)
|
||||
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
||||
define_read_method_for_dates_and_times(name, column.type, time_zone_aware)
|
||||
end
|
||||
end
|
||||
timeliness_methods = []
|
||||
|
||||
unless instance_method_already_implemented?("#{name}=")
|
||||
if [:date, :time, :datetime].include?(column.type)
|
||||
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
|
||||
define_write_method_for_dates_and_times(name, column.type, time_zone_aware)
|
||||
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
|
||||
|
||||
define_method("#{name}=") do |value|
|
||||
write_date_time_attribute(name, value, column.type, time_zone_aware)
|
||||
end
|
||||
timeliness_methods << name
|
||||
end
|
||||
end
|
||||
|
||||
define_attribute_methods_without_timeliness
|
||||
end
|
||||
|
||||
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
||||
method_body = <<-EOV
|
||||
def #{attr_name}=(value)
|
||||
write_date_time_attribute('#{attr_name}', value, #{type.inspect}, #{time_zone_aware})
|
||||
end
|
||||
EOV
|
||||
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
||||
end
|
||||
|
||||
def define_read_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
||||
method_body = <<-EOV
|
||||
def #{attr_name}(reload = false)
|
||||
read_date_time_attribute('#{attr_name}', #{type.inspect}, #{time_zone_aware}, reload)
|
||||
end
|
||||
EOV
|
||||
evaluate_attribute_method attr_name, method_body
|
||||
@generated_methods += timeliness_methods
|
||||
end
|
||||
|
||||
end
|
||||
@@ -127,5 +68,3 @@ module ValidatesTimeliness
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.send(:include, ValidatesTimeliness::ActiveRecord::AttributeMethods)
|
||||
|
||||
@@ -5,14 +5,41 @@ module ValidatesTimeliness
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
|
||||
class << self
|
||||
|
||||
def time_array_to_string(values, type)
|
||||
values.collect! {|v| v.to_s }
|
||||
|
||||
case type
|
||||
when :date
|
||||
extract_date_from_multiparameter_attributes(values)
|
||||
when :time
|
||||
extract_time_from_multiparameter_attributes(values)
|
||||
when :datetime
|
||||
extract_date_from_multiparameter_attributes(values) + " " + extract_time_from_multiparameter_attributes(values)
|
||||
end
|
||||
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("-")
|
||||
end
|
||||
|
||||
def extract_time_from_multiparameter_attributes(values)
|
||||
values[3..5].map { |s| s.rjust(2, "0") }.join(":")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module MultiparameterAttributes
|
||||
|
||||
def self.included(base)
|
||||
base.alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness
|
||||
end
|
||||
|
||||
# Overrides AR method to store multiparameter time and dates as string
|
||||
# allowing validation later.
|
||||
# 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|
|
||||
@@ -23,7 +50,7 @@ module ValidatesTimeliness
|
||||
if values.empty?
|
||||
send("#{name}=", nil)
|
||||
else
|
||||
value = time_array_to_string(values, column.type)
|
||||
value = ValidatesTimeliness::ActiveRecord.time_array_to_string(values, column.type)
|
||||
send("#{name}=", value)
|
||||
end
|
||||
rescue => ex
|
||||
@@ -37,29 +64,6 @@ module ValidatesTimeliness
|
||||
execute_callstack_for_multiparameter_attributes_without_timeliness(callstack)
|
||||
end
|
||||
|
||||
def time_array_to_string(values, type)
|
||||
values.collect! {|v| v.to_s }
|
||||
|
||||
case type
|
||||
when :date
|
||||
extract_date_from_multiparameter_attributes(values)
|
||||
when :time
|
||||
extract_time_from_multiparameter_attributes(values)
|
||||
when :datetime
|
||||
date_values, time_values = values.slice!(0, 3), values
|
||||
extract_date_from_multiparameter_attributes(date_values) + " " + extract_time_from_multiparameter_attributes(time_values)
|
||||
end
|
||||
end
|
||||
|
||||
def extract_date_from_multiparameter_attributes(values)
|
||||
[values[0], *values.slice(1, 2).map { |s| s.rjust(2, "0") }].join("-")
|
||||
end
|
||||
|
||||
def extract_time_from_multiparameter_attributes(values)
|
||||
values = values.size > 3 ? values[3..5] : values
|
||||
values.map { |s| s.rjust(2, "0") }.join(":")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
module ValidatesTimeliness
|
||||
module CoreExtensions
|
||||
module Date
|
||||
|
||||
def to_dummy_time
|
||||
::Time.send(ValidatesTimeliness.default_timezone, 2000, 1, 1, 0, 0, 0)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Date.send(:include, ValidatesTimeliness::CoreExtensions::Date)
|
||||
@@ -1,13 +0,0 @@
|
||||
module ValidatesTimeliness
|
||||
module CoreExtensions
|
||||
module DateTime
|
||||
|
||||
def to_dummy_time
|
||||
::Time.send(ValidatesTimeliness.default_timezone, 2000, 1, 1, hour, min, sec)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
DateTime.send(:include, ValidatesTimeliness::CoreExtensions::DateTime)
|
||||
@@ -1,13 +0,0 @@
|
||||
module ValidatesTimeliness
|
||||
module CoreExtensions
|
||||
module Time
|
||||
|
||||
def to_dummy_time
|
||||
self.class.send(ValidatesTimeliness.default_timezone, 2000, 1, 1, hour, min, sec)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Time.send(:include, ValidatesTimeliness::CoreExtensions::Time)
|
||||
@@ -21,6 +21,26 @@ module ValidatesTimeliness
|
||||
:format_tokens,
|
||||
:format_proc_args
|
||||
|
||||
|
||||
# Set the threshold value for a two digit year to be considered last century
|
||||
#
|
||||
# Default: 30
|
||||
#
|
||||
# Example:
|
||||
# year = '29' is considered 2029
|
||||
# year = '30' is considered 1930
|
||||
#
|
||||
cattr_accessor :ambiguous_year_threshold
|
||||
self.ambiguous_year_threshold = 30
|
||||
|
||||
# Set the dummy date part for a time type value. Should be an array of 3 values
|
||||
# being year, month and day in that order.
|
||||
#
|
||||
# Default: [ 2000, 1, 1 ] same as ActiveRecord
|
||||
#
|
||||
cattr_accessor :dummy_date_for_time_type
|
||||
self.dummy_date_for_time_type = [ 2000, 1, 1 ]
|
||||
|
||||
# Format tokens:
|
||||
# y = year
|
||||
# m = month
|
||||
@@ -46,7 +66,7 @@ module ValidatesTimeliness
|
||||
#
|
||||
# Special Cases:
|
||||
# yy = 2 or 4 digit year
|
||||
# yyyyy = exactly 4 digit year
|
||||
# yyyy = exactly 4 digit year
|
||||
# mmm = month long name (e.g. 'Jul' or 'July')
|
||||
# ddd = Day name of 3 to 9 letters (e.g. Wed or Wednesday)
|
||||
# u = microseconds matches 1 to 6 digits
|
||||
@@ -85,6 +105,7 @@ module ValidatesTimeliness
|
||||
@@datetime_formats = [
|
||||
'yyyy-mm-dd hh:nn:ss',
|
||||
'yyyy-mm-dd h:nn',
|
||||
'yyyy-mm-dd h:nn_ampm',
|
||||
'yyyy-mm-dd hh:nn:ss.u',
|
||||
'm/d/yy h:nn:ss',
|
||||
'm/d/yy h:nn_ampm',
|
||||
@@ -167,6 +188,7 @@ module ValidatesTimeliness
|
||||
options.reverse_merge!(:strict => true)
|
||||
|
||||
sets = if options[:format]
|
||||
options[:strict] = true
|
||||
[ send("#{type}_expressions").assoc(options[:format]) ]
|
||||
else
|
||||
expression_set(type, string)
|
||||
@@ -183,7 +205,11 @@ module ValidatesTimeliness
|
||||
break(proc) if matches = full.match(string.strip)
|
||||
end
|
||||
last = options[:include_offset] ? 8 : 7
|
||||
processor.call(*matches[1..last]) if matches
|
||||
if matches
|
||||
values = processor.call(*matches[1..last])
|
||||
values[0..2] = dummy_date_for_time_type if type == :time
|
||||
return values
|
||||
end
|
||||
end
|
||||
|
||||
# Delete formats of specified type. Error raised if format not found.
|
||||
@@ -226,6 +252,49 @@ module ValidatesTimeliness
|
||||
compile_format_expressions
|
||||
end
|
||||
|
||||
def full_hour(hour, meridian)
|
||||
hour = hour.to_i
|
||||
return hour if meridian.nil?
|
||||
if meridian.delete('.').downcase == 'am'
|
||||
hour == 12 ? 0 : hour
|
||||
else
|
||||
hour == 12 ? hour : hour + 12
|
||||
end
|
||||
end
|
||||
|
||||
def unambiguous_year(year)
|
||||
if year.length <= 2
|
||||
century = Time.now.year.to_s[0..1].to_i
|
||||
century -= 1 if year.to_i >= ambiguous_year_threshold
|
||||
year = "#{century}#{year.rjust(2,'0')}"
|
||||
end
|
||||
year.to_i
|
||||
end
|
||||
|
||||
def month_index(month)
|
||||
return month.to_i if month.to_i.nonzero?
|
||||
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
|
||||
end
|
||||
|
||||
def month_names
|
||||
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
|
||||
end
|
||||
|
||||
def abbr_month_names
|
||||
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
|
||||
end
|
||||
|
||||
def microseconds(usec)
|
||||
(".#{usec}".to_f * 1_000_000).to_i
|
||||
end
|
||||
|
||||
def offset_in_seconds(offset)
|
||||
sign = offset =~ /^-/ ? -1 : 1
|
||||
parts = offset.scan(/\d\d/).map {|p| p.to_f }
|
||||
parts[1] = parts[1].to_f / 60
|
||||
(parts[0] + parts[1]) * sign * 3600
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Compile formats into validation regexps and format procs
|
||||
@@ -259,7 +328,12 @@ module ValidatesTimeliness
|
||||
args = order.invert.sort.map {|p| arg_map[p[1]][1] }
|
||||
arr = [nil] * 7
|
||||
order.keys.each {|k| i = arg_map[k][0]; arr[i] = arg_map[k][2] unless i.nil? }
|
||||
proc_string = "lambda {|#{args.join(',')}| md||=nil; [#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i } }"
|
||||
proc_string = <<-EOL
|
||||
lambda {|#{args.join(',')}|
|
||||
md ||= nil
|
||||
[#{arr.map {|i| i.nil? ? 'nil' : i }.join(',')}].map {|i| i.is_a?(Float) ? i : i.to_i }
|
||||
}
|
||||
EOL
|
||||
eval proc_string
|
||||
end
|
||||
|
||||
@@ -285,44 +359,6 @@ module ValidatesTimeliness
|
||||
end
|
||||
end
|
||||
|
||||
def full_hour(hour, meridian)
|
||||
hour = hour.to_i
|
||||
return hour if meridian.nil?
|
||||
if meridian.delete('.').downcase == 'am'
|
||||
hour == 12 ? 0 : hour
|
||||
else
|
||||
hour == 12 ? hour : hour + 12
|
||||
end
|
||||
end
|
||||
|
||||
def unambiguous_year(year, threshold=30)
|
||||
year = "#{year.to_i < threshold ? '20' : '19'}#{year}" if year.length == 2
|
||||
year.to_i
|
||||
end
|
||||
|
||||
def month_index(month)
|
||||
return month.to_i if month.to_i.nonzero?
|
||||
abbr_month_names.index(month.capitalize) || month_names.index(month.capitalize)
|
||||
end
|
||||
|
||||
def month_names
|
||||
defined?(I18n) ? I18n.t('date.month_names') : Date::MONTHNAMES
|
||||
end
|
||||
|
||||
def abbr_month_names
|
||||
defined?(I18n) ? I18n.t('date.abbr_month_names') : Date::ABBR_MONTHNAMES
|
||||
end
|
||||
|
||||
def microseconds(usec)
|
||||
(".#{usec}".to_f * 1_000_000).to_i
|
||||
end
|
||||
|
||||
def offset_in_seconds(offset)
|
||||
sign = offset =~ /^-/ ? -1 : 1
|
||||
parts = offset.scan(/\d\d/).map {|p| p.to_f }
|
||||
parts[1] = parts[1].to_f / 60
|
||||
(parts[0] + parts[1]) * sign * 3600
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
1
lib/validates_timeliness/matcher.rb
Normal file
1
lib/validates_timeliness/matcher.rb
Normal file
@@ -0,0 +1 @@
|
||||
require 'validates_timeliness/spec/rails/matchers/validate_timeliness'
|
||||
@@ -7,28 +7,24 @@ module ValidatesTimeliness
|
||||
return nil if raw_value.blank?
|
||||
return raw_value if raw_value.acts_like?(:time) || raw_value.is_a?(Date)
|
||||
|
||||
options.reverse_merge!(:strict => true)
|
||||
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, options.reverse_merge(:strict => true))
|
||||
return nil if time_array.nil?
|
||||
|
||||
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, options)
|
||||
raise if time_array.nil?
|
||||
|
||||
# Rails dummy time date part is defined as 2000-01-01
|
||||
time_array[0..2] = 2000, 1, 1 if type == :time
|
||||
|
||||
# Date.new enforces days per month, unlike Time
|
||||
date = Date.new(*time_array[0..2]) unless type == :time
|
||||
|
||||
return date if type == :date
|
||||
|
||||
make_time(time_array[0..7])
|
||||
rescue
|
||||
nil
|
||||
if type == :date
|
||||
Date.new(*time_array[0..2]) rescue nil
|
||||
else
|
||||
make_time(time_array[0..7])
|
||||
end
|
||||
end
|
||||
|
||||
def make_time(time_array)
|
||||
# Enforce date part validity which Time class does not
|
||||
return nil unless Date.valid_civil?(*time_array[0..2])
|
||||
|
||||
if Time.respond_to?(:zone) && ValidatesTimeliness.use_time_zones
|
||||
Time.zone.local(*time_array)
|
||||
else
|
||||
# Older AR way of handling times with datetime fallback
|
||||
begin
|
||||
time_zone = ValidatesTimeliness.default_timezone
|
||||
Time.send(time_zone, *time_array)
|
||||
@@ -38,6 +34,8 @@ module ValidatesTimeliness
|
||||
DateTime.civil(*(time_array << zone_offset))
|
||||
end
|
||||
end
|
||||
rescue ArgumentError, TypeError
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -28,13 +28,12 @@ module Spec
|
||||
|
||||
valid = test_validity
|
||||
|
||||
valid = test_option(:equal_to) if @options[:equal_to] && valid
|
||||
valid = test_option(:before) if @options[:before] && valid
|
||||
valid = test_option(:after) if @options[:after] && valid
|
||||
valid = test_option(:on_or_before) if @options[:on_or_before] && valid
|
||||
valid = test_option(:on_or_after) if @options[:on_or_after] && valid
|
||||
|
||||
valid = test_between if @options[:between] && valid
|
||||
valid = test_option(:equal_to) if valid && @options[:equal_to]
|
||||
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]
|
||||
valid = test_option(:on_or_after) if valid && @options[:on_or_after]
|
||||
valid = test_between if valid && @options[:between]
|
||||
|
||||
return valid
|
||||
end
|
||||
@@ -124,9 +123,14 @@ module Spec
|
||||
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 )
|
||||
|
||||
# get I18n message if defined and has interpolation keys in msg
|
||||
if defined?(I18n) && !@validator.send(:custom_error_messages).include?(option)
|
||||
msg = @record.errors.generate_message(@expected, option, interpolate)
|
||||
msg = if defined?(ActiveRecord::Error)
|
||||
ActiveRecord::Error.new(@record, @expected, option, interpolate).message
|
||||
else
|
||||
@record.errors.generate_message(@expected, option, interpolate)
|
||||
end
|
||||
else
|
||||
msg = msg % interpolate
|
||||
end
|
||||
|
||||
@@ -32,21 +32,13 @@ module ValidatesTimeliness
|
||||
raw_value = raw_value(record, attr_name) || value
|
||||
|
||||
if value.is_a?(String) || configuration[:format]
|
||||
strict = !configuration[:format].nil?
|
||||
value = ValidatesTimeliness::Parser.parse(raw_value, type, :strict => strict, :format => configuration[:format])
|
||||
value = ValidatesTimeliness::Parser.parse(raw_value, type, :strict => false, :format => configuration[:format])
|
||||
end
|
||||
|
||||
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
||||
|
||||
if raw_value.blank?
|
||||
add_error(record, attr_name, :blank)
|
||||
return
|
||||
end
|
||||
|
||||
if value.nil?
|
||||
add_error(record, attr_name, "invalid_#{type}".to_sym)
|
||||
return
|
||||
end
|
||||
return add_error(record, attr_name, :blank) if raw_value.blank?
|
||||
return add_error(record, attr_name, "invalid_#{type}".to_sym) if value.nil?
|
||||
|
||||
validate_restrictions(record, attr_name, value)
|
||||
end
|
||||
@@ -62,21 +54,20 @@ module ValidatesTimeliness
|
||||
end
|
||||
|
||||
def validate_restrictions(record, attr_name, 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])
|
||||
if configuration[:with_time] || configuration[:with_date]
|
||||
value = combine_date_and_time(value, record)
|
||||
end
|
||||
|
||||
value = self.class.type_cast_value(value, implied_type, configuration[:ignore_usec])
|
||||
|
||||
return if value.nil?
|
||||
|
||||
RESTRICTION_METHODS.each do |option, method|
|
||||
next unless restriction = configuration[option]
|
||||
begin
|
||||
restriction = self.class.evaluate_option_value(restriction, restriction_type, record)
|
||||
restriction = self.class.evaluate_option_value(restriction, implied_type, record)
|
||||
next if restriction.nil?
|
||||
restriction = self.class.type_cast_value(restriction, restriction_type, configuration[:ignore_usec])
|
||||
restriction = self.class.type_cast_value(restriction, implied_type, configuration[:ignore_usec])
|
||||
|
||||
unless evaluate_restriction(restriction, value, method)
|
||||
add_error(record, attr_name, option, interpolation_values(option, restriction))
|
||||
@@ -94,7 +85,7 @@ module ValidatesTimeliness
|
||||
restriction = [restriction] unless restriction.is_a?(Array)
|
||||
|
||||
if defined?(I18n)
|
||||
message = custom_error_messages[option] || I18n.t('activerecord.errors.messages')[option]
|
||||
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) }
|
||||
@@ -118,7 +109,7 @@ module ValidatesTimeliness
|
||||
def add_error(record, attr_name, message, interpolate=nil)
|
||||
if defined?(I18n)
|
||||
custom = custom_error_messages[message]
|
||||
record.errors.add(attr_name, custom || message, interpolate || {})
|
||||
record.errors.add(attr_name, message, { :default => custom }.merge(interpolate || {}))
|
||||
else
|
||||
message = error_messages[message] if message.is_a?(Symbol)
|
||||
message = message % interpolate
|
||||
@@ -155,6 +146,10 @@ module ValidatesTimeliness
|
||||
options.assert_valid_keys(VALID_OPTIONS - invalid_for_type)
|
||||
end
|
||||
|
||||
def implied_type
|
||||
@implied_type ||= configuration[:with_date] || configuration[:with_time] ? :datetime : type
|
||||
end
|
||||
|
||||
# class methods
|
||||
class << self
|
||||
|
||||
@@ -185,7 +180,8 @@ module ValidatesTimeliness
|
||||
when Symbol
|
||||
evaluate_option_value(record.send(value), type, record)
|
||||
when Proc
|
||||
evaluate_option_value(value.call(record), type, record)
|
||||
result = value.arity > 0 ? value.call(record) : value.call
|
||||
evaluate_option_value(result, type, record)
|
||||
when Array
|
||||
value.map {|r| evaluate_option_value(r, type, record) }.sort
|
||||
when Range
|
||||
@@ -201,11 +197,11 @@ module ValidatesTimeliness
|
||||
else
|
||||
value = case type
|
||||
when :time
|
||||
value.to_dummy_time
|
||||
dummy_time(value)
|
||||
when :date
|
||||
value.to_date
|
||||
when :datetime
|
||||
if value.is_a?(DateTime) || value.is_a?(Time)
|
||||
if value.is_a?(Time) || value.is_a?(DateTime)
|
||||
value.to_time
|
||||
else
|
||||
value.to_time(ValidatesTimeliness.default_timezone)
|
||||
@@ -221,6 +217,16 @@ module ValidatesTimeliness
|
||||
end
|
||||
end
|
||||
|
||||
def dummy_time(value)
|
||||
if value.is_a?(Time) || value.is_a?(DateTime)
|
||||
time = [value.hour, value.min, value.sec]
|
||||
else
|
||||
time = [0,0,0]
|
||||
end
|
||||
dummy_date = ValidatesTimeliness::Formats.dummy_date_for_time_type
|
||||
Time.send(ValidatesTimeliness.default_timezone, *(dummy_date + time))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
3
lib/validates_timeliness/version.rb
Normal file
3
lib/validates_timeliness/version.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
module ValidatesTimeliness
|
||||
VERSION = "2.2.1"
|
||||
end
|
||||
@@ -1,6 +1,10 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe ValidatesTimeliness::ActionView::InstanceTag, :type => :helper do
|
||||
ValidatesTimeliness.enable_datetime_select_extension!
|
||||
|
||||
describe 'ValidatesTimeliness::ActionView::InstanceTag' do
|
||||
include ActionView::Helpers::DateHelper
|
||||
include ActionController::Assertions::SelectorAssertions
|
||||
|
||||
before do
|
||||
@person = Person.new
|
||||
|
||||
@@ -20,24 +20,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
||||
@person.birth_date_and_time = "2000-01-01 12:00"
|
||||
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
|
||||
ValidatesTimeliness::Parser.should_receive(:parse).once
|
||||
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
||||
@@ -103,7 +85,20 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
||||
@person.birth_date_and_time_before_type_cast.should be_nil
|
||||
end
|
||||
|
||||
unless RAILS_VER < '2.1'
|
||||
if RAILS_VER < '2.1'
|
||||
|
||||
it "should return time object from database in default timezone" do
|
||||
ActiveRecord::Base.default_timezone = :utc
|
||||
time_string = "2000-01-01 09:00:00"
|
||||
@person = Person.new
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save
|
||||
@person.reload
|
||||
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
it "should return stored time string as Time with correct timezone" do
|
||||
Time.zone = 'Melbourne'
|
||||
time_string = "2000-06-01 02:03:04"
|
||||
@@ -121,84 +116,6 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
||||
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z %z').should == time_string + ' EST +1000'
|
||||
end
|
||||
|
||||
describe "dirty attributes" do
|
||||
|
||||
it "should return true for attribute changed? when value updated" do
|
||||
time_string = "2000-01-01 02:03:04"
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.birth_date_and_time_changed?.should be_true
|
||||
end
|
||||
|
||||
it "should show changes when time attribute changed from nil to Time object" do
|
||||
time_string = "2000-01-01 02:03:04"
|
||||
@person.birth_date_and_time = time_string
|
||||
time = @person.birth_date_and_time
|
||||
@person.changes.should == {"birth_date_and_time" => [nil, time]}
|
||||
end
|
||||
|
||||
it "should show changes when time attribute changed from Time object to nil" do
|
||||
time_string = "2020-01-01 02:03:04"
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save false
|
||||
@person.reload
|
||||
time = @person.birth_date_and_time
|
||||
@person.birth_date_and_time = nil
|
||||
@person.changes.should == {"birth_date_and_time" => [time, nil]}
|
||||
end
|
||||
|
||||
it "should show no changes when assigned same value as Time object" do
|
||||
time_string = "2020-01-01 02:03:04"
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save false
|
||||
@person.reload
|
||||
time = @person.birth_date_and_time
|
||||
@person.birth_date_and_time = time
|
||||
@person.changes.should == {}
|
||||
end
|
||||
|
||||
it "should show no changes when assigned same value as time string" do
|
||||
time_string = "2020-01-01 02:03:04"
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save false
|
||||
@person.reload
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.changes.should == {}
|
||||
end
|
||||
|
||||
end
|
||||
else
|
||||
|
||||
it "should return time object from database in default timezone" do
|
||||
ActiveRecord::Base.default_timezone = :utc
|
||||
time_string = "2000-01-01 09:00:00"
|
||||
@person = Person.new
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save
|
||||
@person.reload
|
||||
@person.birth_date_and_time.strftime('%Y-%m-%d %H:%M:%S %Z').should == time_string + ' GMT'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
it "should return same time object on repeat reads on existing object" do
|
||||
Time.zone = 'Melbourne' unless RAILS_VER < '2.1'
|
||||
time_string = "2000-01-01 09:00:00"
|
||||
@person = Person.new
|
||||
@person.birth_date_and_time = time_string
|
||||
@person.save!
|
||||
@person.reload
|
||||
time = @person.birth_date_and_time
|
||||
@person.birth_date_and_time.should == time
|
||||
end
|
||||
|
||||
it "should return same date object on repeat reads on existing object" do
|
||||
date_string = Date.today
|
||||
@person = Person.new
|
||||
@person.birth_date = date_string
|
||||
@person.save!
|
||||
@person.reload
|
||||
date = @person.birth_date
|
||||
@person.birth_date.should == date
|
||||
end
|
||||
|
||||
it "should return correct date value after new value assigned" do
|
||||
@@ -221,14 +138,4 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
||||
@person.birth_date.should == tomorrow
|
||||
end
|
||||
|
||||
it "should skip storing value in attributes hash on read if record frozen" do
|
||||
@person = Person.new
|
||||
@person.birth_date = Date.today
|
||||
@person.save!
|
||||
@person.reload
|
||||
@person.freeze
|
||||
@person.frozen?.should be_true
|
||||
lambda { @person.birth_date }.should_not raise_error
|
||||
@person.birth_date.should == Date.today
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,17 +6,17 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
|
||||
end
|
||||
|
||||
it "should convert array for datetime type into datetime string" do
|
||||
time_string = obj.time_array_to_string([2000,2,1,9,10,11], :datetime)
|
||||
time_string = time_array_to_string([2000,2,1,9,10,11], :datetime)
|
||||
time_string.should == "2000-02-01 09:10:11"
|
||||
end
|
||||
|
||||
it "should convert array for date type into date string" do
|
||||
time_string = obj.time_array_to_string([2000,2,1], :date)
|
||||
time_string = time_array_to_string([2000,2,1], :date)
|
||||
time_string.should == "2000-02-01"
|
||||
end
|
||||
|
||||
it "should convert array for time type into time string" do
|
||||
time_string = obj.time_array_to_string([2000,1,1,9,10,11], :time)
|
||||
time_string = time_array_to_string([2000,1,1,9,10,11], :time)
|
||||
time_string.should == "09:10:11"
|
||||
end
|
||||
|
||||
@@ -45,4 +45,8 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
|
||||
end
|
||||
end
|
||||
|
||||
def time_array_to_string(*args)
|
||||
ValidatesTimeliness::ActiveRecord.time_array_to_string(*args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe ValidatesTimeliness::CoreExtensions::Date do
|
||||
before do
|
||||
@a_date = Date.new(2008, 7, 1)
|
||||
end
|
||||
|
||||
it "should make a date value into a dummy time value" do
|
||||
@a_date.to_dummy_time.should == Time.utc(2000,1,1,0,0,0)
|
||||
end
|
||||
end
|
||||
|
||||
describe ValidatesTimeliness::CoreExtensions::Time do
|
||||
before do
|
||||
@a_time = Time.mktime(2008, 7, 1, 2, 3, 4)
|
||||
end
|
||||
|
||||
it "should make a time value into a dummy time value" do
|
||||
@a_time.to_dummy_time.should == Time.utc(2000,1,1,2,3,4)
|
||||
end
|
||||
end
|
||||
|
||||
describe ValidatesTimeliness::CoreExtensions::DateTime do
|
||||
before do
|
||||
@a_datetime = DateTime.new(2008, 7, 1, 2, 3, 4)
|
||||
end
|
||||
|
||||
it "should make a datetime value into a dummy time value" do
|
||||
@a_datetime.to_dummy_time.should == Time.utc(2000,1,1,2,3,4)
|
||||
end
|
||||
end
|
||||
@@ -1,11 +1,6 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
||||
|
||||
describe ValidatesTimeliness::Formats do
|
||||
attr_reader :formats
|
||||
|
||||
before do
|
||||
@formats = ValidatesTimeliness::Formats
|
||||
end
|
||||
|
||||
describe "format proc generator" do
|
||||
it "should generate proc which outputs date array with values in correct order" do
|
||||
@@ -104,7 +99,7 @@ describe ValidatesTimeliness::Formats do
|
||||
|
||||
it "should return time array from date string" do
|
||||
time_array = formats.parse('12:13:14', :time, :strict => true)
|
||||
time_array.should == [0,0,0,12,13,14,0]
|
||||
time_array.should == [2000,1,1,12,13,14,0]
|
||||
end
|
||||
|
||||
it "should return date array from time string" do
|
||||
@@ -134,7 +129,7 @@ describe ValidatesTimeliness::Formats do
|
||||
|
||||
it "should ignore date when extracting time and strict is false" do
|
||||
time_array = formats.parse('2000-02-01 12:13', :time, :strict => false)
|
||||
time_array.should == [0,0,0,12,13,0,0]
|
||||
time_array.should == [2000,1,1,12,13,0,0]
|
||||
end
|
||||
|
||||
it "should return zone offset when :include_offset options is true" do
|
||||
@@ -155,6 +150,44 @@ describe ValidatesTimeliness::Formats do
|
||||
end
|
||||
end
|
||||
|
||||
describe "parsing date with ambiguous year" do
|
||||
it "should return year in current century if year below threshold" do
|
||||
time_array = formats.parse('01-02-29', :date)
|
||||
time_array.should == [2029,2,1,0,0,0,0]
|
||||
end
|
||||
|
||||
it "should return year in last century if year at or above threshold" do
|
||||
time_array = formats.parse('01-02-30', :date)
|
||||
time_array.should == [1930,2,1,0,0,0,0]
|
||||
end
|
||||
|
||||
it "should allow custom threshold" do
|
||||
default = ValidatesTimeliness::Formats.ambiguous_year_threshold
|
||||
ValidatesTimeliness::Formats.ambiguous_year_threshold = 40
|
||||
time_array = formats.parse('01-02-39', :date)
|
||||
time_array.should == [2039,2,1,0,0,0,0]
|
||||
time_array = formats.parse('01-02-40', :date)
|
||||
time_array.should == [1940,2,1,0,0,0,0]
|
||||
ValidatesTimeliness::Formats.ambiguous_year_threshold = default
|
||||
end
|
||||
end
|
||||
|
||||
describe "parse with custom dummy date values" do
|
||||
before(:all) do
|
||||
@old_dummy_date = formats.dummy_date_for_time_type
|
||||
formats.dummy_date_for_time_type = [2009,1,1]
|
||||
end
|
||||
|
||||
it "should return time array with custom dummy date" do
|
||||
time_array = formats.parse('12:13:14', :time, :strict => true)
|
||||
time_array.should == [2009,1,1,12,13,14,0]
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
formats.dummy_date_for_time_type = @old_dummy_date
|
||||
end
|
||||
end
|
||||
|
||||
describe "removing formats" do
|
||||
it "should remove format from format array" do
|
||||
formats.remove_formats(:time, 'h.nn_ampm')
|
||||
@@ -197,7 +230,7 @@ describe ValidatesTimeliness::Formats do
|
||||
formats.add_formats(:time, "ss:hh:nn", :before => 'hh:nn:ss')
|
||||
validate("59:23:58", :time).should be_true
|
||||
time_array = formats.parse('59:23:58', :time)
|
||||
time_array.should == [0,0,0,23,58,59,0]
|
||||
time_array.should == [2000,1,1,23,58,59,0]
|
||||
end
|
||||
|
||||
it "should raise error if format exists" do
|
||||
@@ -229,6 +262,11 @@ describe ValidatesTimeliness::Formats do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def formats
|
||||
ValidatesTimeliness::Formats
|
||||
end
|
||||
|
||||
def validate(time_string, type)
|
||||
valid = false
|
||||
formats.send("#{type}_expressions").each do |format, regexp, processor|
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
# ginger spec
|
||||
#
|
||||
Ginger.configure do |config|
|
||||
rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.2']
|
||||
rails_versions = ['2.0.2', '2.1.2', '2.2.2', '2.3.3', '2.3.4']
|
||||
|
||||
rails_versions.each do |v|
|
||||
g = Ginger::Scenario.new
|
||||
g = Ginger::Scenario.new("Rails #{v}")
|
||||
g['rails'] = v
|
||||
config.scenarios << g.dup
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
|
||||
require 'validates_timeliness/matcher'
|
||||
|
||||
class NoValidation < Person
|
||||
end
|
||||
|
||||
@@ -2,10 +2,10 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
||||
$:.unshift(File.dirname(__FILE__))
|
||||
$:.unshift(File.dirname(__FILE__) + '/resources')
|
||||
|
||||
ENV['RAILS_ENV'] = 'test'
|
||||
RAILS_ENV = ENV['RAILS_ENV'] = 'test'
|
||||
|
||||
require 'rubygems'
|
||||
require 'spec'
|
||||
require 'spec/autorun'
|
||||
|
||||
vendored_rails = File.dirname(__FILE__) + '/../../../../vendor/rails'
|
||||
|
||||
@@ -45,8 +45,7 @@ if RAILS_VER >= '2.1'
|
||||
end
|
||||
|
||||
require 'validates_timeliness'
|
||||
|
||||
ValidatesTimeliness.enable_datetime_select_extension!
|
||||
require 'validates_timeliness/matcher'
|
||||
|
||||
ActiveRecord::Migration.verbose = false
|
||||
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
||||
|
||||
@@ -358,18 +358,6 @@ describe ValidatesTimeliness::Validator do
|
||||
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)
|
||||
@@ -409,6 +397,16 @@ describe ValidatesTimeliness::Validator do
|
||||
end
|
||||
end
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
end
|
||||
|
||||
describe "instance with :with_time option" do
|
||||
|
||||
it "should validate date attribute as datetime combining value of :with_time against restrictions " do
|
||||
@@ -423,20 +421,31 @@ describe ValidatesTimeliness::Validator do
|
||||
should_have_no_error(:birth_date, :on_or_before)
|
||||
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)
|
||||
validate_with(:birth_date, "2000-01-01")
|
||||
should_have_no_error(:birth_date, :equal_to)
|
||||
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)
|
||||
validate_with(:birth_time, "12:30")
|
||||
should_have_error(:birth_time, :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)
|
||||
validate_with(:birth_time, "12:30")
|
||||
should_have_no_error(:birth_time, :on_or_before)
|
||||
end
|
||||
|
||||
it "should should ignore usec value on combined value if :ignore_usec option is true" do
|
||||
configure_validator(:type => :time, :with_date => Date.new(2000,1,1), :on_or_before => Time.mktime(2000,1,1,12,30), :ignore_usec => true)
|
||||
validate_with(:birth_time, Time.mktime(2000,1,1,12,30,0,50))
|
||||
should_have_no_error(:birth_time, :on_or_before)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -506,12 +515,6 @@ describe ValidatesTimeliness::Validator do
|
||||
configure_validator(:type => :date, :before => before)
|
||||
validator.send(:interpolation_values, :before, before.to_date).should == {:restriction => before}
|
||||
end
|
||||
|
||||
it "should return empty hash if no interpolation keys are in message" do
|
||||
before = '1900-01-01'
|
||||
configure_validator(:type => :date, :before => before, :before_message => 'too late')
|
||||
validator.send(:interpolation_values, :before, before.to_date).should be_empty
|
||||
end
|
||||
else
|
||||
it "should return array of interpolation values" do
|
||||
before = '1900-01-01'
|
||||
|
||||
@@ -2,26 +2,25 @@
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{validates_timeliness}
|
||||
s.version = "2.0.0"
|
||||
s.version = "2.2.1"
|
||||
|
||||
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-04-12}
|
||||
s.date = %q{2009-09-12}
|
||||
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/core_ext", "lib/validates_timeliness/core_ext/date.rb", "lib/validates_timeliness/core_ext/date_time.rb", "lib/validates_timeliness/core_ext/time.rb", "lib/validates_timeliness/action_view", "lib/validates_timeliness/action_view/instance_tag.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.yml", "lib/validates_timeliness/validation_methods.rb", "lib/validates_timeliness/active_record", "lib/validates_timeliness/active_record/attribute_methods.rb", "lib/validates_timeliness/active_record/multiparameter_attributes.rb", "lib/validates_timeliness/parser.rb", "lib/validates_timeliness/formats.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/spec", "lib/validates_timeliness/spec/rails", "lib/validates_timeliness/spec/rails/matchers", "lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb", "lib/validates_timeliness.rb", "spec/core_ext", "spec/core_ext/dummy_time_spec.rb", "spec/validator_spec.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/ginger_scenarios.rb", "spec/spec_helper.rb", "spec/formats_spec.rb", "spec/active_record", "spec/active_record/attribute_methods_spec.rb", "spec/active_record/multiparameter_attributes_spec.rb", "spec/time_travel", "spec/time_travel/time_travel.rb", "spec/time_travel/time_extensions.rb", "spec/time_travel/MIT-LICENSE", "spec/parser_spec.rb", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/resources", "spec/resources/person.rb", "spec/resources/sqlite_patch.rb", "spec/resources/schema.rb", "spec/resources/application.rb"]
|
||||
s.has_rdoc = true
|
||||
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.homepage = %q{http://github.com/adzap/validates_timeliness}
|
||||
s.require_paths = ["lib"]
|
||||
s.rubyforge_project = %q{validatestime}
|
||||
s.rubygems_version = %q{1.3.1}
|
||||
s.rubygems_version = %q{1.3.3}
|
||||
s.summary = %q{Date and time validation plugin for Rails 2.x which allows custom formats}
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||
s.specification_version = 2
|
||||
s.specification_version = 3
|
||||
|
||||
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user