Class: Do::Scheduler

Inherits:
Object
  • Object
show all
Defined in:
lib/do/scheduler.rb

Overview

Translates a task's declarative schedule into a systemd calendar expression (#on_calendar) suitable for use in an OnCalendar= directive.

do does not implement its own timer loop; it only maps a small, fixed set of schedule shapes onto systemd's native calendar syntax.

Constant Summary collapse

TIME_RE =
/\A([01]?\d|2[0-3]):([0-5]\d)\z/

Class Method Summary collapse

Class Method Details

.daily_calendar(task) ⇒ Object



39
40
41
42
# File 'lib/do/scheduler.rb', line 39

def self.daily_calendar(task)
  hour, min = split_time(task, fallback: [0, 0])
  "*-*-* #{pad(hour)}:#{pad(min)}:00"
end

.hourly_calendar(task) ⇒ Object



34
35
36
37
# File 'lib/do/scheduler.rb', line 34

def self.hourly_calendar(task)
  _, min = split_time(task, fallback: [0, 0])
  "*-*-* *:#{pad(min)}:00"
end

.monthly_calendar(task) ⇒ Object



50
51
52
53
54
# File 'lib/do/scheduler.rb', line 50

def self.monthly_calendar(task)
  hour, min = split_time(task, fallback: [0, 0])
  dom = monthly_day(task)
  "*-*-#{dom} #{pad(hour)}:#{pad(min)}:00"
end

.monthly_day(task) ⇒ Object



69
70
71
72
73
74
75
76
77
78
# File 'lib/do/scheduler.rb', line 69

def self.monthly_day(task)
  day = task.day&.to_s
  if day.nil? || day.empty?
    '1'
  elsif day =~ /\A([1-9]|[12]\d|3[01])\z/
    day
  else
    raise Error, "invalid monthly day '#{task.day}'; expected a day-of-month 1-31"
  end
end

.on_calendar(task) ⇒ String?

Returns the systemd OnCalendar expression, or nil when the task is manual-only.

Returns:

  • (String, nil)

    the systemd OnCalendar expression, or nil when the task is manual-only.



14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/do/scheduler.rb', line 14

def self.on_calendar(task)
  case task.schedule
  when nil, '' then nil
  when 'once'   then once_calendar
  when 'hourly' then hourly_calendar(task)
  when 'daily'  then daily_calendar(task)
  when 'weekly' then weekly_calendar(task)
  when 'monthly' then monthly_calendar(task)
  else
    raise Error, "unsupported schedule '#{task.schedule}'"
  end
end

.once_calendarObject



27
28
29
30
31
32
# File 'lib/do/scheduler.rb', line 27

def self.once_calendar
  # OnActiveSec=0 fires the service exactly once, when the timer is
  # activated. The reload/enable workflow activates the timer, so `once`
  # means "run once now when scheduled", without repeating.
  'OnActiveSec=0'
end

.pad(value) ⇒ Object



89
90
91
# File 'lib/do/scheduler.rb', line 89

def self.pad(value)
  value.to_s.rjust(2, '0')
end

.split_time(task, fallback:) ⇒ Object

Raises:



80
81
82
83
84
85
86
87
# File 'lib/do/scheduler.rb', line 80

def self.split_time(task, fallback:)
  return fallback if task.time.nil? || task.time.empty?

  m = TIME_RE.match(task.time.to_s)
  raise Error, "invalid time '#{task.time}'; expected HH:MM" unless m

  [m[1].to_i, m[2].to_i]
end

.weekday_number(task) ⇒ Object



63
64
65
66
67
# File 'lib/do/scheduler.rb', line 63

def self.weekday_number(task)
  day = task.day&.to_s&.downcase
  Task::WEEKDAY_NUMBERS[day] or
    raise Error, "invalid day '#{task.day}'; expected a day name like 'monday'"
end

.weekday_short(task) ⇒ Object



56
57
58
59
60
61
# File 'lib/do/scheduler.rb', line 56

def self.weekday_short(task)
  day = task.day&.to_s&.downcase
  Task::WEEKDAY_NUMBERS[day] or
    raise Error, "invalid day '#{task.day}'; expected a day name like 'monday'"
  day[0, 3].capitalize
end

.weekly_calendar(task) ⇒ Object



44
45
46
47
48
# File 'lib/do/scheduler.rb', line 44

def self.weekly_calendar(task)
  hour, min = split_time(task, fallback: [0, 0])
  dow = weekday_short(task)
  "#{dow} *-*-* #{pad(hour)}:#{pad(min)}:00"
end