lib/validates_timeliness/validator.rb in adzap-validates_timeliness-1.1.5 vs lib/validates_timeliness/validator.rb in adzap-validates_timeliness-1.1.6

- old
+ new

@@ -10,29 +10,36 @@ :date => '%Y-%m-%d', :datetime => '%Y-%m-%d %H:%M:%S' } RESTRICTION_METHODS = { + :equal_to => :==, :before => :<, :after => :>, :on_or_before => :<=, :on_or_after => :>=, :between => lambda {|v, r| (r.first..r.last).include?(v) } } + VALID_OPTIONS = [ + :on, :if, :unless, :allow_nil, :empty, :allow_blank, :blank, + :with_time, :with_date, :ignore_usec, + :invalid_time_message, :invalid_date_message, :invalid_datetime_message + ] + RESTRICTION_METHODS.keys.map {|option| [option, "#{option}_message".to_sym] }.flatten + attr_reader :configuration, :type def initialize(configuration) - defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false } + defaults = { :on => :save, :type => :datetime, :allow_nil => false, :allow_blank => false, :ignore_usec => false } @configuration = defaults.merge(configuration) @type = @configuration.delete(:type) + validate_options(@configuration) end - def call(record, attr_name) - value = record.send(attr_name) + def call(record, attr_name, value) value = record.class.parse_date_time(value, type, false) if value.is_a?(String) - raw_value = raw_value(record, attr_name) + raw_value = raw_value(record, attr_name) || value return if (raw_value.nil? && configuration[:allow_nil]) || (raw_value.blank? && configuration[:allow_blank]) add_error(record, attr_name, :blank) and return if raw_value.blank? @@ -42,22 +49,29 @@ end private def raw_value(record, attr_name) - record.send("#{attr_name}_before_type_cast") + record.send("#{attr_name}_before_type_cast") rescue nil end def validate_restrictions(record, attr_name, value) - value = type_cast_value(value) - + value = if @configuration[:with_time] || @configuration[:with_date] + restriction_type = :datetime + combine_date_and_time(value, record) + else + restriction_type = type + self.class.type_cast_value(value, type, @configuration[:ignore_usec]) + end + return if value.nil? + RESTRICTION_METHODS.each do |option, method| next unless restriction = configuration[option] begin - restriction = restriction_value(restriction, record) + restriction = self.class.evaluate_option_value(restriction, restriction_type, record) next if restriction.nil? - restriction = type_cast_value(restriction) + restriction = self.class.type_cast_value(restriction, restriction_type, @configuration[:ignore_usec]) unless evaluate_restriction(restriction, value, method) add_error(record, attr_name, option, interpolation_values(option, restriction)) end rescue @@ -85,14 +99,14 @@ def evaluate_restriction(restriction, value, comparator) return true if restriction.nil? case comparator - when Symbol - value.send(comparator, restriction) - when Proc - comparator.call(value, restriction) + when Symbol + value.send(comparator, restriction) + when Proc + comparator.call(value, restriction) end end def add_error(record, attr_name, message, interpolate=nil) if defined?(I18n) @@ -105,59 +119,87 @@ record.errors.add(attr_name, message) end end def error_messages - return @error_messages if defined?(@error_messages) - @error_messages = ValidatesTimeliness.default_error_messages.merge(custom_error_messages) + @error_messages ||= ValidatesTimeliness.default_error_messages.merge(custom_error_messages) end def custom_error_messages - return @custom_error_messages if defined?(@custom_error_messages) - @custom_error_messages = configuration.inject({}) {|msgs, (k, v)| + @custom_error_messages ||= configuration.inject({}) {|msgs, (k, v)| if md = /(.*)_message$/.match(k.to_s) msgs[md[1].to_sym] = v end msgs } end - def restriction_value(restriction, record) - case restriction + def combine_date_and_time(value, record) + if type == :date + date = value + time = @configuration[:with_time] + else + date = @configuration[:with_date] + time = value + end + date, time = self.class.evaluate_option_value(date, :date, record), self.class.evaluate_option_value(time, :time, record) + 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]) + end + + def validate_options(options) + invalid_for_type = ([:time, :date, :datetime] - [@type]).map {|k| "invalid_#{k}_message".to_sym } + invalid_for_type << :with_date unless @type == :time + invalid_for_type << :with_time unless @type == :date + options.assert_valid_keys(VALID_OPTIONS - invalid_for_type) + end + + # class methods + class << self + + def evaluate_option_value(value, type, record) + case value when Time, Date, DateTime - restriction + value when Symbol - restriction_value(record.send(restriction), record) + evaluate_option_value(record.send(value), type, record) when Proc - restriction_value(restriction.call(record), record) + evaluate_option_value(value.call(record), type, record) when Array - restriction.map {|r| restriction_value(r, record) }.sort + value.map {|r| evaluate_option_value(r, type, record) }.sort when Range - restriction_value([restriction.first, restriction.last], record) + evaluate_option_value([value.first, value.last], type, record) else - record.class.parse_date_time(restriction, type, false) + record.class.parse_date_time(value, type, false) + end end - end - - def type_cast_value(value) - if value.is_a?(Array) - value.map {|v| type_cast_value(v) } - else - case type - when :time - value.to_dummy_time - when :date - value.to_date - when :datetime - if value.is_a?(DateTime) || value.is_a?(Time) - value.to_time + + def type_cast_value(value, type, ignore_usec=false) + if value.is_a?(Array) + value.map {|v| type_cast_value(v, type, ignore_usec) } + else + value = case type + when :time + value.to_dummy_time + when :date + value.to_date + when :datetime + if value.is_a?(DateTime) || value.is_a?(Time) + value.to_time + else + value.to_time(ValidatesTimeliness.default_timezone) + end else - value.to_time(ValidatesTimeliness.default_timezone) + nil end - else - nil + if ignore_usec && value.is_a?(Time) + ::ActiveRecord::Base.send(:make_time, Array(value).reverse[4..9]) + else + value + end end end + end end end