Compare commits

..

15 Commits
3.0.0 ... 3.0.3

Author SHA1 Message Date
Adam Meehan
01d1dc7c5e version 3.0.3 2010-12-11 09:17:31 +11:00
Adam Meehan
b09f937688 tiny consistency change 2010-12-11 09:17:08 +11:00
Adam Meehan
509336e080 Fix type_cast_value for values which don't respond to to_time or to_date (renatoelias) 2010-12-11 09:13:35 +11:00
Adam Meehan
e345b5dc7d update README with supported Ruby platforms 2010-12-04 17:14:46 +11:00
Adam Meehan
57eef70b7c remove ruby-debug from Gemfile for now 2010-12-04 17:14:16 +11:00
Adam Meehan
46eafe31a1 version 3.0.2 2010-12-04 16:40:13 +11:00
Adam Meehan
f17a799b5c updated Timeliness to 0.3.2 for zone and offset in string support 2010-12-04 16:34:34 +11:00
Adam Meehan
3bf364a395 reluctantly overriding whole execute_callstack_for_multiparameter_attributes method
this fixes issue for Date column types. Damn method is just too
unwieldly and should be refactored in Rails.
2010-12-04 16:29:05 +11:00
Adam Meehan
bfcab52c22 can use super for some InstanceTag methods
cleans up for DateTimeSelect extension a little
2010-11-02 10:13:13 +11:00
Adam Meehan
20c0aaa793 version 3.0.1 2010-11-02 09:53:25 +11:00
Adam Meehan
a756b66f75 add contributors link and change credits to maintainers 2010-11-02 09:51:45 +11:00
José Valim
889b5a9b07 Move timeliness defined methods to a module so they can be overwritten. 2010-11-01 14:56:03 -02:00
José Valim
0e3f56e26d Update Gemfile.
* Use gemspec to retrieve timeliness and other gem specifications.
* Remove test group (since it is the only option) and break groups into AR and Mongoid.
2010-11-01 14:47:19 -02:00
Adam Meehan
7ca662ada8 readme words 2010-10-21 08:25:35 +11:00
Adam Meehan
03effb9e52 readme tweaks 2010-10-21 07:50:37 +11:00
16 changed files with 196 additions and 97 deletions

View File

@@ -1,3 +1,13 @@
= 3.0.3 [2010-12-11]
* Fix validation of values which don't respond to to_date or to_time (renatoelias)
= 3.0.2 [2010-12-04]
* Fix AR multiparameter extension for Date columns
* Update to Timeliness 0.3.2 for zone abbreviation and offset support
= 3.0.1 [2010-11-02]
* Generate timeliness write methods in an included module to allow overriding in model class (josevalim)
= 3.0.0 [2010-10-18] = 3.0.0 [2010-10-18]
* Rails 3 and ActiveModel compatibility * Rails 3 and ActiveModel compatibility
* Uses ActiveModel::EachValidator as validator base class. * Uses ActiveModel::EachValidator as validator base class.

25
Gemfile
View File

@@ -1,16 +1,19 @@
source 'http://rubygems.org' source 'http://rubygems.org'
gem 'timeliness', '~> 0.1.1' gemspec
group :test do gem 'ZenTest'
gem 'ZenTest' gem 'rails', '3.0.0'
gem 'rails', '3.0.0' gem 'rspec', '>= 2.0.0.beta.17'
gem 'sqlite3-ruby', :require => 'sqlite3' gem 'rspec-rails', '>= 2.0.0.beta.17'
gem 'mongoid', '2.0.0.beta.17' gem 'timecop'
gem 'rspec_tag_matchers'
group :mongoid do
gem 'mongoid', '2.0.0.beta.19'
gem 'bson_ext', '1.0.4' gem 'bson_ext', '1.0.4'
gem 'rspec', '>= 2.0.0.beta.17' end
gem 'rspec-rails', '>= 2.0.0.beta.17'
gem 'timecop' group :active_record do
gem 'rspec_tag_matchers' gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'ruby-debug'
end end

View File

