From fdc30869761301c21eacf04da34284c373fd7905 Mon Sep 17 00:00:00 2001 From: Adam Meehan Date: Sun, 1 Aug 2010 18:35:18 +1000 Subject: [PATCH] initial Rails 3 rewrite commit completely rewritten for ActiveModel compatibility uses ActiveModel EachValidator class as validator base class simplifies :between by splitting into a :on_or_before and an :on_of_after only :is_at option tested --- .gitignore | 1 + .rspec | 2 + Gemfile | 11 ++ Gemfile.lock | 104 ++++++++++++++++++ LICENSE | 20 ++++ Rakefile | 44 ++++++++ autotest/discover.rb | 1 + lib/validates_timeliness.rb | 26 +++++ lib/validates_timeliness/conversion.rb | 27 +++++ lib/validates_timeliness/helper_methods.rb | 19 ++++ lib/validates_timeliness/locale/en.yml | 16 +++ lib/validates_timeliness/validator.rb | 61 ++++++++++ lib/validates_timeliness/version.rb | 3 + spec/model_helpers.rb | 27 +++++ spec/spec_helper.rb | 44 ++++++++ spec/validates_timeliness/conversion_spec.rb | 102 +++++++++++++++++ .../validator/is_at_spec.rb | 85 ++++++++++++++ spec/validates_timeliness/validator_spec.rb | 49 +++++++++ 18 files changed, 642 insertions(+) create mode 100644 .gitignore create mode 100644 .rspec create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 LICENSE create mode 100644 Rakefile create mode 100644 autotest/discover.rb create mode 100644 lib/validates_timeliness.rb create mode 100644 lib/validates_timeliness/conversion.rb create mode 100644 lib/validates_timeliness/helper_methods.rb create mode 100644 lib/validates_timeliness/locale/en.yml create mode 100644 lib/validates_timeliness/validator.rb create mode 100644 lib/validates_timeliness/version.rb create mode 100644 spec/model_helpers.rb create mode 100644 spec/spec_helper.rb create mode 100644 spec/validates_timeliness/conversion_spec.rb create mode 100644 spec/validates_timeliness/validator/is_at_spec.rb create mode 100644 spec/validates_timeliness/validator_spec.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..01d0a08 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +pkg/ diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..42f3246 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format nested +--color diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..5625c42 --- /dev/null +++ b/Gemfile @@ -0,0 +1,11 @@ +source 'http://rubygems.org' + +group :test do + gem 'ZenTest' + gem 'rails', '3.0.0.rc' + gem 'sqlite3-ruby', :require => 'sqlite3' + gem 'ruby-debug' + gem 'rspec', '>= 2.0.0.beta.17' + gem 'rspec-rails', '>= 2.0.0.beta.17' + gem 'timecop' +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..cf7ac4e --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,104 @@ +GEM + remote: http://rubygems.org/ + specs: + ZenTest (4.3.3) + abstract (1.0.0) + actionmailer (3.0.0.rc) + actionpack (= 3.0.0.rc) + mail (~> 2.2.5) + actionpack (3.0.0.rc) + activemodel (= 3.0.0.rc) + activesupport (= 3.0.0.rc) + builder (~> 2.1.2) + erubis (~> 2.6.6) + i18n (~> 0.4.1) + rack (~> 1.2.1) + rack-mount (~> 0.6.9) + rack-test (~> 0.5.4) + tzinfo (~> 0.3.22) + activemodel (3.0.0.rc) + activesupport (= 3.0.0.rc) + builder (~> 2.1.2) + i18n (~> 0.4.1) + activerecord (3.0.0.rc) + activemodel (= 3.0.0.rc) + activesupport (= 3.0.0.rc) + arel (~> 0.4.0) + tzinfo (~> 0.3.22) + activeresource (3.0.0.rc) + activemodel (= 3.0.0.rc) + activesupport (= 3.0.0.rc) + activesupport (3.0.0.rc) + arel (0.4.0) + activesupport (>= 3.0.0.beta) + builder (2.1.2) + columnize (0.3.1) + diff-lcs (1.1.2) + erubis (2.6.6) + abstract (>= 1.0.0) + i18n (0.4.1) + linecache (0.43) + mail (2.2.5) + activesupport (>= 2.3.6) + mime-types + treetop (>= 1.4.5) + mime-types (1.16) + nokogiri (1.4.2) + polyglot (0.3.1) + rack (1.2.1) + rack-mount (0.6.9) + rack (>= 1.0.0) + rack-test (0.5.4) + rack (>= 1.0) + rails (3.0.0.rc) + actionmailer (= 3.0.0.rc) + actionpack (= 3.0.0.rc) + activerecord (= 3.0.0.rc) + activeresource (= 3.0.0.rc) + activesupport (= 3.0.0.rc) + bundler (>= 1.0.0.rc.1) + railties (= 3.0.0.rc) + railties (3.0.0.rc) + actionpack (= 3.0.0.rc) + activesupport (= 3.0.0.rc) + rake (>= 0.8.3) + thor (~> 0.14.0) + rake (0.8.7) + rspec (2.0.0.beta.19) + rspec-core (= 2.0.0.beta.19) + rspec-expectations (= 2.0.0.beta.19) + rspec-mocks (= 2.0.0.beta.19) + rspec-core (2.0.0.beta.19) + rspec-expectations (2.0.0.beta.19) + diff-lcs (>= 1.1.2) + rspec-mocks (2.0.0.beta.19) + rspec-rails (2.0.0.beta.19) + rspec (= 2.0.0.beta.19) + webrat (>= 0.7.2.beta.1) + 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) + thor (0.14.0) + timecop (0.3.5) + treetop (1.4.8) + polyglot (>= 0.3.1) + tzinfo (0.3.22) + webrat (0.7.2.beta.1) + nokogiri (>= 1.2.0) + rack (>= 1.0) + rack-test (>= 0.5.3) + +PLATFORMS + ruby + +DEPENDENCIES + ZenTest + rails (= 3.0.0.rc) + rspec (>= 2.0.0.beta.17) + rspec-rails (>= 2.0.0.beta.17) + ruby-debug + sqlite3-ruby + timecop diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..290fec0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2010 Adam Meehan + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..4a17287 --- /dev/null +++ b/Rakefile @@ -0,0 +1,44 @@ +require 'rubygems' +require 'rake/rdoctask' +require 'rake/gempackagetask' +require 'rubygems/specification' +require 'rspec/core/rake_task' +require 'lib/validates_timeliness/version' + +GEM_NAME = "active_enum" +GEM_VERSION = ValidatesTimeliness::VERSION + +desc 'Default: run specs.' +task :default => :spec + +desc "Run specs" +RSpec::Core::RakeTask.new do |t| + t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default. +end + +desc "Generate code coverage" +RSpec::Core::RakeTask.new(:coverage) do |t| + t.rcov = true + t.rcov_opts = ['--exclude', 'spec'] +end + +desc 'Generate documentation for plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'ActiveEnum' + rdoc.options << '--line-numbers' << '--inline-source' + rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +desc "install the gem locally" +task :install => [:package] do + sh %{gem install pkg/#{GEM_NAME}-#{GEM_VERSION}} +end + +desc "create a gemspec file" +task :make_spec do + File.open("#{GEM_NAME}.gemspec", "w") do |file| + file.puts spec.to_ruby + end +end diff --git a/autotest/discover.rb b/autotest/discover.rb new file mode 100644 index 0000000..cd6892c --- /dev/null +++ b/autotest/discover.rb @@ -0,0 +1 @@ +Autotest.add_discovery { "rspec2" } diff --git a/lib/validates_timeliness.rb b/lib/validates_timeliness.rb new file mode 100644 index 0000000..c09d6c5 --- /dev/null +++ b/lib/validates_timeliness.rb @@ -0,0 +1,26 @@ +require 'date' +require 'active_support/time_with_zone' +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/date/acts_like' +require 'active_support/core_ext/date/conversions' +require 'active_support/core_ext/date/zones' +require 'active_support/core_ext/time/acts_like' +require 'active_support/core_ext/time/conversions' +require 'active_support/core_ext/time/zones' +require 'active_support/core_ext/date_time/acts_like' +require 'active_support/core_ext/date_time/conversions' +require 'active_support/core_ext/date_time/zones' + +module ValidatesTimeliness + + # Set the dummy date part for a time type values. + mattr_accessor :dummy_date_for_time_type + @@dummy_date_for_time_type = [ 2000, 1, 1 ] + +end + +require 'validates_timeliness/conversion' +require 'validates_timeliness/validator' +require 'validates_timeliness/helper_methods' +require 'validates_timeliness/version' diff --git a/lib/validates_timeliness/conversion.rb b/lib/validates_timeliness/conversion.rb new file mode 100644 index 0000000..3e53bca --- /dev/null +++ b/lib/validates_timeliness/conversion.rb @@ -0,0 +1,27 @@ +module ValidatesTimeliness + module Conversion + + def type_cast_value(value, type) + value = case type + when :time + dummy_time(value) + when :date + value.to_date + when :datetime + value.to_time.in_time_zone + end + end + + def dummy_time(value) + time = if value.acts_like?(:time) + value = value.in_time_zone + [value.hour, value.min, value.sec] + else + [0,0,0] + end + dummy_date = ValidatesTimeliness.dummy_date_for_time_type + Time.local(*(dummy_date + time)) + end + + end +end diff --git a/lib/validates_timeliness/helper_methods.rb b/lib/validates_timeliness/helper_methods.rb new file mode 100644 index 0000000..b1d497a --- /dev/null +++ b/lib/validates_timeliness/helper_methods.rb @@ -0,0 +1,19 @@ +module ValidatesTimeliness + module HelperMethods + def validates_date(*attr_names) + validates_with Validator, _merge_attributes(attr_names).merge(:type => :date) + end + + def validates_time(*attr_names) + validates_with Validator, _merge_attributes(attr_names).merge(:type => :time) + end + + def validates_datetime(*attr_names) + validates_with Validator, _merge_attributes(attr_names).merge(:type => :datetime) + end + end +end + +module ActiveModel::Validations::HelperMethods + include ValidatesTimeliness::HelperMethods +end diff --git a/lib/validates_timeliness/locale/en.yml b/lib/validates_timeliness/locale/en.yml new file mode 100644 index 0000000..634c9da --- /dev/null +++ b/lib/validates_timeliness/locale/en.yml @@ -0,0 +1,16 @@ +en: + errors: + messages: + invalid_date: "is not a valid date" + invalid_time: "is not a valid time" + invalid_datetime: "is not a valid datetime" + is_at: "must be at %{restriction}" + before: "must be before %{restriction}" + on_or_before: "must be on or before %{restriction}" + after: "must be after %{restriction}" + on_or_after: "must be on or after %{restriction}" + validates_timeliness: + error_value_formats: + date: '%Y-%m-%d' + time: '%H:%M:%S' + datetime: '%Y-%m-%d %H:%M:%S' diff --git a/lib/validates_timeliness/validator.rb b/lib/validates_timeliness/validator.rb new file mode 100644 index 0000000..5016c7e --- /dev/null +++ b/lib/validates_timeliness/validator.rb @@ -0,0 +1,61 @@ +require 'active_model/validator' + +module ValidatesTimeliness + class Validator < ActiveModel::EachValidator + include Conversion + + CHECKS = { + :is_at => :==, + :before => :<, + :after => :>, + :on_or_before => :<=, + :on_or_after => :>=, + }.freeze + + def self.kind + :timeliness + end + + def initialize(options) + @allow_nil, @allow_blank = options.delete(:allow_nil), options.delete(:allow_blank) + @type = options.delete(:type) + + if range = options.delete(:between) + raise ArgumentError, ":between must be a Range or an Array" unless range.is_a?(Range) || range.is_a?(Array) + options[:on_or_after], options[:on_or_before] = range.begin, range.end + end + super + end + + def check_validity! + end + + def validate_each(record, attr_name, value) + raw_value = attribute_raw_value(record, attr_name) || value + return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?) + + return record.errors.add(attr_name, :"invalid_#{@type}") if value.blank? + + value = type_cast(value) + + (CHECKS.keys & options.keys).each do |check| + check_value = type_cast(options[check]) + unless value.send(CHECKS[check], check_value) + return record.errors.add(attr_name, check, :restriction => check_value) + end + end + end + + def attribute_raw_value(record, attr_name) + before_type_cast = "#{attr_name}_before_type_cast" + record.send("#{attr_name}_before_type_cast") if record.respond_to?(before_type_cast) + end + + def type_cast(value) + type_cast_value(value, @type) + end + end +end + +# Compatibility with ActiveModel validates method which tries match option keys to their validator class +TimelinessValidator = ValidatesTimeliness::Validator diff --git a/lib/validates_timeliness/version.rb b/lib/validates_timeliness/version.rb new file mode 100644 index 0000000..9bfccc8 --- /dev/null +++ b/lib/validates_timeliness/version.rb @@ -0,0 +1,3 @@ +module ValidatesTimeliness + VERSION = '3.0.0' +end diff --git a/spec/model_helpers.rb b/spec/model_helpers.rb new file mode 100644 index 0000000..caaac30 --- /dev/null +++ b/spec/model_helpers.rb @@ -0,0 +1,27 @@ +module ModelHelpers + + # Some test helpers from Rails source + def invalid!(attr_name, values, error = nil) + with_each_person_value(attr_name, values) do |record, value| + record.should be_invalid + record.errors[attr_name].size.should >= 1 + record.errors[attr_name].first.should == error if error + end + end + + def valid!(attr_name, values) + with_each_person_value(attr_name, values) do |record, value| + record.should be_valid + end + end + + def with_each_person_value(attr_name, values) + record = Person.new + values = [values] unless values.is_a?(Array) + values.each do |value| + record.send("#{attr_name}=", value) + yield record, value + end + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..5e67043 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,44 @@ +$:.unshift(File.dirname(__FILE__) + '/../lib') +$:.unshift(File.dirname(__FILE__)) + +require 'rspec' +require 'rspec/autorun' + +require 'active_model' +require 'active_model/validations' +# require 'active_record' +# require 'action_controller' +# require 'action_view' +# require 'action_mailer' +# require 'rspec/rails' + +require 'timecop' +require 'model_helpers' + +require 'validates_timeliness' + +Time.zone = 'Australia/Melbourne' + +LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/../lib/validates_timeliness/locale/en.yml') +I18n.load_path.unshift(LOCALE_PATH) + +class Person + include ActiveModel::Validations + extend ActiveModel::Translation + + attr_accessor :birth_date, :birth_time, :birth_datetime + + def initialize(attributes = {}) + attributes.each do |key, value| + send "#{key}=", value + end + end +end + +Rspec.configure do |c| + c.mock_with :rspec + c.before do + Person.reset_callbacks(:validate) + Person._validators.clear + end +end diff --git a/spec/validates_timeliness/conversion_spec.rb b/spec/validates_timeliness/conversion_spec.rb new file mode 100644 index 0000000..28becd3 --- /dev/null +++ b/spec/validates_timeliness/conversion_spec.rb @@ -0,0 +1,102 @@ +require 'spec_helper' + +describe ValidatesTimeliness::Conversion do + include ValidatesTimeliness::Conversion + + before do + Timecop.freeze(Time.mktime(2010, 1, 1, 0, 0, 0)) + end + + describe "#type_cast_value" do + describe "for date type" do + it "should return same value for date value" do + type_cast_value(Date.new(2010, 1, 1), :date).should == Date.new(2010, 1, 1) + end + + it "should return date part of time value" do + type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0), :date).should == Date.new(2010, 1, 1) + end + + 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) + end + end + + describe "for time type" do + it "should return same value for time value matching dummy date part" do + type_cast_value(Time.mktime(2000, 1, 1, 0, 0, 0), :time).should == Time.mktime(2000, 1, 1, 0, 0, 0) + end + + it "should return dummy time value with same time part for time value with different date" do + type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0), :time).should == Time.mktime(2000, 1, 1, 0, 0, 0) + end + + it "should return dummy time only for date value" do + type_cast_value(Date.new(2010, 1, 1), :time).should == Time.mktime(2000, 1, 1, 0, 0, 0) + end + + it "should return dummy date with shifted local time for UTC datetime value" do + type_cast_value(DateTime.new(2010, 1, 1, 12, 34, 56), :time).should == Time.mktime(2000, 1, 1, 23, 34, 56) + end + + it "should return dummy date with time part for local datetime value" do + type_cast_value(DateTime.civil_from_format(:local, 2010, 1, 1, 12, 34, 56), :time).should == Time.mktime(2000, 1, 1, 12, 34, 56) + end + end + + describe "for datetime type" do + it "should return time in local offset for date value" do + type_cast_value(Date.new(2010, 1, 1), :datetime).should == Time.local_time(2010, 1, 1, 0, 0, 0) + end + + it "should return same value for same time value in local offset" do + type_cast_value(Time.local_time(2010, 1, 1, 12, 34, 56), :datetime).should == Time.local_time(2010, 1, 1, 12, 34, 56) + end + + it "should return shifted local time value for UTC time value" do + type_cast_value(Time.utc(2010, 1, 1, 12, 34, 56), :datetime).should == Time.local_time(2010, 1, 1, 23, 34, 56) + end + + it "should return shifted local time value for UTC datetime value" do + type_cast_value(DateTime.new(2010, 1, 1, 12, 34, 56), :datetime).should == Time.local_time(2010, 1, 1, 23, 34, 56) + end + + it "should return time value with same component values for local datetime value" do + type_cast_value(DateTime.civil_from_format(:local, 2010, 1, 1, 12, 34, 56), :datetime).should == Time.local_time(2010, 1, 1, 12, 34, 56) + end + end + end + + describe "#dummy_time" do + it 'should return dummy date with shifted local time part for UTC time value' do + dummy_time(Time.utc(2010, 11, 22, 12, 34, 56)).should == Time.local_time(2000, 1, 1, 23, 34, 56) + end + + it 'should return dummy date with same time part part for local time value with non-dummy date' do + dummy_time(Time.local_time(2010, 11, 22, 12, 34, 56)).should == Time.local_time(2000, 1, 1, 12, 34, 56) + end + + it 'should return same value for local time with dummy date' do + dummy_time(Time.local_time(2000, 1, 1, 12, 34, 56)).should == Time.local_time(2000, 1, 1, 12, 34, 56) + end + + it 'should return exact dummy time value for date value' do + dummy_time(Date.new(2010, 11, 22)).should == Time.mktime(2000, 1, 1, 0, 0, 0) + end + + describe "with custom dummy date" do + before(:all) do + @@original_dummy_date = ValidatesTimeliness.dummy_date_for_time_type + ValidatesTimeliness.dummy_date_for_time_type = [2010, 1, 1] + end + + it 'should return dummy time with custom dummy date' do + dummy_time(Time.local_time(1999, 11, 22, 12, 34, 56)).should == Time.local_time(2010, 1, 1, 12, 34, 56) + end + + after(:all) do + ValidatesTimeliness.dummy_date_for_time_type = @@original_dummy_date + end + end + end +end diff --git a/spec/validates_timeliness/validator/is_at_spec.rb b/spec/validates_timeliness/validator/is_at_spec.rb new file mode 100644 index 0000000..ae2065e --- /dev/null +++ b/spec/validates_timeliness/validator/is_at_spec.rb @@ -0,0 +1,85 @@ +require 'spec_helper' + +describe ValidatesTimeliness::Validator, ":is_at option" do + include ModelHelpers + + before do + Timecop.freeze(Time.local_time(2010, 1, 1, 0, 0, 0)) + end + + describe "for date type" do + it "should be equal to same date value" do + Person.validates_date :birth_date, :is_at => Date.new(2010, 1, 1) + valid!(:birth_date, Date.new(2010, 1, 1)) + end + + it "should be equal to Time with same date part" do + Person.validates_date :birth_date, :is_at => Time.local_time(2010, 1, 1, 0, 0, 0) + valid!(:birth_date, Date.new(2010, 1, 1)) + end + + it "should be equal to DateTime with same date part" do + Person.validates_date :birth_date, :is_at => DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0) + valid!(:birth_date, Date.new(2010, 1, 1)) + end + end + + describe "for time type" do + it "should be equal to Date if attribute value is midnight" do + Person.validates_time :birth_time, :is_at => Date.new(2010, 1, 1) + valid!(:birth_time, Time.local_time(2000, 1, 1, 0, 0, 0)) + end + + it "should not be be equal to Date if attribute value is other than midnight" do + Person.validates_time :birth_time, :is_at => Date.new(2010, 1, 1) + invalid!(:birth_time, Time.local_time(2000, 1, 1, 9, 30, 0)) + end + + it "should be equal to local Time with same time part" do + Person.validates_time :birth_time, :is_at => Time.local_time(2010, 1, 1, 0, 0, 0) + valid!(:birth_time, Time.local_time(2000, 1, 1, 0, 0, 0)) + end + + it "should not be equal to UTC Time with same time part" do + Person.validates_time :birth_time, :is_at => Time.utc(2010, 1, 1, 0, 0, 0) + invalid!(:birth_time, Time.local_time(2000, 1, 1, 0, 0, 0)) + end + + it "should be equal to local DateTime with same time part" do + Person.validates_time :birth_time, :is_at => DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0) + valid!(:birth_time, Time.local_time(2000, 1, 1, 0, 0, 0)) + end + + it "should not be equal to UTC DateTime with same time part" do + Person.validates_time :birth_time, :is_at => DateTime.new(2010, 1, 1, 0, 0, 0) + invalid!(:birth_time, Time.local_time(2000, 1, 1, 0, 0, 0)) + end + end + + describe "for datetime type" do + it "should be equal to Date with same" do + Person.validates_datetime :birth_datetime, :is_at => Date.new(2010, 1, 1) + valid!(:birth_datetime, DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0)) + end + + it "should be equal to local Time with same component values" do + Person.validates_datetime :birth_datetime, :is_at => Time.local_time(2010, 1, 1, 0, 0, 0) + valid!(:birth_datetime, DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0)) + end + + it "should not be equal to UTC Time with same component values" do + Person.validates_datetime :birth_datetime, :is_at => Time.utc(2010, 1, 1, 0, 0, 0) + invalid!(:birth_datetime, DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0)) + end + + it "should be equal to same local DateTime value" do + Person.validates_datetime :birth_datetime, :is_at => DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0) + valid!(:birth_datetime, DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0)) + end + + it "should not be equal to UTC DateTime with same component values" do + Person.validates_datetime :birth_datetime, :is_at => DateTime.new(2010, 1, 1, 0, 0, 0) + invalid!(:birth_datetime, DateTime.civil_from_format(:local, 2010, 1, 1, 0, 0, 0)) + end + end +end diff --git a/spec/validates_timeliness/validator_spec.rb b/spec/validates_timeliness/validator_spec.rb new file mode 100644 index 0000000..e1ccba5 --- /dev/null +++ b/spec/validates_timeliness/validator_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe ValidatesTimeliness::Validator do + include ModelHelpers + NIL = [nil] + + before do + Timecop.freeze(Time.local_time(2010, 1, 1, 0, 0, 0)) + end + + it 'should return validator kind as :timeliness' do + ValidatesTimeliness::Validator.kind.should == :timeliness + end + + describe "Model.validates :timeliness option" do + it 'should use plugin validator class' do + Person.validates :birth_date, :timeliness => {:is_at => Date.new(2010,1,1), :type => :date} + Person.validators.should have(1).kind_of(TimelinessValidator) + valid!(:birth_date, Date.new(2010,1,1)) + invalid!(:birth_date, Date.new(2010,1,2)) + end + end + + describe ":allow_nil option" do + it 'should not allow nil by default' do + Person.validates_datetime :birth_date + invalid!(:birth_date, NIL) + valid!(:birth_date, Date.today) + end + + it 'should allow nil when true' do + Person.validates_datetime :birth_date, :allow_nil => true + valid!(:birth_date, NIL) + end + end + + describe ":allow_blank option" do + it 'should not allow blank by default' do + Person.validates_datetime :birth_date + invalid!(:birth_date, '') + valid!(:birth_date, Date.today) + end + + it 'should allow blank when true' do + Person.validates_datetime :birth_date, :allow_blank => true + valid!(:birth_date, '') + end + end +end