boblail/hiccup

View on GitHub
lib/hiccup/validatable.rb

Summary

Maintainability
A
1 hr
Test Coverage
require "hiccup/convenience"
require "active_support/concern"


module Hiccup
  module Validatable
    include Convenience
    extend ActiveSupport::Concern


    # !todo: use ActiveModel:Validation rather than a custom method
    included do
      validates :skip, numericality: {greater_than: 0}
      validate                    :validate_recurrence
    end


  private


    # !todo: use i18n to let clients of this library supply their own wording
    def validate_recurrence
      case kind
      when :never;
      when :weekly;     validate_weekly_recurrence
      when :monthly;    validate_monthly_recurrence
      when :annually;
      else;             invalid_kind!
      end

      errors.add :start_date, "is a #{start_date.class} not a Date" unless start_date.is_a?(Date)
      if ends?
        if end_date.is_a?(Date)
          errors.add :end_date, "cannot be before start" if start_date.is_a?(Date) && end_date < start_date
        else
          errors.add :end_date, "is a #{end_date.class} not a Date"
        end
      end
    end


    def validate_weekly_recurrence
      if !weekly_pattern.is_a?(Array)
        errors.add(:weekly_pattern, "is a #{weekly_pattern.class}. It should be an array")
      elsif weekly_pattern.empty?
        errors.add(:weekly_pattern, "is empty. It should contain a list of weekdays")
      elsif (invalid_names = weekly_pattern - Date::DAYNAMES).any?
        errors.add(:weekly_pattern, "should contain only weekdays. (#{invalid_names.to_sentence} are invalid)")
      end
    end


    def validate_monthly_recurrence
      if !monthly_pattern.is_a?(Array)
        errors.add(:monthly_pattern, "is a #{monthly_pattern.class}. It should be an array")
      elsif monthly_pattern.empty?
        errors.add(:monthly_pattern, "is empty. It should contain a list of monthly occurrences")
      elsif monthly_pattern.select(&method(:invalid_occurrence?)).any?
        errors.add(:monthly_pattern, "contains invalid monthly occurrences")
      end
    end


    def invalid_occurrence?(occurrence)
      !valid_occurrence?(occurrence)
    end

    def valid_occurrence?(occurrence)
      if occurrence.is_a?(Array)
        i, wd = occurrence
        Date::DAYNAMES.member?(wd) && i.is_a?(Integer) && ((i == -1) || (1..6).include?(i))
      else
        i = occurrence
        i.is_a?(Integer) && ([-1] + (1..31).to_a).include?(i)
      end
    end


    def invalid_kind!
      errors.add(:kind, "#{kind.inspect} is not recognized. It must be one of #{Kinds.collect{|kind| ":#{kind}"}.to_sentence(:two_words_connector => " or ", :last_word_connector => ", or ")}.")
    end


    # def valid_occurrence?(occurrence)
    #   if occurrence.is_a?(Array)
    #     ordinal, kind = occurrence
    #
    #     errors.add(:kind, "is not a valid monthly occurrence kind") unless Date::DAYNAMES.member?(kind)
    #     if ordinal.is_a?(Integer)
    #       errors.add(:ordinal, "is not a valid integer") unless (ordinal==-1) or (1..6).include?(ordinal)
    #     else
    #       errors.add(:ordinal, "is not an integer")
    #     end
    #   else
    #     ordinal = occurrence
    #
    #     if ordinal.is_a?(Integer)
    #       errors.add(:ordinal, "is not an integer between 1 and 31") unless (1..31).include?(ordinal)
    #     else
    #       errors.add(:ordinal, "is not an integer")
    #     end
    #   end
    # end


  end
end