@@ -1,3 +1,9 @@
PATH
remote: .
specs:
validates_timeliness (3.0.2)
timeliness (~> 0.3.2)
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
@@ -31,26 +37,24 @@ GEM
activesupport (3.0.0) activesupport (3.0.0)
arel (1.0.1) arel (1.0.1)
activesupport (~> 3.0.0) activesupport (~> 3.0.0)
bson (1.0.4) bson (1.1.1)
bson_ext (1.0.4) bson_ext (1.0.4)
builder (2.1.2) builder (2.1.2)
columnize (0.3.1)
diff-lcs (1.1.2) diff-lcs (1.1.2)
erubis (2.6.6) erubis (2.6.6)
abstract (>= 1.0.0) abstract (>= 1.0.0)
i18n (0.4.1) i18n (0.4.2)
linecache (0.43) mail (2.2.10)
mail (2.2.6.1)
activesupport (>= 2.3.6) activesupport (>= 2.3.6)
mime-types i18n (~> 0.4.1)
treetop (>= 1.4.5) mime-types (~> 1.16)
treetop (~> 1.4.8)
mime-types (1.16) mime-types (1.16)
mongo (1.0.7) mongo (1.0.9)
bson (>= 1.0.4) bson (>= 1.0.5)
mongoid (2.0.0.beta.17) mongoid (2.0.0.beta.19)
activemodel (~> 3.0.0) activemodel (~> 3.0)
bson (= 1.0.4) mongo (= 1.0.9)
mongo (= 1.0.7)
tzinfo (~> 0.3.22) tzinfo (~> 0.3.22)
will_paginate (~> 3.0.pre) will_paginate (~> 3.0.pre)
nokogiri (1.4.3.1) nokogiri (1.4.3.1)
@@ -58,7 +62,7 @@ GEM
rack (1.2.1) rack (1.2.1)
rack-mount (0.6.13) rack-mount (0.6.13)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (0.5.4) rack-test (0.5.6)
rack (>= 1.0) rack (>= 1.0)
rails (3.0.0) rails (3.0.0)
actionmailer (= 3.0.0) actionmailer (= 3.0.0)
@@ -89,16 +93,11 @@ GEM
rspec_tag_matchers (1.0.0) rspec_tag_matchers (1.0.0)
nokogiri (>= 1.4.0) nokogiri (>= 1.4.0)
rspec-rails (>= 1.2.6) rspec-rails (>= 1.2.6)
ruby-debug (0.10.3)
columnize (>= 0.1)
ruby-debug-base (~> 0.10.3.0)
ruby-debug-base (0.10.3)
linecache (>= 0.3)
sqlite3-ruby (1.3.1) sqlite3-ruby (1.3.1)
thor (0.14.1) thor (0.14.6)
timecop (0.3.5) timecop (0.3.5)
timeliness (0.1.1) timeliness (0.3.2)
treetop (1.4.8) treetop (1.4.9)
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.23) tzinfo (0.3.23)
will_paginate (3.0.pre2) will_paginate (3.0.pre2)
@@ -109,12 +108,12 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
ZenTest ZenTest
bson_ext (= 1.0.4) bson_ext (= 1.0.4)
mongoid (= 2.0.0.beta.17) mongoid (= 2.0.0.beta.19)
rails (= 3.0.0) rails (= 3.0.0)
rspec (>= 2.0.0.beta.17) rspec (>= 2.0.0.beta.17)
rspec-rails (>= 2.0.0.beta.17) rspec-rails (>= 2.0.0.beta.17)
rspec_tag_matchers rspec_tag_matchers
ruby-debug
sqlite3-ruby sqlite3-ruby
timecop timecop
timeliness (~> 0.1.1) timeliness (~> 0.3.2)
validates_timeliness!

View File

