diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 81990ce..641e064 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,3 +1,6 @@ += 4.1.0 [2015-08-21] +* Restore extensions for Rails 4 (< 4.2) + = 4.0.0 [2015-07-19] * Better compatibility with Rails 4.x (and drop support < 4.0) * Support Ruby 2.0+ diff --git a/lib/validates_timeliness/extensions.rb b/lib/validates_timeliness/extensions.rb index e76485c..8cd5a23 100644 --- a/lib/validates_timeliness/extensions.rb +++ b/lib/validates_timeliness/extensions.rb @@ -1,14 +1,16 @@ module ValidatesTimeliness module Extensions - autoload :DateTimeSelect, 'validates_timeliness/extensions/date_time_select' - autoload :MultiparameterHandler, 'validates_timeliness/extensions/multiparameter_handler' + autoload :DateTimeSelect, 'validates_timeliness/extensions/date_time_select' + autoload :AttributeAssignment, 'validates_timeliness/extensions/attribute_assignment' + autoload :MultiparameterAttribute, 'validates_timeliness/extensions/multiparameter_attribute' end def self.enable_date_time_select_extension! - ::ActionView::Helpers::InstanceTag.send(:include, ValidatesTimeliness::Extensions::DateTimeSelect) + ::ActionView::Helpers::Tags::DateSelect.send(:include, ValidatesTimeliness::Extensions::DateTimeSelect) end def self.enable_multiparameter_extension! - ::ActiveRecord::Base.send(:include, ValidatesTimeliness::Extensions::MultiparameterHandler) + ::ActiveRecord::Base.send(:include, ValidatesTimeliness::Extensions::AttributeAssignment) + ::ActiveRecord::AttributeAssignment::MultiparameterAttribute.send(:include, ValidatesTimeliness::Extensions::MultiparameterAttribute) end end diff --git a/lib/validates_timeliness/extensions/attribute_assignment.rb b/lib/validates_timeliness/extensions/attribute_assignment.rb new file mode 100644 index 0000000..ed91e18 --- /dev/null +++ b/lib/validates_timeliness/extensions/attribute_assignment.rb @@ -0,0 +1,33 @@ +module ValidatesTimeliness + module Extensions + module AttributeAssignment + extend ActiveSupport::Concern + + # Stricter handling of date and time values from multiparameter + # assignment from the date/time select view helpers + + included do + alias_method :execute_callstack_for_multiparameter_attributes, :execute_callstack_for_multiparameter_attributes_with_timeliness + end + + private + + def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) + errors = [] + callstack.each do |name, values_with_empty_parameters| + begin + send("#{name}=", self.class::MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value) + rescue => ex + values = values_with_empty_parameters.is_a?(Hash) ? values_with_empty_parameters.values : values_with_empty_parameters + errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name} (#{ex.message})", ex, name) + end + end + unless errors.empty? + error_descriptions = errors.map { |ex| ex.message }.join(",") + raise ActiveRecord::MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]" + end + end + + end + end +end diff --git a/lib/validates_timeliness/extensions/multiparameter_attribute.rb b/lib/validates_timeliness/extensions/multiparameter_attribute.rb new file mode 100644 index 0000000..b127852 --- /dev/null +++ b/lib/validates_timeliness/extensions/multiparameter_attribute.rb @@ -0,0 +1,66 @@ +module ValidatesTimeliness + module Extensions + module MultiparameterAttribute + extend ActiveSupport::Concern + + # Stricter handling of date and time values from multiparameter + # assignment from the date/time select view helpers + + included do + alias_method_chain :instantiate_time_object, :timeliness + alias_method :read_value, :read_value_with_timeliness + end + + +# private + + def invalid_multiparameter_date_or_time_as_string(set_values) + value = [set_values[0], *set_values[1..2].map {|s| s.to_s.rjust(2,"0")} ].join("-") + value += ' ' + set_values[3..5].map {|s| s.to_s.rjust(2, "0") }.join(":") unless set_values[3..5].empty? + value + end + + def instantiate_time_object_with_timeliness(set_values) + validate_multiparameter_date_values(set_values) { + instantiate_time_object_without_timeliness(set_values) + } + end + + def instantiate_date_object(set_values) + validate_multiparameter_date_values(set_values) { + Date.new(*set_values) + } + end + + # 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 read_value_with_timeliness + @column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name) + klass = column.klass + + set_values = values.is_a?(Hash) ? values.to_a.sort_by(&:first).map(&:last) : values + if set_values.empty? || set_values.all?{ |v| v.nil? } + nil + elsif klass == Time + instantiate_time_object(set_values) + elsif klass == Date + instantiate_date_object(set_values) + else + if respond_to?(:read_other_parameter_value) + read_date_parameter_value(name, values) + else + klass.new(*set_values) + end + end + end + + end + end +end diff --git a/lib/validates_timeliness/extensions/multiparameter_handler.rb b/lib/validates_timeliness/extensions/multiparameter_handler.rb index 3da494d..dee3f8a 100644 --- a/lib/validates_timeliness/extensions/multiparameter_handler.rb +++ b/lib/validates_timeliness/extensions/multiparameter_handler.rb @@ -3,7 +3,7 @@ module Extensions module MultiparameterHandler extend ActiveSupport::Concern - # Stricter handling of date and time values from multiparameter + # Stricter handling of date and time values from multiparameter # assignment from the date/time select view helpers included do @@ -66,7 +66,7 @@ def execute_callstack_for_multiparameter_attributes_with_timeliness(callstack) begin send(name + "=", read_value_from_parameter(name, values_with_empty_parameters)) rescue => ex - values = values_with_empty_parameters.is_a?(Hash) ? values_with_empty_parameters.values : values_with_empty_parameters + values = values_with_empty_parameters.is_a?(Hash) ? values_with_empty_parameters.values : values_with_empty_parameters errors << ActiveRecord::AttributeAssignmentError.new("error on assignment #{values.inspect} to #{name}", ex, name) end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c7d8f9c..8bbbd2c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -23,8 +23,8 @@ ValidatesTimeliness.setup do |c| c.extend_orms = [ :active_record ] - # c.enable_date_time_select_extension! - # c.enable_multiparameter_extension! + c.enable_date_time_select_extension! + c.enable_multiparameter_extension! c.default_timezone = :utc end diff --git a/spec/validates_timeliness/conversion_spec.rb b/spec/validates_timeliness/conversion_spec.rb index e62a47d..ca3b784 100644 --- a/spec/validates_timeliness/conversion_spec.rb +++ b/spec/validates_timeliness/conversion_spec.rb @@ -157,12 +157,12 @@ expect(evaluate_option_value(:birth_time, person)).to eq(value) end - it 'should return Time value is default zone from string time value' do + it 'should return Time value in 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)) end - it 'should return Time value is current zone from string time value if timezone aware' do + it 'should return Time value in 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)) @@ -174,9 +174,9 @@ end it 'should return Time value for attribute method symbol which returns string time value' do - value = '2010-01-01 12:00:00' + value = '2010-01-01 12:00:00 UTC' person.birth_time = value - expect(evaluate_option_value(:birth_time, person)).to eq(Time.local(2010,1,1,12,0,0)) + expect(evaluate_option_value(:birth_time, person)).to eq(Time.utc(2010,1,1,12,0,0)) end context "restriction shorthand" do diff --git a/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb b/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb index 3ed00b0..5040f0d 100644 --- a/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb +++ b/spec/validates_timeliness/extensions/multiparameter_handler_spec.rb @@ -1,44 +1,41 @@ -# require 'spec_helper' -# -# describe ValidatesTimeliness::Extensions::MultiparameterHandler do -# -# context "time column" do -# it 'should assign a string value for 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' -# 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) -# end -# -# it 'should assign a string value for incomplete time' do -# employee = record_with_multiparameter_attribute(:birth_datetime, [2000, nil, nil]) -# expect(employee.birth_datetime_before_type_cast).to eq '2000-00-00' -# end -# end -# -# context "date column" do -# it 'should assign a string value 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' -# 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) -# end -# -# it 'should assign a string value 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' -# end -# end -# -# def record_with_multiparameter_attribute(name, values) -# hash = {} -# values.each_with_index {|value, index| hash["#{name}(#{index+1}i)"] = value.to_s } -# Employee.new(hash) -# end -# end +require 'spec_helper' + +describe ValidatesTimeliness::Extensions do + + context "time column" do + it 'should assign a string value for invalid date portion' do + employee = record_with_multiparameter_attribute(:birth_datetime, [2000, 2, 31, 12, 0, 0]) + expect(employee.birth_datetime_before_type_cast.class).to eq String + expect(employee.birth_datetime_before_type_cast).to be_a(String) + expect(employee.birth_datetime_before_type_cast).to eq '2000-02-31 12:00:00' + 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) + end + end + + context "date column" do + it 'should assign a string value 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' + 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) + end + + it 'should assign a string value 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' + end + end + + def record_with_multiparameter_attribute(name, values) + hash = {} + values.each_with_index {|value, index| hash["#{name}(#{index+1}i)"] = value.to_s } + Employee.new(hash) + end +end diff --git a/spec/validates_timeliness/orm/active_record_spec.rb b/spec/validates_timeliness/orm/active_record_spec.rb index f8d3069..94d639c 100644 --- a/spec/validates_timeliness/orm/active_record_spec.rb +++ b/spec/validates_timeliness/orm/active_record_spec.rb @@ -52,21 +52,21 @@ class Janitor < Employee record.birth_date = '2012-01-01' record.valid? - record.errors[:birth_date].should be_empty + expect(record.errors[:birth_date]).to be_empty end it "should validate a invalid value string" do record.birth_date = 'not a date' record.valid? - record.errors[:birth_date].should_not be_empty + expect(record.errors[:birth_date]).not_to be_empty end it "should validate a nil value" do record.birth_date = nil record.valid? - record.errors[:birth_date].should be_empty + expect(record.errors[:birth_date]).to be_empty end end