mirror of
https://github.com/ditkrg/validates_timeliness.git
synced 2026-01-22 22:06:45 +00:00
Change multiparamter extension to use AR type classes
Multiparamter extension used to do a lot of crappy work to ensure that the invalid Date dates were not accepted, but also to store a before_type_cast value in cases when it was invalid. We no longer need to handle the before_type_cast value as ActiveRecord does that nicely. Now we just ensure values to cast with a invalid date parts is not accepted.
This commit is contained in:
parent
a1f42fce28
commit
402a6b6e3e
@ -1,6 +1,6 @@
|
||||
= [UNRELEASED]
|
||||
* Fix removed namespace ActionRecord::AttributeAssignment::MultiparameterAttribute (AquisTech)
|
||||
* Fix DateTimeSelect extension support (AquisTech)
|
||||
* Update Multiparameter extension to use ActiveRecord type classes with multiparameter handling
|
||||
|
||||
= 4.0.2 [2016-01-07]
|
||||
* Fix undefine_generated_methods ivar guard setting to false
|
||||
|
||||
@ -1,74 +1,55 @@
|
||||
ActiveRecord::AttributeAssignment.class_eval do
|
||||
private
|
||||
module ValidatesTimeliness
|
||||
module Extensions
|
||||
class AcceptsMultiparameterTime < Module
|
||||
|
||||
# Yield if date values are valid
|
||||
def validate_multiparameter_date_values(set_values)
|
||||
if set_values[0..2].all?{ |v| v.present? } && Date.valid_civil?(*set_values[0..2])
|
||||
yield
|
||||
else
|
||||
invalid_multiparameter_date_or_time_as_string(set_values)
|
||||
end
|
||||
end
|
||||
def initialize(defaults: {})
|
||||
|
||||
def invalid_multiparameter_date_or_time_as_string(values)
|
||||
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?
|
||||
value
|
||||
end
|
||||
define_method(:cast) do |value|
|
||||
if value.is_a?(Hash)
|
||||
value_from_multiparameter_assignment(value)
|
||||
else
|
||||
super(value)
|
||||
end
|
||||
end
|
||||
|
||||
def instantiate_time_object(set_values)
|
||||
raise if set_values.any?(&:nil?)
|
||||
define_method(:assert_valid_value) do |value|
|
||||
if value.is_a?(Hash)
|
||||
value_from_multiparameter_assignment(value)
|
||||
else
|
||||
super(value)
|
||||
end
|
||||
end
|
||||
|
||||
validate_multiparameter_date_values(set_values) {
|
||||
set_values = set_values.map {|v| v.is_a?(String) ? v.strip : v }
|
||||
define_method(:value_from_multiparameter_assignment) do |values_hash|
|
||||
defaults.each do |k, v|
|
||||
values_hash[k] ||= v
|
||||
end
|
||||
return unless values_hash.values_at(1,2,3).all?{ |v| v.present? } &&
|
||||
Date.valid_civil?(*values_hash.values_at(1,2,3))
|
||||
|
||||
values = values_hash.sort.map(&:last)
|
||||
::Time.send(default_timezone, *values)
|
||||
end
|
||||
private :value_from_multiparameter_assignment
|
||||
|
||||
if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type_or_column)
|
||||
Time.zone.local(*set_values)
|
||||
else
|
||||
Time.send(object.class.default_timezone, *set_values)
|
||||
end
|
||||
}
|
||||
rescue
|
||||
invalid_multiparameter_date_or_time_as_string(set_values)
|
||||
end
|
||||
|
||||
def read_time
|
||||
# If column is a :time (and not :date or :timestamp) there is no need to validate if
|
||||
# there are year/month/day fields
|
||||
if cast_type_or_column.type == :time
|
||||
# if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
|
||||
{ 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
|
||||
values[key] ||= value
|
||||
end
|
||||
end
|
||||
|
||||
max_position = extract_max_param(6)
|
||||
set_values = values.values_at(*(1..max_position))
|
||||
|
||||
instantiate_time_object(set_values)
|
||||
end
|
||||
|
||||
def read_date
|
||||
set_values = values.values_at(1,2,3).map {|v| v.is_a?(String) ? v.strip : v }
|
||||
|
||||
if set_values.any? { |v| v.is_a?(String) }
|
||||
Timeliness.parse(set_values.join('-'), :date).try(:to_date) or raise TypeError
|
||||
else
|
||||
Date.new(*set_values)
|
||||
end
|
||||
rescue TypeError, ArgumentError, NoMethodError => ex # if Date.new raises an exception on an invalid date
|
||||
# Date.new with nil values throws NoMethodError
|
||||
raise ex if ex.is_a?(NoMethodError) && ex.message !~ /undefined method `div' for/
|
||||
invalid_multiparameter_date_or_time_as_string(set_values)
|
||||
end
|
||||
|
||||
# Cast type is v4.2 and column before
|
||||
def cast_type_or_column
|
||||
@cast_type || @column
|
||||
end
|
||||
|
||||
def timezone_conversion_attribute?
|
||||
object.class.send(:create_time_zone_conversion_attribute?, name, column)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
ActiveModel::Type::Date.class_eval do
|
||||
include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new
|
||||
end
|
||||
|
||||
ActiveModel::Type::Time.class_eval do
|
||||
include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new(
|
||||
defaults: { 1 => 1970, 2 => 1, 3 => 1, 4 => 0, 5 => 0 }
|
||||
)
|
||||
end
|
||||
|
||||
ActiveModel::Type::DateTime.class_eval do
|
||||
include ValidatesTimeliness::Extensions::AcceptsMultiparameterTime.new(
|
||||
defaults: { 4 => 0, 5 => 0 }
|
||||
)
|
||||
end
|
||||
@ -57,6 +57,7 @@ end
|
||||
ActiveRecord::Base.default_timezone = :utc
|
||||
ActiveRecord::Base.time_zone_aware_attributes = true
|
||||
ActiveRecord::Base.establish_connection({:adapter => 'sqlite3', :database => ':memory:'})
|
||||
ActiveRecord::Base.time_zone_aware_types = [:datetime, :time]
|
||||
ActiveRecord::Migration.verbose = false
|
||||
ActiveRecord::Schema.define(:version => 1) do
|
||||
create_table :employees, :force => true do |t|
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
RSpec.describe 'ValidatesTimeliness::Extensions::MultiparameterHandler' do
|
||||
|
||||
context "time column" do
|
||||
it 'should assign a string value for invalid date portion' do
|
||||
it 'should be nil invalid date portion' do
|
||||
employee = record_with_multiparameter_attribute(:birth_datetime, [2000, 2, 31, 12, 0, 0])
|
||||
expect(employee.birth_datetime_before_type_cast).to eq '2000-02-31 12:00:00'
|
||||
expect(employee.birth_datetime).to be_nil
|
||||
end
|
||||
|
||||
it 'should assign a Time value for valid datetimes' do
|
||||
employee = record_with_multiparameter_attribute(:birth_datetime, [2000, 2, 28, 12, 0, 0])
|
||||
expect(employee.birth_datetime_before_type_cast).to eq Time.zone.local(2000, 2, 28, 12, 0, 0)
|
||||
expect(employee.birth_datetime).to eq Time.zone.local(2000, 2, 28, 12, 0, 0)
|
||||
end
|
||||
|
||||
it 'should assign a string value for incomplete time' do
|
||||
it 'should be nil for incomplete date portion' do
|
||||
employee = record_with_multiparameter_attribute(:birth_datetime, [2000, nil, nil])
|
||||
expect(employee.birth_datetime_before_type_cast).to eq '2000-00-00'
|
||||
expect(employee.birth_datetime).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "date column" do
|
||||
it 'should assign a string value for invalid date' do
|
||||
it 'should assign nil for invalid date' do
|
||||
employee = record_with_multiparameter_attribute(:birth_date, [2000, 2, 31])
|
||||
expect(employee.birth_date_before_type_cast).to eq '2000-02-31'
|
||||
expect(employee.birth_date).to be_nil
|
||||
end
|
||||
|
||||
it 'should assign a Date value for valid date' do
|
||||
employee = record_with_multiparameter_attribute(:birth_date, [2000, 2, 28])
|
||||
expect(employee.birth_date_before_type_cast).to eq Date.new(2000, 2, 28)
|
||||
expect(employee.birth_date).to eq Date.new(2000, 2, 28)
|
||||
end
|
||||
|
||||
it 'should assign a string value for incomplete date' do
|
||||
it 'should assign hash values for incomplete date' do
|
||||
employee = record_with_multiparameter_attribute(:birth_date, [2000, nil, nil])
|
||||
expect(employee.birth_date_before_type_cast).to eq '2000-00-00'
|
||||
expect(employee.birth_date).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user