@@ -18,12 +18,13 @@ If you a looking for the old version for Rails 2.x go here[http://github.com/adz
* Only Rails date/time validation plugin offering complete validation (See ORM/ODM support) * Only Rails date/time validation plugin offering complete validation (See ORM/ODM support)
* Adds extensions to fix Rails date/time select issues * Adds extensions to fix Rails date/time select issues (See Extensions)
* Uses extensible date/time parser ({timeliness gem}[http://github.com/adzap/timeliness]) * Uses extensible date/time parser (Using {timeliness gem}[http://github.com/adzap/timeliness]. See Plugin Parser)
* Supports I18n for the error messages * Supports I18n for the error messages
* Supports MRI 1.8.x and 1.9.x and Rubinius.
== Installation == Installation
@@ -31,10 +32,10 @@ As plugin (from master)
rails plugin install git://github.com/adzap/validates_timeliness.git rails plugin install git://github.com/adzap/validates_timeliness.git
As gem (in beta) As gem
# in Gemfile # in Gemfile
gem 'validates_timeliness', '~> 3.0.0' gem 'validates_timeliness', '~> 3.0.2'
# Run bundler # Run bundler
$ bundle install $ bundle install
@@ -46,6 +47,8 @@ Then run
This creates configuration initializer and locale files. In the initializer, you there are a number of config This creates configuration initializer and locale files. In the initializer, you there are a number of config
options to customize the plugin. options to customize the plugin.
NOTE: You may wish to enable the plugin parser and the extensions to start. Please read those sections first.
== Examples == Examples
@@ -175,7 +178,8 @@ You can also use validation options for custom error messages. The following opt
:after_message :after_message
:on_or_after_message :on_or_after_message
Note: There is no :between_message option. The between error message should be defined using the :on_or_before and :on_or_after messages. Note: There is no :between_message option. The between error message should be defined using the
:on_or_before and :on_or_after messages.
It is highly recommended you use the I18n system for error messages. It is highly recommended you use the I18n system for error messages.
@@ -254,36 +258,42 @@ To turn them on/off:
config.ignore_restriction_errors = true config.ignore_restriction_errors = true
=== Display Invalid Values in Select Helpers == Extensions
The plugin offers an extension for ActionView to allowing invalid
date and time values to be redisplayed to the user as feedback, instead of
a blank field which happens by default in Rails. Though the date helpers make this a
pretty rare occurrence, given the select dropdowns for each date/time component, but
it may be something of interest.
To activate it, put this in an initializer:
# in the setup block
config.enable_date_time_select_extension!
=== Strict Parsing for Select Helpers === Strict Parsing for Select Helpers
When using date/time select helpers, the component values are handled by ActiveRecord using When using date/time select helpers, the component values are handled by ActiveRecord using
the Time class to instantiate them into a time value. But this mean that some invalid dates, the Time class to instantiate them into a time value. This means that some invalid dates,
such as 31st June, are shifted forward and treated as valid. To handle these cases in a strict such as 31st June, are shifted forward and treated as valid. To handle these cases in a strict
way you can enable the plugin handler to treat them as invalid dates. way, you can enable the plugin extension to treat them as invalid dates.
To activate it, put this in an initializer: To activate it, uncomment this line in the initializer:
# in the setup block # in the setup block
config.enable_multiparameter_extension! config.enable_multiparameter_extension!
== Credits === Display Invalid Values in Select Helpers
* Adam Meehan (adam.meehan@gmail.com, http://github.com/adzap) The plugin offers an extension for ActionView to allowing invalid date and time values to be
redisplayed to the user as feedback, instead of a blank field which happens by default in
Rails. Though the date helpers make this a pretty rare occurrence, given the select dropdowns
for each date/time component, but it may be something of interest.
To activate it, uncomment this line in the initializer:
# in the setup block
config.enable_date_time_select_extension!
== Contributors
To see the generous people who have contributed code, take a look at the {contributors list}[http://github.com/adzap/validates_timeliness/contributors].
== Maintainers
* {Adam Meehan}[http://github.com/adzap]
== License == License

View File

@@ -22,7 +22,7 @@ spec = Gem::Specification.new do |s|
s.homepage = "http://github.com/adzap/validates_timeliness" s.homepage = "http://github.com/adzap/validates_timeliness"
s.require_path = 'lib' s.require_path = 'lib'
s.files = %w(validates_timeliness.gemspec LICENSE CHANGELOG.rdoc README.rdoc Rakefile) + Dir.glob("{lib,spec}/**/*") s.files = %w(validates_timeliness.gemspec LICENSE CHANGELOG.rdoc README.rdoc Rakefile) + Dir.glob("{lib,spec}/**/*")
s.add_runtime_dependency 'timeliness', '~> 0.1.0' s.add_runtime_dependency 'timeliness', '~> 0.3.2'
end end
desc 'Default: run specs.' desc 'Default: run specs.'

View File

@@ -42,7 +42,7 @@ module ValidatesTimeliness
super super
end end
EOV EOV
class_eval(method_body, __FILE__, line) generated_timeliness_methods.module_eval(method_body, __FILE__, line)
end end
def define_timeliness_before_type_cast_method(attr_name) def define_timeliness_before_type_cast_method(attr_name)
@@ -51,7 +51,11 @@ module ValidatesTimeliness
_timeliness_raw_value_for('#{attr_name}') _timeliness_raw_value_for('#{attr_name}')
end end
EOV EOV
class_eval(method_body, __FILE__, line) generated_timeliness_methods.module_eval(method_body, __FILE__, line)
end
def generated_timeliness_methods
@generated_timeliness_methods ||= Module.new.tap { |m| include(m) }
end end
end end

View File

@@ -2,7 +2,7 @@ module ValidatesTimeliness
module Conversion module Conversion
def type_cast_value(value, type) def type_cast_value(value, type)
return nil if value.nil? return nil if value.nil? || !value.respond_to?(:to_time)
value = value.in_time_zone if value.acts_like?(:time) && @timezone_aware value = value.in_time_zone if value.acts_like?(:time) && @timezone_aware
value = case type value = case type

View File

@@ -10,7 +10,6 @@ module ValidatesTimeliness
included do included do
alias_method_chain :datetime_selector, :timeliness alias_method_chain :datetime_selector, :timeliness
alias_method_chain :value, :timeliness
end end
module InstanceMethods module InstanceMethods
@@ -22,13 +21,13 @@ module ValidatesTimeliness
datetime_selector_without_timeliness(*args) datetime_selector_without_timeliness(*args)
end end
def value_with_timeliness(object) def value(object)
unless @timeliness_date_or_time_tag && @template_object.params[@object_name] unless @timeliness_date_or_time_tag && @template_object.params[@object_name]
return value_without_timeliness(object) return super
end end
pairs = @template_object.params[@object_name].select {|k,v| k =~ /^#{@method_name}\(/ } pairs = @template_object.params[@object_name].select {|k,v| k =~ /^#{@method_name}\(/ }
return value_without_timeliness(object) if pairs.empty? return super if pairs.empty?
values = [nil] * 6 values = [nil] * 6
pairs.map do |(param, value)| pairs.map do |(param, value)|

View File

@@ -3,26 +3,64 @@ module ValidatesTimeliness
module MultiparameterHandler module MultiparameterHandler
extend ActiveSupport::Concern extend ActiveSupport::Concern
# Stricter handling of date and time values from multiparameter
# assignment from the date/time select view helpers
included do included do
alias_method_chain :instantiate_time_object, :timeliness alias_method_chain :instantiate_time_object, :timeliness
alias_method_chain :execute_callstack_for_multiparameter_attributes, :timeliness
end end
private private
# Stricter handling of date and time values from multiparameter def invalid_multiparameter_date_or_time_as_string(values)
# assignment from the date/time select view helpers value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-")
# value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty?
def instantiate_time_object_with_timeliness(name, values) value
unless Date.valid_civil?(*values[0..2]) end
value = [values[0], *values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-")
value += ' ' + values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless values[3..5].empty?
return value
end
if self.class.send(:create_time_zone_conversion_attribute?, name, column_for_attribute(name)) def instantiate_time_object_with_timeliness(name, values)
Time.zone.local(*values) if Date.valid_civil?(*values[0..2])
instantiate_time_object_without_timeliness(name, values)
else else
Time.time_with_datetime_fallback(self.class.default_timezone, *values) invalid_multiparameter_date_or_time_as_string(values)
end
end
def instantiate_date_object(name, values)
values = values.map { |v| v.nil? ? 1 : v }
Date.new(*values)
rescue ArgumentError => ex
invalid_multiparameter_date_or_time_as_string(values)
end
def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack)
errors = []
callstack.each do |name, values_with_empty_parameters|
begin
klass = (self.class.reflect_on_aggregation(name.to_sym) || column_for_attribute(name)).klass
values = values_with_empty_parameters.reject { |v| v.nil? }
if values.empty?
send(name + "=", nil)
else
value = if Time == klass
instantiate_time_object(name, values)
elsif Date == klass
instantiate_date_object(name, values_with_empty_parameters)
else
klass.new(*values)
end
send(name + "=", value)
end
rescue => ex
errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name)
end
end
unless errors.empty?
raise ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes"
end end
end end

View File

@@ -72,9 +72,8 @@ module ValidatesTimeliness
end end
def attribute_raw_value(record, attr_name) def attribute_raw_value(record, attr_name)
if record.respond_to?(:_timeliness_raw_value_for) record.respond_to?(:_timeliness_raw_value_for) &&
record._timeliness_raw_value_for(attr_name) record._timeliness_raw_value_for(attr_name)
end
end end
def timezone_aware?(record, attr_name) def timezone_aware?(record, attr_name)

View File

@@ -1,3 +1,3 @@
module ValidatesTimeliness module ValidatesTimeliness
VERSION = '3.0.0' VERSION = '3.0.3'
end end

View File

@@ -75,6 +75,13 @@ class Employee < ActiveRecord::Base
validates_time :birth_time validates_time :birth_time
validates_datetime :birth_datetime validates_datetime :birth_datetime
define_attribute_methods define_attribute_methods
attr_accessor :redefined_birth_date_called
def birth_date=(value)
self.redefined_birth_date_called = true
super
end
end end
Rspec.configure do |c| Rspec.configure do |c|

View File

@@ -34,6 +34,12 @@ describe ValidatesTimeliness::AttributeMethods do
r._timeliness_raw_value_for(:birth_datetime).should == date_string r._timeliness_raw_value_for(:birth_datetime).should == date_string
end end
it 'should not overwrite user defined methods' do
e = Employee.new
e.birth_date = '2010-01-01'
e.redefined_birth_date_called.should be_true
end
context "with plugin parser" do context "with plugin parser" do
class PersonWithParser class PersonWithParser
include TestModel include TestModel

View File

@@ -22,6 +22,10 @@ describe ValidatesTimeliness::Conversion do
it "should return date part of datetime value" do it "should return date part of datetime value" do
type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0), :date).should == Date.new(2010, 1, 1) type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0), :date).should == Date.new(2010, 1, 1)
end end
it 'should return nil for invalid value types' do
type_cast_value(12, :date).should == nil
end
end end
describe "for time type" do describe "for time type" do
@@ -40,6 +44,10 @@ describe ValidatesTimeliness::Conversion do
it "should return dummy date with time part for datetime value" do it "should return dummy date with time part for datetime value" do
type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :time).should == Time.utc(2000, 1, 1, 12, 34, 56) type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :time).should == Time.utc(2000, 1, 1, 12, 34, 56)
end end
it 'should return nil for invalid value types' do
type_cast_value(12, :time).should == nil
end
end end
describe "for datetime type" do describe "for datetime type" do
@@ -63,6 +71,10 @@ describe ValidatesTimeliness::Conversion do
result.should == Time.zone.local(2010, 1, 1, 23, 34, 56) result.should == Time.zone.local(2010, 1, 1, 23, 34, 56)
result.zone.should == 'EST' result.zone.should == 'EST'
end end
it 'should return nil for invalid value types' do
type_cast_value(12, :datetime).should == nil
end
end end
describe "ignore_usec option" do describe "ignore_usec option" do

