Class: Nanobot::Scheduler::ScheduleStore

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

Overview

ScheduleStore manages schedule CRUD and JSON persistence

Constant Summary collapse

VALID_KINDS =
%w[at every cron].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path: nil) ⇒ ScheduleStore

Returns a new instance of ScheduleStore.

Parameters:

  • path (String, Pathname) (defaults to: nil) —

    path to the schedules JSON file



25
26
27
28
29
30
# File 'lib/nanobot/scheduler/store.rb', line 25

def initialize(path: nil)
  @path = Pathname.new(path || File.expand_path('~/.nanobot/schedules.json'))
  @mutex = Mutex.new
  @schedules = {}
  load_from_disk
end

Instance Attribute Details

#path ⇒ Object (readonly)

Returns the value of attribute path.



22
23
24
# File 'lib/nanobot/scheduler/store.rb', line 22

def path
  @path
end

Instance Method Details

#add(kind:, expression:, prompt:, timezone: nil, deliver_to: nil) ⇒ Schedule

Create a new schedule

Parameters:

  • kind (String) —

    "at", "every", or "cron"

  • expression (String) —

    time expression (ISO 8601, duration, or cron)

  • prompt (String) —

    instruction for the agent

  • timezone (String, nil) (defaults to: nil) —

    IANA timezone for cron schedules

  • deliver_to (Hash, nil) (defaults to: nil) —

    target channel/chat_id for response routing

Returns:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/nanobot/scheduler/store.rb', line 39

def add(kind:, expression:, prompt:, timezone: nil, deliver_to: nil)
  validate_kind!(kind)
  validate_expression!(kind, expression, timezone)

  schedule = Schedule.new(
    id: SecureRandom.uuid,
    kind: kind,
    expression: expression,
    timezone: timezone,
    prompt: prompt,
    deliver_to: deliver_to,
    enabled: true,
    created_at: Time.now.iso8601,
    last_run_at: nil,
    next_run_at: compute_next_run(kind, expression, timezone).iso8601
  )

  @mutex.synchronize do
    @schedules[schedule.id] = schedule
    save_to_disk
  end

  schedule
end

#advance!(schedule, now = Time.now) ⇒ Object

Advance a schedule after firing

Parameters:

  • schedule (Schedule) —

    the schedule that just fired

  • now (Time) (defaults to: Time.now) —

    current time



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/nanobot/scheduler/store.rb', line 117

def advance!(schedule, now = Time.now)
  @mutex.synchronize do
    s = @schedules[schedule.id]
    return unless s

    s.last_run_at = now.iso8601

    case s.kind
    when 'at'
      s.enabled = false
      s.next_run_at = nil
    when 'every'
      duration = Fugit::Duration.parse(s.expression)
      s.next_run_at = (now + duration.to_sec).iso8601
    when 'cron'
      cron = Fugit::Cron.parse(s.expression)
      s.next_run_at = cron.next_time(now).to_t.iso8601
    end

    save_to_disk
  end
end

#due_schedules(now = Time.now) ⇒ Array<Schedule>

Find schedules that are due for execution

Parameters:

  • now (Time) (defaults to: Time.now) —

    current time

Returns:



106
107
108
109
110
111
112
# File 'lib/nanobot/scheduler/store.rb', line 106

def due_schedules(now = Time.now)
  @mutex.synchronize do
    @schedules.values.select do |s|
      s.enabled && s.next_run_at && Time.iso8601(s.next_run_at) <= now
    end
  end
end

#get(id) ⇒ Schedule?

Get a schedule by ID

Parameters:

  • id (String) —

    schedule UUID

Returns:



78
79
80
# File 'lib/nanobot/scheduler/store.rb', line 78

def get(id)
  @mutex.synchronize { @schedules[id] }
end

#list ⇒ Array<Schedule>

List all schedules

Returns:



84
85
86
# File 'lib/nanobot/scheduler/store.rb', line 84

def list
  @mutex.synchronize { @schedules.values }
end

#remove(id) ⇒ Boolean

Remove a schedule by ID

Parameters:

  • id (String) —

    schedule UUID

Returns:

  • (Boolean) —

    true if removed



67
68
69
70
71
72
73
# File 'lib/nanobot/scheduler/store.rb', line 67

def remove(id)
  @mutex.synchronize do
    removed = @schedules.delete(id)
    save_to_disk if removed
    !removed.nil?
  end
end

#update(id, **attrs) ⇒ Schedule?

Update specific fields on a schedule

Parameters:

  • id (String) —

    schedule UUID

  • attrs (Hash) —

    fields to update

Returns:



92
93
94
95
96
97
98
99
100
101
# File 'lib/nanobot/scheduler/store.rb', line 92

def update(id, **attrs)
  @mutex.synchronize do
    schedule = @schedules[id]
    return nil unless schedule

    attrs.each { |key, value| schedule[key] = value }
    save_to_disk
    schedule
  end
end