mirror of
https://github.com/ditkrg/validates_timeliness.git
synced 2026-01-25 07:16:41 +00:00
refactored AR parsing methods into Parser module to reduce AR method pollution and make more consistent
This commit is contained in:
parent
88fce1d679
commit
312c1510cb
@ -1,4 +1,5 @@
|
|||||||
require 'validates_timeliness/formats'
|
require 'validates_timeliness/formats'
|
||||||
|
require 'validates_timeliness/parser'
|
||||||
require 'validates_timeliness/validator'
|
require 'validates_timeliness/validator'
|
||||||
require 'validates_timeliness/validation_methods'
|
require 'validates_timeliness/validation_methods'
|
||||||
require 'validates_timeliness/spec/rails/matchers/validate_timeliness' if ENV['RAILS_ENV'] == 'test'
|
require 'validates_timeliness/spec/rails/matchers/validate_timeliness' if ENV['RAILS_ENV'] == 'test'
|
||||||
@ -14,9 +15,11 @@ require 'validates_timeliness/core_ext/date_time'
|
|||||||
module ValidatesTimeliness
|
module ValidatesTimeliness
|
||||||
|
|
||||||
mattr_accessor :default_timezone
|
mattr_accessor :default_timezone
|
||||||
|
|
||||||
self.default_timezone = :utc
|
self.default_timezone = :utc
|
||||||
|
|
||||||
|
mattr_accessor :use_time_zones
|
||||||
|
self.use_time_zones = false
|
||||||
|
|
||||||
LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/validates_timeliness/locale/en.yml')
|
LOCALE_PATH = File.expand_path(File.dirname(__FILE__) + '/validates_timeliness/locale/en.yml')
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
@ -46,10 +49,10 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def setup_for_rails
|
def setup_for_rails
|
||||||
major, minor = Rails::VERSION::MAJOR, Rails::VERSION::MINOR
|
|
||||||
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
self.default_timezone = ::ActiveRecord::Base.default_timezone
|
||||||
self.enable_datetime_select_extension!
|
self.use_time_zones = ::ActiveRecord::Base.time_zone_aware_attributes rescue false
|
||||||
self.load_error_messages
|
enable_datetime_select_extension!
|
||||||
|
load_error_messages
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -46,7 +46,7 @@ module ValidatesTimeliness
|
|||||||
# implementation as it chains the write_attribute method which deletes
|
# implementation as it chains the write_attribute method which deletes
|
||||||
# the attribute from the cache.
|
# the attribute from the cache.
|
||||||
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
def write_date_time_attribute(attr_name, value, type, time_zone_aware)
|
||||||
new = self.class.parse_date_time(value, type)
|
new = ValidatesTimeliness::Parser.parse(value, type)
|
||||||
|
|
||||||
if new && type != :date
|
if new && type != :date
|
||||||
new = new.to_time
|
new = new.to_time
|
||||||
@ -73,7 +73,7 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
if @attributes_cache.has_key?(attr_name)
|
if @attributes_cache.has_key?(attr_name)
|
||||||
time = read_attribute_before_type_cast(attr_name)
|
time = read_attribute_before_type_cast(attr_name)
|
||||||
time = self.class.parse_date_time(time, type)
|
time = ValidatesTimeliness::Parser.parse(time, type)
|
||||||
else
|
else
|
||||||
time = read_attribute(attr_name)
|
time = read_attribute(attr_name)
|
||||||
@attributes[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
@attributes[attr_name] = time && time_zone_aware ? time.in_time_zone : time
|
||||||
@ -83,8 +83,6 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
# Define attribute reader and writer method for date, time and
|
|
||||||
# datetime attributes to use plugin parser.
|
|
||||||
def define_attribute_methods_with_timeliness
|
def define_attribute_methods_with_timeliness
|
||||||
return if generated_methods?
|
return if generated_methods?
|
||||||
columns_hash.each do |name, column|
|
columns_hash.each do |name, column|
|
||||||
@ -105,7 +103,6 @@ module ValidatesTimeliness
|
|||||||
define_attribute_methods_without_timeliness
|
define_attribute_methods_without_timeliness
|
||||||
end
|
end
|
||||||
|
|
||||||
# Define write method for date, time and datetime columns
|
|
||||||
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
def define_write_method_for_dates_and_times(attr_name, type, time_zone_aware)
|
||||||
method_body = <<-EOV
|
method_body = <<-EOV
|
||||||
def #{attr_name}=(value)
|
def #{attr_name}=(value)
|
||||||
|
|||||||
@ -38,7 +38,7 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def time_array_to_string(values, type)
|
def time_array_to_string(values, type)
|
||||||
values = values.map {|v| v.to_s }
|
values.collect! {|v| v.to_s }
|
||||||
|
|
||||||
case type
|
case type
|
||||||
when :date
|
when :date
|
||||||
|
|||||||
45
lib/validates_timeliness/parser.rb
Normal file
45
lib/validates_timeliness/parser.rb
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
module ValidatesTimeliness
|
||||||
|
module Parser
|
||||||
|
|
||||||
|
class << self
|
||||||
|
|
||||||
|
def parse(raw_value, type, strict=true)
|
||||||
|
return nil if raw_value.blank?
|
||||||
|
return raw_value if raw_value.acts_like?(:time) || raw_value.is_a?(Date)
|
||||||
|
|
||||||
|
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, strict)
|
||||||
|
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
|
||||||
|
|
||||||
|
# Create time object which checks time part, and return time object
|
||||||
|
make_time(time_array)
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_time(time_array)
|
||||||
|
if Time.respond_to?(:zone) && ValidatesTimeliness.use_time_zones
|
||||||
|
Time.zone.local(*time_array)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
time_zone = ValidatesTimeliness.default_timezone
|
||||||
|
Time.send(time_zone, *time_array)
|
||||||
|
rescue ArgumentError, TypeError
|
||||||
|
zone_offset = time_zone == :local ? DateTime.local_offset : 0
|
||||||
|
time_array.pop # remove microseconds
|
||||||
|
DateTime.civil(*(time_array << zone_offset))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -7,27 +7,6 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
|
|
||||||
def parse_date_time(raw_value, type, strict=true)
|
|
||||||
return nil if raw_value.blank?
|
|
||||||
return raw_value if raw_value.acts_like?(:time) || raw_value.is_a?(Date)
|
|
||||||
|
|
||||||
time_array = ValidatesTimeliness::Formats.parse(raw_value, type, strict)
|
|
||||||
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
|
|
||||||
|
|
||||||
# Create time object which checks time part, and return time object
|
|
||||||
make_time(time_array)
|
|
||||||
rescue
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def validates_time(*attr_names)
|
def validates_time(*attr_names)
|
||||||
configuration = attr_names.extract_options!
|
configuration = attr_names.extract_options!
|
||||||
configuration[:type] = :time
|
configuration[:type] = :time
|
||||||
@ -59,21 +38,6 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Time.zone. Rails 2.0 should be default_timezone.
|
|
||||||
def make_time(time_array)
|
|
||||||
if Time.respond_to?(:zone) && time_zone_aware_attributes
|
|
||||||
Time.zone.local(*time_array)
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
Time.send(::ActiveRecord::Base.default_timezone, *time_array)
|
|
||||||
rescue ArgumentError, TypeError
|
|
||||||
zone_offset = ::ActiveRecord::Base.default_timezone == :local ? DateTime.local_offset : 0
|
|
||||||
time_array.pop # remove microseconds
|
|
||||||
DateTime.civil(*(time_array << zone_offset))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -36,7 +36,7 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
|
|
||||||
def call(record, attr_name, value)
|
def call(record, attr_name, value)
|
||||||
value = record.class.parse_date_time(value, type, false) if value.is_a?(String)
|
value = ValidatesTimeliness::Parser.parse(value, type, false) if value.is_a?(String)
|
||||||
raw_value = raw_value(record, attr_name) || value
|
raw_value = raw_value(record, attr_name) || value
|
||||||
|
|
||||||
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank])
|
||||||
@ -143,7 +143,7 @@ module ValidatesTimeliness
|
|||||||
end
|
end
|
||||||
date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record)
|
date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record)
|
||||||
return if date.nil? || time.nil?
|
return if date.nil? || time.nil?
|
||||||
record.class.send(:make_time, [date.year, date.month, date.day, time.hour, time.min, time.sec, time.usec])
|
ValidatesTimeliness::Parser.make_time([date.year, date.month, date.day, time.hour, time.min, time.sec, time.usec])
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_options(options)
|
def validate_options(options)
|
||||||
@ -158,7 +158,7 @@ module ValidatesTimeliness
|
|||||||
|
|
||||||
def evaluate_option_value(value, type, record)
|
def evaluate_option_value(value, type, record)
|
||||||
case value
|
case value
|
||||||
when Time, Date, DateTime
|
when Time, Date
|
||||||
value
|
value
|
||||||
when Symbol
|
when Symbol
|
||||||
evaluate_option_value(record.send(value), type, record)
|
evaluate_option_value(record.send(value), type, record)
|
||||||
@ -169,7 +169,7 @@ module ValidatesTimeliness
|
|||||||
when Range
|
when Range
|
||||||
evaluate_option_value([value.first, value.last], type, record)
|
evaluate_option_value([value.first, value.last], type, record)
|
||||||
else
|
else
|
||||||
record.class.parse_date_time(value, type, false)
|
ValidatesTimeliness::Parser.parse(value, type, false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -192,7 +192,7 @@ module ValidatesTimeliness
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
if ignore_usec && value.is_a?(Time)
|
if ignore_usec && value.is_a?(Time)
|
||||||
::ActiveRecord::Base.send(:make_time, Array(value).reverse[4..9])
|
ValidatesTimeliness::Parser.make_time(Array(value).reverse[4..9])
|
||||||
else
|
else
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
|||||||
@ -39,17 +39,17 @@ describe ValidatesTimeliness::ActiveRecord::AttributeMethods do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "should call parser on write for datetime attribute" do
|
it "should call parser on write for datetime attribute" do
|
||||||
@person.class.should_receive(:parse_date_time).once
|
ValidatesTimeliness::Parser.should_receive(:parse).once
|
||||||
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
@person.birth_date_and_time = "2000-01-01 02:03:04"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should call parser on write for date attribute" do
|
it "should call parser on write for date attribute" do
|
||||||
@person.class.should_receive(:parse_date_time).once
|
ValidatesTimeliness::Parser.should_receive(:parse).once
|
||||||
@person.birth_date = "2000-01-01"
|
@person.birth_date = "2000-01-01"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should call parser on write for time attribute" do
|
it "should call parser on write for time attribute" do
|
||||||
@person.class.should_receive(:parse_date_time).once
|
ValidatesTimeliness::Parser.should_receive(:parse).once
|
||||||
@person.birth_time = "12:00"
|
@person.birth_time = "12:00"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
61
spec/parser_spec.rb
Normal file
61
spec/parser_spec.rb
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
||||||
|
|
||||||
|
describe ValidatesTimeliness::Parser do
|
||||||
|
attr_accessor :person
|
||||||
|
|
||||||
|
describe "parse" do
|
||||||
|
it "should return time object for valid time string" do
|
||||||
|
parse("2000-01-01 12:13:14", :datetime).should be_kind_of(Time)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return nil for time string with invalid date part" do
|
||||||
|
parse("2000-02-30 12:13:14", :datetime).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return nil for time string with invalid time part" do
|
||||||
|
parse("2000-02-01 25:13:14", :datetime).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return Time object when passed a Time object" do
|
||||||
|
parse(Time.now, :datetime).should be_kind_of(Time)
|
||||||
|
end
|
||||||
|
|
||||||
|
if RAILS_VER >= '2.1'
|
||||||
|
it "should convert time string into current timezone" do
|
||||||
|
Time.zone = 'Melbourne'
|
||||||
|
time = parse("2000-01-01 12:13:14", :datetime)
|
||||||
|
Time.zone.utc_offset.should == 10.hours
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return nil for invalid date string" do
|
||||||
|
parse("2000-02-30", :date).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(*args)
|
||||||
|
ValidatesTimeliness::Parser.parse(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "make_time" do
|
||||||
|
|
||||||
|
if RAILS_VER >= '2.1'
|
||||||
|
|
||||||
|
it "should create time using current timezone" do
|
||||||
|
Time.zone = 'Melbourne'
|
||||||
|
time = ValidatesTimeliness::Parser.make_time([2000,1,1,12,0,0])
|
||||||
|
time.zone.should == "EST"
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
it "should create time using default timezone" do
|
||||||
|
time = ValidatesTimeliness::Parser.make_time([2000,1,1,12,0,0])
|
||||||
|
time.zone.should == "UTC"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@ -30,6 +30,7 @@ require 'active_record'
|
|||||||
require 'active_record/version'
|
require 'active_record/version'
|
||||||
require 'action_controller'
|
require 'action_controller'
|
||||||
require 'action_view'
|
require 'action_view'
|
||||||
|
require 'action_mailer'
|
||||||
|
|
||||||
require 'spec/rails'
|
require 'spec/rails'
|
||||||
require 'time_travel/time_travel'
|
require 'time_travel/time_travel'
|
||||||
@ -38,13 +39,13 @@ ActiveRecord::Base.default_timezone = :utc
|
|||||||
RAILS_VER = Rails::VERSION::STRING
|
RAILS_VER = Rails::VERSION::STRING
|
||||||
puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
|
puts "Using #{vendored ? 'vendored' : 'gem'} Rails version #{RAILS_VER} (ActiveRecord version #{ActiveRecord::VERSION::STRING})"
|
||||||
|
|
||||||
require 'validates_timeliness'
|
|
||||||
|
|
||||||
if RAILS_VER >= '2.1'
|
if RAILS_VER >= '2.1'
|
||||||
Time.zone_default = ActiveSupport::TimeZone['UTC']
|
Time.zone_default = ActiveSupport::TimeZone['UTC']
|
||||||
ActiveRecord::Base.time_zone_aware_attributes = true
|
ActiveRecord::Base.time_zone_aware_attributes = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'validates_timeliness'
|
||||||
|
|
||||||
ActiveRecord::Migration.verbose = false
|
ActiveRecord::Migration.verbose = false
|
||||||
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
||||||
|
|
||||||
|
|||||||
@ -66,7 +66,7 @@ describe ValidatesTimeliness::Validator do
|
|||||||
|
|
||||||
it "should return array of Time objects when restriction is array of strings" do
|
it "should return array of Time objects when restriction is array of strings" do
|
||||||
time1, time2 = "2000-01-02", "2000-01-01"
|
time1, time2 = "2000-01-02", "2000-01-01"
|
||||||
evaluate_option_value([time1, time2], :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
evaluate_option_value([time1, time2], :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should return array of Time objects when restriction is Range of Time objects" do
|
it "should return array of Time objects when restriction is Range of Time objects" do
|
||||||
@ -76,7 +76,7 @@ describe ValidatesTimeliness::Validator do
|
|||||||
|
|
||||||
it "should return array of Time objects when restriction is Range of time strings" do
|
it "should return array of Time objects when restriction is Range of time strings" do
|
||||||
time1, time2 = "2000-01-02", "2000-01-01"
|
time1, time2 = "2000-01-02", "2000-01-01"
|
||||||
evaluate_option_value(time1..time2, :datetime).should == [Person.parse_date_time(time2, :datetime), Person.parse_date_time(time1, :datetime)]
|
evaluate_option_value(time1..time2, :datetime).should == [parse(time2, :datetime), parse(time1, :datetime)]
|
||||||
end
|
end
|
||||||
def evaluate_option_value(restriction, type)
|
def evaluate_option_value(restriction, type)
|
||||||
configure_validator(:type => type)
|
configure_validator(:type => type)
|
||||||
@ -587,6 +587,10 @@ describe ValidatesTimeliness::Validator do
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse(*args)
|
||||||
|
ValidatesTimeliness::Parser.parse(*args)
|
||||||
|
end
|
||||||
|
|
||||||
def configure_validator(options={})
|
def configure_validator(options={})
|
||||||
@validator = ValidatesTimeliness::Validator.new(options)
|
@validator = ValidatesTimeliness::Validator.new(options)
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user