Compare commits

...

21 Commits
2.1.0 ... 2.2.0

Author SHA1 Message Date
Adam Meehan
76e159b350 version 2.2.0 2009-09-12 13:43:50 +10:00
Adam Meehan
c762b6d4f8 added version file with VERSION constant 2009-09-12 13:42:07 +10:00
Adam Meehan
91f9f65bc0 tiny doc change 2009-09-12 13:36:57 +10:00
Adam Meehan
6db8b7d908 push dummy date value assignment into Formats.parse and allow custom values to be used 2009-09-12 13:07:01 +10:00
Adam Meehan
d3c5101f92 use implied_type in restriction evaluations 2009-09-12 13:05:12 +10:00
Adam Meehan
899e96b880 tiny clean up 2009-09-12 13:05:12 +10:00
Adam Meehan
162faf632a push strict override for format option into Formats.parse 2009-09-12 13:05:12 +10:00
Adam Meehan
1b865cc834 split ignore_sec into own describe 2009-09-12 13:05:11 +10:00
Adam Meehan
c29478df45 fix deprecation for ActiveRecord::Errors#generate_message in Rails 2.3.4
ginger scenario added 2.3.4
2009-09-12 13:05:11 +10:00
Adam Meehan
df3283e5a1 fix ignore_usec for with_date and with_time options 2009-09-12 13:05:04 +10:00
Adam Meehan
0e382e15f2 moved action view extension enable call to relevant spec 2009-09-11 13:13:02 +10:00
Adam Meehan
9a697b9cab finally fixed spec for latest rspec 2009-09-11 13:11:09 +10:00
Adam Meehan
7bf7ed0569 catch both possible exception types 2009-09-09 16:25:04 +10:00
Adam Meehan
a969a49ae8 little tweaks 2009-09-09 16:16:11 +10:00
Adam Meehan
687e61a3f2 Merge branch 'master' of github.com:adzap/validates_timeliness 2009-09-05 19:50:43 +10:00
Adam Meehan
4cc20ae620 fixed some bad rescue behaviour in parse method 2009-09-05 19:47:49 +10:00
adzap
2d510504e6 change to lambda in examples 2009-08-26 21:07:29 -07:00
Adam Meehan
2028d68b17 checking proc arity in option value for ruby 1.9 compat 2009-08-22 14:59:46 +10:00
Adam Meehan
e399c6b510 moved mutliparam helper methods our of AR to reduce method pollution 2009-07-28 12:52:25 +10:00
Adam Meehan
7aa1a87731 require matcher in spec helper 2009-07-28 12:51:32 +10:00
Adam Meehan
1a31e7463d have to manually require matcher now because the presence of the Spec
namespace caused issues for shoulda when not using rspec:wq
2009-07-07 15:32:15 +10:00
20 changed files with 193 additions and 115 deletions

View File

@@ -1,3 +1,10 @@
= 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+

View File

@@ -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
@@ -263,6 +263,18 @@ Now you get:
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
@@ -355,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.

View File

@@ -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.1.0"
GEM_VERSION = ValidatesTimeliness::VERSION
AUTHOR = "Adam Meehan"
EMAIL = "adam.meehan@gmail.com"
HOMEPAGE = "http://github.com/adzap/validates_timeliness"

View File

@@ -2,7 +2,6 @@ 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'

View File

@@ -36,7 +36,8 @@ module ValidatesTimeliness
end
def read_attribute_before_type_cast_with_timeliness(attr_name)
return @attributes_cache["_#{attr_name}_before_type_cast"] if @attributes_cache.has_key?("_#{attr_name}_before_type_cast")
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
@@ -50,11 +51,9 @@ module ValidatesTimeliness
if [:date, :time, :datetime].include?(column.type)
time_zone_aware = create_time_zone_conversion_attribute?(name, column) rescue false
class_eval <<-EOV
def #{name}=(value)
write_date_time_attribute('#{name}', value, #{column.type.inspect}, #{time_zone_aware})
end
EOV
define_method("#{name}=") do |value|
write_date_time_attribute(name, value, column.type, time_zone_aware)
end
timeliness_methods << name
end
end

View File

@@ -5,6 +5,33 @@ 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)
@@ -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,28 +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
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
end

View File

@@ -23,6 +23,7 @@ module ValidatesTimeliness
# Set the threshold value for a two digit year to be considered last century
#
# Default: 30
#
# Example:
@@ -32,6 +33,14 @@ module ValidatesTimeliness
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
@@ -179,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)
@@ -195,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.

