mirror of
https://github.com/ditkrg/validates_timeliness.git
synced 2026-01-22 22:06:45 +00:00
Move conversion module methods in Converter class
Encapsulate conversion helper methods
This commit is contained in:
parent
e5bb096161
commit
093e33fbed
@ -62,7 +62,7 @@ module ValidatesTimeliness
|
||||
def self.parser; Timeliness end
|
||||
end
|
||||
|
||||
require 'validates_timeliness/conversion'
|
||||
require 'validates_timeliness/converter'
|
||||
require 'validates_timeliness/validator'
|
||||
require 'validates_timeliness/helper_methods'
|
||||
require 'validates_timeliness/attribute_methods'
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
module ValidatesTimeliness
|
||||
module Conversion
|
||||
class Converter
|
||||
attr_reader :type, :format, :ignore_usec
|
||||
|
||||
def type_cast_value(value, type)
|
||||
def initialize(type:, format: nil, ignore_usec: false, time_zone_aware: false)
|
||||
@type = type
|
||||
@format = format
|
||||
@ignore_usec = ignore_usec
|
||||
@time_zone_aware = time_zone_aware
|
||||
end
|
||||
|
||||
def type_cast_value(value)
|
||||
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) && time_zone_aware?
|
||||
value = case type
|
||||
when :time
|
||||
dummy_time(value)
|
||||
@ -15,8 +23,8 @@ module ValidatesTimeliness
|
||||
else
|
||||
value
|
||||
end
|
||||
if options[:ignore_usec] && value.is_a?(Time)
|
||||
Timeliness::Parser.make_time(Array(value).reverse[4..9], (:current if @timezone_aware))
|
||||
if ignore_usec && value.is_a?(Time)
|
||||
Timeliness::Parser.make_time(Array(value).reverse[4..9], (:current if time_zone_aware?))
|
||||
else
|
||||
value
|
||||
end
|
||||
@ -24,30 +32,30 @@ module ValidatesTimeliness
|
||||
|
||||
def dummy_time(value)
|
||||
time = if value.acts_like?(:time)
|
||||
value = value.in_time_zone if @timezone_aware
|
||||
value = value.in_time_zone if time_zone_aware?
|
||||
[value.hour, value.min, value.sec]
|
||||
else
|
||||
[0,0,0]
|
||||
end
|
||||
values = ValidatesTimeliness.dummy_date_for_time_type + time
|
||||
Timeliness::Parser.make_time(values, (:current if @timezone_aware))
|
||||
Timeliness::Parser.make_time(values, (:current if time_zone_aware?))
|
||||
end
|
||||
|
||||
def evaluate_option_value(value, record)
|
||||
def evaluate(value, scope=nil)
|
||||
case value
|
||||
when Time, Date
|
||||
value
|
||||
when String
|
||||
parse(value)
|
||||
when Symbol
|
||||
if !record.respond_to?(value) && restriction_shorthand?(value)
|
||||
if !scope.respond_to?(value) && restriction_shorthand?(value)
|
||||
ValidatesTimeliness.restriction_shorthand_symbols[value].call
|
||||
else
|
||||
evaluate_option_value(record.send(value), record)
|
||||
evaluate(scope.send(value))
|
||||
end
|
||||
when Proc
|
||||
result = value.arity > 0 ? value.call(record) : value.call
|
||||
evaluate_option_value(result, record)
|
||||
result = value.arity > 0 ? value.call(scope) : value.call
|
||||
evaluate(result, scope)
|
||||
else
|
||||
value
|
||||
end
|
||||
@ -59,14 +67,18 @@ module ValidatesTimeliness
|
||||
|
||||
def parse(value)
|
||||
return nil if value.nil?
|
||||
|
||||
if ValidatesTimeliness.use_plugin_parser
|
||||
Timeliness::Parser.parse(value, @type, :zone => (:current if @timezone_aware), :format => options[:format], :strict => false)
|
||||
Timeliness::Parser.parse(value, type, zone: (:current if time_zone_aware?), format: format, strict: false)
|
||||
else
|
||||
@timezone_aware ? Time.zone.parse(value) : value.to_time(ValidatesTimeliness.default_timezone)
|
||||
time_zone_aware? ? Time.zone.parse(value) : value.to_time(ValidatesTimeliness.default_timezone)
|
||||
end
|
||||
rescue ArgumentError, TypeError
|
||||
nil
|
||||
end
|
||||
|
||||
def time_zone_aware?
|
||||
@time_zone_aware
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3,9 +3,7 @@ require 'active_model/validator'
|
||||
|
||||
module ValidatesTimeliness
|
||||
class Validator < ActiveModel::EachValidator
|
||||
include Conversion
|
||||
|
||||
attr_reader :type, :attributes
|
||||
attr_reader :type, :attributes, :converter
|
||||
|
||||
RESTRICTIONS = {
|
||||
:is_at => :==,
|
||||
@ -59,9 +57,10 @@ module ValidatesTimeliness
|
||||
raw_value = attribute_raw_value(record, attr_name) || value
|
||||
return if (@allow_nil && raw_value.nil?) || (@allow_blank && raw_value.blank?)
|
||||
|
||||
@timezone_aware = timezone_aware?(record, attr_name)
|
||||
value = parse(raw_value) if value.is_a?(String) || options[:format]
|
||||
value = type_cast_value(value, @type)
|
||||
@converter = initialize_converter(record, attr_name)
|
||||
|
||||
value = @converter.parse(raw_value) if value.is_a?(String) || options[:format]
|
||||
value = @converter.type_cast_value(value)
|
||||
|
||||
add_error(record, attr_name, :"invalid_#{@type}") and return if value.blank?
|
||||
|
||||
@ -71,7 +70,7 @@ module ValidatesTimeliness
|
||||
def validate_restrictions(record, attr_name, value)
|
||||
@restrictions_to_check.each do |restriction|
|
||||
begin
|
||||
restriction_value = type_cast_value(evaluate_option_value(options[restriction], record), @type)
|
||||
restriction_value = @converter.type_cast_value(@converter.evaluate(options[restriction], record))
|
||||
unless value.send(RESTRICTIONS[restriction], restriction_value)
|
||||
add_error(record, attr_name, restriction, restriction_value) and break
|
||||
end
|
||||
@ -100,10 +99,20 @@ module ValidatesTimeliness
|
||||
record.read_timeliness_attribute_before_type_cast(attr_name.to_s)
|
||||
end
|
||||
|
||||
def timezone_aware?(record, attr_name)
|
||||
def time_zone_aware?(record, attr_name)
|
||||
record.class.respond_to?(:skip_time_zone_conversion_for_attributes) &&
|
||||
!record.class.skip_time_zone_conversion_for_attributes.include?(attr_name.to_sym)
|
||||
end
|
||||
|
||||
def initialize_converter(record, attr_name)
|
||||
ValidatesTimeliness::Converter.new(
|
||||
type: @type,
|
||||
time_zone_aware: time_zone_aware?(record, attr_name),
|
||||
format: options[:format],
|
||||
ignore_usec: options[:ignore_usec]
|
||||
)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -1,97 +1,112 @@
|
||||
RSpec.describe ValidatesTimeliness::Conversion do
|
||||
include ValidatesTimeliness::Conversion
|
||||
RSpec.describe ValidatesTimeliness::Converter do
|
||||
subject(:converter) { described_class.new(type: type, time_zone_aware: time_zone_aware, ignore_usec: ignore_usec) }
|
||||
|
||||
let(:options) { Hash.new }
|
||||
let(:type) { :date }
|
||||
let(:time_zone_aware) { false }
|
||||
let(:ignore_usec) { false }
|
||||
|
||||
before do
|
||||
Timecop.freeze(Time.mktime(2010, 1, 1, 0, 0, 0))
|
||||
end
|
||||
|
||||
delegate :type_cast_value, :evaluate, :parse, :dummy_time, to: :converter
|
||||
|
||||
describe "#type_cast_value" do
|
||||
describe "for date type" do
|
||||
let(:type) { :date }
|
||||
|
||||
it "should return same value for date value" do
|
||||
expect(type_cast_value(Date.new(2010, 1, 1), :date)).to eq(Date.new(2010, 1, 1))
|
||||
expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Date.new(2010, 1, 1))
|
||||
end
|
||||
|
||||
it "should return date part of time value" do
|
||||
expect(type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0), :date)).to eq(Date.new(2010, 1, 1))
|
||||
expect(type_cast_value(Time.mktime(2010, 1, 1, 0, 0, 0))).to eq(Date.new(2010, 1, 1))
|
||||
end
|
||||
|
||||
it "should return date part of datetime value" do
|
||||
expect(type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0), :date)).to eq(Date.new(2010, 1, 1))
|
||||
expect(type_cast_value(DateTime.new(2010, 1, 1, 0, 0, 0))).to eq(Date.new(2010, 1, 1))
|
||||
end
|
||||
|
||||
it 'should return nil for invalid value types' do
|
||||
expect(type_cast_value(12, :date)).to eq(nil)
|
||||
expect(type_cast_value(12)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for time type" do
|
||||
let(:type) { :time }
|
||||
|
||||
it "should return same value for time value matching dummy date part" do
|
||||
expect(type_cast_value(Time.utc(2000, 1, 1, 0, 0, 0), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
expect(type_cast_value(Time.utc(2000, 1, 1, 0, 0, 0))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
end
|
||||
|
||||
it "should return dummy time value with same time part for time value with different date" do
|
||||
expect(type_cast_value(Time.utc(2010, 1, 1, 0, 0, 0), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
expect(type_cast_value(Time.utc(2010, 1, 1, 0, 0, 0))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
end
|
||||
|
||||
it "should return dummy time only for date value" do
|
||||
expect(type_cast_value(Date.new(2010, 1, 1), :time)).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Time.utc(2000, 1, 1, 0, 0, 0))
|
||||
end
|
||||
|
||||
it "should return dummy date with time part for datetime value" do
|
||||
expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :time)).to eq(Time.utc(2000, 1, 1, 12, 34, 56))
|
||||
expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56))).to eq(Time.utc(2000, 1, 1, 12, 34, 56))
|
||||
end
|
||||
|
||||
it 'should return nil for invalid value types' do
|
||||
expect(type_cast_value(12, :time)).to eq(nil)
|
||||
expect(type_cast_value(12)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "for datetime type" do
|
||||
let(:type) { :datetime }
|
||||
let(:time_zone_aware) { true }
|
||||
|
||||
it "should return Date as Time value" do
|
||||
expect(type_cast_value(Date.new(2010, 1, 1), :datetime)).to eq(Time.local(2010, 1, 1, 0, 0, 0))
|
||||
expect(type_cast_value(Date.new(2010, 1, 1))).to eq(Time.local(2010, 1, 1, 0, 0, 0))
|
||||
end
|
||||
|
||||
it "should return same Time value" do
|
||||
value = Time.utc(2010, 1, 1, 12, 34, 56)
|
||||
expect(type_cast_value(Time.utc(2010, 1, 1, 12, 34, 56), :datetime)).to eq(value)
|
||||
expect(type_cast_value(Time.utc(2010, 1, 1, 12, 34, 56))).to eq(value)
|
||||
end
|
||||
|
||||
it "should return as Time with same component values" do
|
||||
expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56), :datetime)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
|
||||
expect(type_cast_value(DateTime.civil_from_format(:utc, 2010, 1, 1, 12, 34, 56))).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
|
||||
end
|
||||
|
||||
it "should return same Time in correct zone if timezone aware" do
|
||||
@timezone_aware = true
|
||||
value = Time.utc(2010, 1, 1, 12, 34, 56)
|
||||
result = type_cast_value(value, :datetime)
|
||||
result = type_cast_value(value)
|
||||
expect(result).to eq(Time.zone.local(2010, 1, 1, 23, 34, 56))
|
||||
expect(result.zone).to eq('AEDT')
|
||||
end
|
||||
|
||||
it 'should return nil for invalid value types' do
|
||||
expect(type_cast_value(12, :datetime)).to eq(nil)
|
||||
expect(type_cast_value(12)).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "ignore_usec option" do
|
||||
let(:options) { {:ignore_usec => true} }
|
||||
let(:type) { :datetime }
|
||||
let(:ignore_usec) { true }
|
||||
|
||||
it "should ignore usec on time values when evaluated" do
|
||||
value = Time.utc(2010, 1, 1, 12, 34, 56, 10000)
|
||||
expect(type_cast_value(value, :datetime)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
|
||||
expect(type_cast_value(value)).to eq(Time.utc(2010, 1, 1, 12, 34, 56))
|
||||
end
|
||||
|
||||
context do
|
||||
let(:time_zone_aware) { true }
|
||||
|
||||
it "should ignore usec and return time in correct zone if timezone aware" do
|
||||
@timezone_aware = true
|
||||
value = Time.utc(2010, 1, 1, 12, 34, 56, 10000)
|
||||
result = type_cast_value(value, :datetime)
|
||||
result = type_cast_value(value)
|
||||
expect(result).to eq(Time.zone.local(2010, 1, 1, 23, 34, 56))
|
||||
expect(result.zone).to eq('AEDT')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dummy_time" do
|
||||
it 'should return Time with dummy date values but same time components' do
|
||||
@ -103,7 +118,6 @@ RSpec.describe ValidatesTimeliness::Conversion do
|
||||
end
|
||||
|
||||
it 'should return time component values shifted to current zone if timezone aware' do
|
||||
@timezone_aware = true
|
||||
expect(dummy_time(Time.utc(2000, 1, 1, 12, 34, 56))).to eq(Time.zone.local(2000, 1, 1, 23, 34, 56))
|
||||
end
|
||||
|
||||
@ -120,61 +134,64 @@ RSpec.describe ValidatesTimeliness::Conversion do
|
||||
end
|
||||
end
|
||||
|
||||
describe "#evaluate_option_value" do
|
||||
describe "#evaluate" do
|
||||
let(:person) { Person.new }
|
||||
|
||||
it 'should return Date object as is' do
|
||||
value = Date.new(2010,1,1)
|
||||
expect(evaluate_option_value(value, person)).to eq(value)
|
||||
expect(evaluate(value, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return Time object as is' do
|
||||
value = Time.mktime(2010,1,1)
|
||||
expect(evaluate_option_value(value, person)).to eq(value)
|
||||
expect(evaluate(value, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return DateTime object as is' do
|
||||
value = DateTime.new(2010,1,1,0,0,0)
|
||||
expect(evaluate_option_value(value, person)).to eq(value)
|
||||
expect(evaluate(value, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return Time value returned from proc with 0 arity' do
|
||||
value = Time.mktime(2010,1,1)
|
||||
expect(evaluate_option_value(lambda { value }, person)).to eq(value)
|
||||
expect(evaluate(lambda { value }, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return Time value returned by record attribute call in proc arity of 1' do
|
||||
value = Time.mktime(2010,1,1)
|
||||
person.birth_time = value
|
||||
expect(evaluate_option_value(lambda {|r| r.birth_time }, person)).to eq(value)
|
||||
expect(evaluate(lambda {|r| r.birth_time }, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return Time value for attribute method symbol which returns Time' do
|
||||
value = Time.mktime(2010,1,1)
|
||||
person.birth_datetime = value
|
||||
expect(evaluate_option_value(:birth_datetime, person)).to eq(value)
|
||||
expect(evaluate(:birth_datetime, person)).to eq(value)
|
||||
end
|
||||
|
||||
it 'should return Time value is default zone from string time value' do
|
||||
value = '2010-01-01 12:00:00'
|
||||
expect(evaluate_option_value(value, person)).to eq(Time.utc(2010,1,1,12,0,0))
|
||||
expect(evaluate(value, person)).to eq(Time.utc(2010,1,1,12,0,0))
|
||||
end
|
||||
|
||||
context do
|
||||
let(:converter) { described_class.new(type: :date, time_zone_aware: true) }
|
||||
|
||||
it 'should return Time value is current zone from string time value if timezone aware' do
|
||||
@timezone_aware = true
|
||||
value = '2010-01-01 12:00:00'
|
||||
expect(evaluate_option_value(value, person)).to eq(Time.zone.local(2010,1,1,12,0,0))
|
||||
expect(evaluate(value, person)).to eq(Time.zone.local(2010,1,1,12,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
it 'should return Time value in default zone from proc which returns string time' do
|
||||
value = '2010-11-12 13:00:00'
|
||||
expect(evaluate_option_value(lambda { value }, person)).to eq(Time.utc(2010,11,12,13,0,0))
|
||||
expect(evaluate(lambda { value }, person)).to eq(Time.utc(2010,11,12,13,0,0))
|
||||
end
|
||||
|
||||
skip 'should return Time value for attribute method symbol which returns string time value' do
|
||||
it 'should return Time value for attribute method symbol which returns string time value' do
|
||||
value = '13:00:00'
|
||||
person.birth_time = value
|
||||
expect(evaluate_option_value(:birth_time, person)).to eq(Time.utc(2000,1,1,13,0,0))
|
||||
expect(evaluate(:birth_time, person)).to eq(Time.utc(2000,1,1,13,0,0))
|
||||
end
|
||||
|
||||
context "restriction shorthand" do
|
||||
@ -183,17 +200,17 @@ RSpec.describe ValidatesTimeliness::Conversion do
|
||||
end
|
||||
|
||||
it 'should evaluate :now as current time' do
|
||||
expect(evaluate_option_value(:now, person)).to eq(Time.now)
|
||||
expect(evaluate(:now, person)).to eq(Time.now)
|
||||
end
|
||||
|
||||
it 'should evaluate :today as current time' do
|
||||
expect(evaluate_option_value(:today, person)).to eq(Date.today)
|
||||
expect(evaluate(:today, person)).to eq(Date.today)
|
||||
end
|
||||
|
||||
it 'should not use shorthand if symbol if is record method' do
|
||||
time = 1.day.from_now
|
||||
allow(person).to receive(:now).and_return(time)
|
||||
expect(evaluate_option_value(:now, person)).to eq(time)
|
||||
expect(evaluate(:now, person)).to eq(time)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -212,13 +229,11 @@ RSpec.describe ValidatesTimeliness::Conversion do
|
||||
with_config(:use_plugin_parser, false)
|
||||
|
||||
it 'should use Time.zone.parse attribute is timezone aware' do
|
||||
@timezone_aware = true
|
||||
expect(Time.zone).to receive(:parse)
|
||||
expect(Timeliness::Parser).to_not receive(:parse)
|
||||
parse('2000-01-01')
|
||||
end
|
||||
|
||||
it 'should use value#to_time if use_plugin_parser setting is false and attribute is not timezone aware' do
|
||||
@timezone_aware = false
|
||||
value = '2000-01-01'
|
||||
expect(value).to receive(:to_time)
|
||||
parse(value)
|
||||
Loading…
Reference in New Issue
Block a user