Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
eventful / app / models / eventful / schedule.rb
Size: Mime:
module Eventful
  # Recurrence strategy for Events
  class Schedule < ApplicationRecord
    self.table_name = 'eventful_schedules'

    before_validation :rebuild_program!

    attr_reader :days_of_week, :recur_until
    serialize :program, ::IceCube::Schedule

    validates :start_time, :end_time, presence: true
    validates_with RecurringTerminationValidator
    validates_with EndAfterStartValidator

    belongs_to :schedulable, polymorphic: true, touch: true

    def program
      self[:program] ||= ::IceCube::Schedule.new
    end

    def rebuild_program!
      reset_program_rules!
      if recurring?
        self.program.add_recurrence_rule build_weekly_rule
      end
      self.program
    end

    def recurring?
      days_of_week.present?
    end

    def terminating?
      program.terminating?
    end

    def occurrences
      program.all_occurrences
    end

    delegate :start_time, :end_time, to: :program
    def start_time=(value)
      program.start_time = parse_timeish(value, :start_time)
    end

    def end_time=(value)
      program.end_time = parse_timeish(value, :end_time)
    end

    def recur_until
      @recur_until ||= extract_recur_until(program)
    end

    def days_of_week
      @days_of_week ||= extract_days_of_week(program)
    end

    def recur_until=(value)
      @recur_until = parse_timeish(value, :recur_until)
    end

    def days_of_week=(days)
      return [] if days.nil?
      @days_of_week = days.reject(&:blank?).map(&:to_i)
    end

    private

    def build_weekly_rule
      unless days_of_week.empty?
        IceCube::Rule.weekly.day(*days_of_week).until(recur_until)
      end
    end

    def reset_program_rules!
      program.instance_variable_set(:@all_recurrence_rules, [])
    end

    # TODO: Extract the extractors?
    def extract_recur_until(ice_cube_schedule)
      ice_cube_schedule.recurrence_rules[0]&.validations&.dig(:until, 0)&.time
    end

    def extract_days_of_week(ice_cube_schedule)
      return unless rule = ice_cube_schedule.recurrence_rules.first
      day_validations = rule.validations.fetch(:day, [])
      return [] if day_validations.empty?
      day_validations.map(&:day)
    end

    def parse_timeish(value, field = :base)
      begin
        Time.zone.parse value.to_s
      rescue ArgumentError
        errors[field] << I18n.t('eventful.errors.invalid_time_format')
        nil
      end
    end
  end
end