Module: CronSwanson

Defined in:
lib/cron_swanson.rb,
lib/cron_swanson/version.rb,
lib/cron_swanson/whenever.rb

Overview

CronSwanson is a utility to generate run times for cron jobs which are regular, but which vary per job.

Defined Under Namespace

Classes: Whenever

Constant Summary collapse

SECONDS_PER_MINUTE =
60
SECONDS_PER_HOUR =
SECONDS_PER_MINUTE * 60
SECONDS_PER_DAY =
SECONDS_PER_HOUR * 24
VERSION =
"0.3.3"

Class Method Summary collapse

Class Method Details

.build_schedule(job_identifier, interval: default_interval) ⇒ String

generate a cron schedule string

the same input will always produce the same output.

Parameters:

  • job_identifier (String)

    a job to generate a schedule for

  • interval (Integer, ActiveSupport::Duration) (defaults to: default_interval)

    how often should the job be scheduled to run?

Returns:

  • (String)

    a schedule string like ‘38 4 * * *’



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/cron_swanson.rb', line 48

def self.build_schedule(job_identifier, interval: default_interval)
  if interval > SECONDS_PER_DAY
    raise ArgumentError, "interval must be less than 1 day (#{SECONDS_PER_DAY} seconds)."
  end

  if SECONDS_PER_DAY % interval != 0
    raise ArgumentError, "A day (#{SECONDS_PER_DAY} seconds) must be evenly " \
      "divisible by the given interval."
  end

  job_offset = offset(job_identifier, interval: interval)

  if interval >= SECONDS_PER_HOUR
    # figure out how many times job will happen in a day
    runs_per_day = SECONDS_PER_DAY / interval

    run_at = Time.at(job_offset).utc
    hours = []
    runs_per_day.times do |i|
      hours << run_at.hour + (i * interval / SECONDS_PER_HOUR)
    end

    "#{run_at.min} #{hours.join(',')} * * *"
  else
    minutes = []

    job_offset_minutes = job_offset / SECONDS_PER_MINUTE
    interval_minutes = interval / SECONDS_PER_MINUTE

    runs_per_hour = SECONDS_PER_HOUR / interval
    runs_per_hour.times do |i|
      minutes << job_offset_minutes + (i * interval_minutes)
    end

    "#{minutes.join(',')} * * * *"
  end
end

.default_intervalObject



12
13
14
# File 'lib/cron_swanson.rb', line 12

def self.default_interval
  SECONDS_PER_DAY
end

.offset(job_identifier, interval: default_interval) ⇒ Integer

offset within a time period

if the interval is 6 hours, the returned offset will be some number of seconds between 0 and 60 * 60 * 6 seconds (6 hours).

Parameters:

  • job_identifier (String)

    if nil, method will determine this on its own

  • interval (Integer) (defaults to: default_interval)

    how often will the job be run?

Returns:

  • (Integer)

    number of seconds to offset this job



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/cron_swanson.rb', line 25

def self.offset(job_identifier, interval: default_interval)
  sha = Digest::SHA256.hexdigest(job_identifier.to_s)

  # largest possible hex sha256 value
  max_sha256_value = (16**64).to_f

  # what % of the max sha256 is the job_identifier?
  sha_pct_of_max_sha256 = sha.to_i(16) / max_sha256_value

  # apply that same % to the desired interval to get an offset
  offset_seconds = (sha_pct_of_max_sha256 * interval).round

  offset_seconds
end