View File

@@ -3,19 +3,31 @@ require 'spec_helper'
describe ValidatesTimeliness::Extensions::MultiparameterHandler do describe ValidatesTimeliness::Extensions::MultiparameterHandler do
let(:employee) { Employee.new } let(:employee) { Employee.new }
it 'should return string value for invalid dates' do context "time column" do
instantiate_time_object('birth_date', [2000, 2, 31]).should == '2000-02-31' it 'should return string value for invalid date portion' do
multiparameter_attribute(:birth_datetime, [2000, 2, 31, 12, 0, 0])
employee.birth_datetime_before_type_cast.should == '2000-02-31 12:00:00'
end
it 'should return Time value for valid datetimes' do
multiparameter_attribute(:birth_datetime, [2000, 2, 28, 12, 0, 0])
employee.birth_datetime_before_type_cast.should be_kind_of(Time)
end
end end
it 'should return string value for invalid datetimes' do context "date column" do
instantiate_time_object('birth_datetime', [2000, 2, 31, 12, 0, 0]).should == '2000-02-31 12:00:00' it 'should return string value for invalid date' do
end multiparameter_attribute(:birth_date, [2000, 2, 31])
employee.birth_date_before_type_cast.should == '2000-02-31'
it 'should return Time value for valid datetimes' do end
instantiate_time_object('birth_datetime', [2000, 2, 28, 12, 0, 0]).should be_kind_of(Time)
it 'should return Date value for valid date' do
multiparameter_attribute(:birth_date, [2000, 2, 28])
employee.birth_date_before_type_cast.should be_kind_of(Date)
end
end end
def instantiate_time_object(name, values) def multiparameter_attribute(name, values)
employee.send(:instantiate_time_object, name, values) employee.send(:execute_callstack_for_multiparameter_attributes, name.to_s => values)
end end
end end

View File

@@ -2,11 +2,11 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = %q{validates_timeliness} s.name = %q{validates_timeliness}
s.version = "3.0.0" s.version = "3.0.3"
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.date = %q{2010-10-18} s.date = %q{2010-12-11}
s.description = %q{Date and time validation plugin for Rails which allows custom formats} s.description = %q{Date and time validation plugin for Rails which allows custom formats}
s.email = %q{adam.meehan@gmail.com} s.email = %q{adam.meehan@gmail.com}
s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc", "LICENSE"] s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc", "LICENSE"]
@@ -22,11 +22,11 @@ Gem::Specification.new do |s|
s.specification_version = 3 s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<timeliness>, ["~> 0.1.0"]) s.add_runtime_dependency(%q<timeliness>, ["~> 0.3.2"])
else else
s.add_dependency(%q<timeliness>, ["~> 0.1.0"]) s.add_dependency(%q<timeliness>, ["~> 0.3.2"])
end end
else else
s.add_dependency(%q<timeliness>, ["~> 0.1.0"]) s.add_dependency(%q<timeliness>, ["~> 0.3.2"])
end end
end end