View File

@@ -0,0 +1 @@
require 'validates_timeliness/spec/rails/matchers/validate_timeliness'

View File

@@ -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)
raise if time_array.nil?
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, options.reverse_merge(:strict => true))
return nil 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

View File

@@ -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

View File

@@ -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))
@@ -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

View File

@@ -0,0 +1,3 @@
module ValidatesTimeliness
VERSION = "2.2.0"
end

View File

@@ -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

View File

@@ -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
@@ -44,5 +44,9 @@ describe ValidatesTimeliness::ActiveRecord::MultiparameterAttributes do
obj.send(:execute_callstack_for_multiparameter_attributes, @callstack)
end
end
def time_array_to_string(*args)
ValidatesTimeliness::ActiveRecord.time_array_to_string(*args)
end
end

View File

@@ -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
@@ -177,6 +172,22 @@ describe ValidatesTimeliness::Formats do
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')
@@ -219,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
@@ -251,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|

View File

@@ -9,7 +9,7 @@
# 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("Rails #{v}")

View File

@@ -1,4 +1,5 @@
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper')
require 'validates_timeliness/matcher'
class NoValidation < Person
end

View File

@@ -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:'})

View File

@@ -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

View File

@@ -2,16 +2,16 @@
Gem::Specification.new do |s|
s.name = %q{validates_timeliness}
s.version = "2.1.0"
s.version = "2.2.0"
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-06-20}
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/active_record", "lib/validates_timeliness/active_record/multiparameter_attributes.rb", "lib/validates_timeliness/active_record/attribute_methods.rb", "lib/validates_timeliness/parser.rb", "lib/validates_timeliness/core_ext", "lib/validates_timeliness/core_ext/date.rb", "lib/validates_timeliness/core_ext/time.rb", "lib/validates_timeliness/core_ext/date_time.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/validation_methods.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.yml", "lib/validates_timeliness/spec", "lib/validates_timeliness/spec/rails", "lib/validates_timeliness/spec/rails/matchers", "lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb", "lib/validates_timeliness/action_view", "lib/validates_timeliness/action_view/instance_tag.rb", "lib/validates_timeliness/formats.rb", "lib/validates_timeliness.rb", "spec/active_record", "spec/active_record/multiparameter_attributes_spec.rb", "spec/active_record/attribute_methods_spec.rb", "spec/formats_spec.rb", "spec/parser_spec.rb", "spec/core_ext", "spec/core_ext/dummy_time_spec.rb", "spec/spec_helper.rb", "spec/ginger_scenarios.rb", "spec/time_travel", "spec/time_travel/time_extensions.rb", "spec/time_travel/time_travel.rb", "spec/time_travel/MIT-LICENSE", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/validator_spec.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/resources", "spec/resources/schema.rb", "spec/resources/application.rb", "spec/resources/person.rb", "spec/resources/sqlite_patch.rb"]
s.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/core_ext", "lib/validates_timeliness/core_ext/date.rb", "lib/validates_timeliness/core_ext/time.rb", "lib/validates_timeliness/core_ext/date_time.rb", "lib/validates_timeliness/validator.rb", "lib/validates_timeliness/validation_methods.rb", "lib/validates_timeliness/locale", "lib/validates_timeliness/locale/en.yml", "lib/validates_timeliness/spec", "lib/validates_timeliness/spec/rails", "lib/validates_timeliness/spec/rails/matchers", "lib/validates_timeliness/spec/rails/matchers/validate_timeliness.rb", "lib/validates_timeliness/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/core_ext", "spec/core_ext/dummy_time_spec.rb", "spec/spec_helper.rb", "spec/ginger_scenarios.rb", "spec/time_travel", "spec/time_travel/time_extensions.rb", "spec/time_travel/time_travel.rb", "spec/time_travel/MIT-LICENSE", "spec/spec", "spec/spec/rails", "spec/spec/rails/matchers", "spec/spec/rails/matchers/validate_timeliness_spec.rb", "spec/validator_spec.rb", "spec/action_view", "spec/action_view/instance_tag_spec.rb", "spec/resources", "spec/resources/schema.rb", "spec/resources/application.rb", "spec/resources/person.rb", "spec/resources/sqlite_patch.rb"]
s.homepage = %q{http://github.com/adzap/validates_timeliness}
s.require_paths = ["lib"]
s.rubyforge_project = %q{validatestime}