class SimpleScheduler::Task

Class for parsing each task in the scheduler config YAML file and returning the values needed to schedule the task in the future.

@!attribute [r] job_class

@return [Class] The class of the job or worker.

@!attribute [r] params

@return [Hash] The params used to create the task

Constants

DEFAULT_QUEUE_AHEAD_MINUTES

Attributes

job_class[R]
params[R]

Public Class Methods

new(params) click to toggle source

Initializes a task by parsing the params so the task can be queued in the future. @param params [Hash] @option params [String] :class The class of the Active Job or Sidekiq Worker @option params [String] :every How frequently the job will be performed @option params [String] :at The starting time for the interval @option params [String] :expires_after The interval used to determine how late the job is allowed to run @option params [Integer] :queue_ahead The number of minutes that jobs should be queued in the future @option params [String] :task_name The name of the task as defined in the YAML config @option params [String] :tz The time zone to use when parsing the `at` option

# File lib/simple_scheduler/task.rb, line 23
def initialize(params)
  validate_params!(params)
  @params = params
end
scheduled_set() click to toggle source

Loads the scheduled jobs from Sidekiq once to avoid loading from Redis for each task when looking up existing scheduled jobs. @return [Sidekiq::ScheduledSet]

# File lib/simple_scheduler/task.rb, line 112
def self.scheduled_set
  @scheduled_set ||= Sidekiq::ScheduledSet.new
end

Public Instance Methods

at() click to toggle source

The task's first run time as a Time-like object. @return [SimpleScheduler::At]

# File lib/simple_scheduler/task.rb, line 30
def at
  @at ||= At.new(@params[:at], time_zone)
end
existing_jobs() click to toggle source

Returns an array of existing jobs matching the job class of the task. @return [Array<Sidekiq::SortedEntry>]

# File lib/simple_scheduler/task.rb, line 42
def existing_jobs
  @existing_jobs ||= SimpleScheduler::Task.scheduled_set.select do |job|
    next unless job.display_class == "SimpleScheduler::FutureJob"

    task_params = job.display_args[0].symbolize_keys
    task_params[:class] == job_class_name && task_params[:name] == name
  end.to_a
end
existing_run_times() click to toggle source

Returns an array of existing future run times that have already been scheduled. @return [Array<Time>]

# File lib/simple_scheduler/task.rb, line 53
def existing_run_times
  @existing_run_times ||= existing_jobs.map(&:at)
end
expires_after() click to toggle source

The time between the scheduled and actual run time that should cause the job not to run. @return [String]

# File lib/simple_scheduler/task.rb, line 36
def expires_after
  @params[:expires_after]
end
frequency() click to toggle source

How often the job will be run. @return [ActiveSupport::Duration]

# File lib/simple_scheduler/task.rb, line 59
def frequency
  @frequency ||= parse_frequency(@params[:every])
end
future_run_times() click to toggle source

Returns an array Time objects for future run times based on the current time and the given minutes to look ahead. @return [Array<Time>] rubocop:disable Metrics/AbcSize

# File lib/simple_scheduler/task.rb, line 67
def future_run_times
  future_run_times = existing_run_times.dup
  last_run_time = future_run_times.last || at - frequency
  last_run_time = last_run_time.in_time_zone(time_zone)

  # Ensure there are at least two future jobs scheduled and that the queue ahead time is filled
  while future_run_times.length < 2 || minutes_queued_ahead(last_run_time) < queue_ahead
    last_run_time = frequency.from_now(last_run_time)
    # The hour may not match because of a shift caused by DST in previous run times,
    # so we need to ensure that the hour matches the specified hour if given.
    last_run_time = last_run_time.change(hour: at.hour, min: at.min) if at.hour?
    future_run_times << last_run_time
  end

  future_run_times
end
job_class_name() click to toggle source

The class name of the job or worker. @return [String]

# File lib/simple_scheduler/task.rb, line 87
def job_class_name
  @params[:class]
end
name() click to toggle source

The name of the task as defined in the YAML config. @return [String]

# File lib/simple_scheduler/task.rb, line 93
def name
  @params[:name]
end
queue_ahead() click to toggle source

The number of minutes that jobs should be queued in the future. @return [Integer]

# File lib/simple_scheduler/task.rb, line 99
def queue_ahead
  @queue_ahead ||= @params[:queue_ahead] || DEFAULT_QUEUE_AHEAD_MINUTES
end
time_zone() click to toggle source

The time zone to use when parsing the `at` option. @return [ActiveSupport::TimeZone]

# File lib/simple_scheduler/task.rb, line 105
def time_zone
  @time_zone ||= params[:tz] ? ActiveSupport::TimeZone.new(params[:tz]) : Time.zone
end

Private Instance Methods

minutes_queued_ahead(last_run_time) click to toggle source
# File lib/simple_scheduler/task.rb, line 118
def minutes_queued_ahead(last_run_time)
  (last_run_time - Time.now) / 60
end
parse_frequency(every_string) click to toggle source
# File lib/simple_scheduler/task.rb, line 122
def parse_frequency(every_string)
  split_duration = every_string.split(".")
  frequency = split_duration[0].to_i
  frequency_units = split_duration[1]
  frequency.send(frequency_units)
end
validate_params!(params) click to toggle source
# File lib/simple_scheduler/task.rb, line 129
def validate_params!(params)
  raise ArgumentError, "Missing param `class` specifying the class of the job to run." unless params.key?(:class)
  raise ArgumentError, "Missing param `every` specifying how often the job should run." unless params.key?(:every)

  @job_class = params[:class].constantize
  params[:name] ||= params[:class]
end