Class: Silas::Schedule

Inherits:
Object
  • Object
show all
Defined in:
lib/silas/schedule.rb,
lib/silas/schedule/compiler.rb

Overview

A schedule is a TRIGGER: it fires on a cron cadence and starts a normal, fully-durable turn. Two forms (eve's shapes):

.md with cron frontmatter — "task mode": the body becomes the turn input,
output discarded (fire-and-forget).
.rb subclassing Silas::Schedule::Handler — programmatic control (fan-out,
continue an existing session, conditional start).

Identity is the filesystem path under app/agent/schedules (subdirs included): schedules/billing/sweep.rb -> "billing/sweep" -> Agent::Schedules::Billing::Sweep. Schedules are NOT model-visible capabilities, so they never enter the definitions digest — adding or removing one cannot diverge an in-flight turn.

Defined Under Namespace

Modules: Compiler Classes: Handler

Constant Summary collapse

KINDS =
%i[task handler].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, cron:, queue:, kind:, payload:) ⇒ Schedule

Returns a new instance of Schedule.



20
21
22
23
24
25
26
# File 'lib/silas/schedule.rb', line 20

def initialize(name:, cron:, queue:, kind:, payload:)
  @name = name
  @cron = cron
  @queue = queue
  @kind = kind
  @payload = payload
end

Instance Attribute Details

#cronObject (readonly)

Returns the value of attribute cron.



18
19
20
# File 'lib/silas/schedule.rb', line 18

def cron
  @cron
end

#kindObject (readonly)

Returns the value of attribute kind.



18
19
20
# File 'lib/silas/schedule.rb', line 18

def kind
  @kind
end

#nameObject (readonly)

Returns the value of attribute name.



18
19
20
# File 'lib/silas/schedule.rb', line 18

def name
  @name
end

#payloadObject (readonly)

Returns the value of attribute payload.



18
19
20
# File 'lib/silas/schedule.rb', line 18

def payload
  @payload
end

#queueObject (readonly)

Returns the value of attribute queue.



18
19
20
# File 'lib/silas/schedule.rb', line 18

def queue
  @queue
end

Class Method Details

.from_handler(name, klass) ⇒ Object

Raises:



55
56
57
58
59
60
61
62
63
64
# File 'lib/silas/schedule.rb', line 55

def self.from_handler(name, klass)
  unless klass.ancestors.include?(Schedule::Handler)
    raise Error, "#{klass} (schedule #{name}) must inherit Silas::Schedule::Handler"
  end
  cron = klass.cron
  raise Error, "schedule #{name}: handler must declare `cron` or `every`" if cron.nil?

  new(name: name, cron: cron, queue: (klass.queue || Silas.config.queue_name).to_s,
      kind: :handler, payload: klass)
end

.from_markdown(name, path) ⇒ Object

Raises:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/silas/schedule.rb', line 39

def self.from_markdown(name, path)
  content = File.read(path)
  if content =~ /\A---\s*\n(.*?)\n---\s*\n(.*)\z/m
    frontmatter = YAML.safe_load(Regexp.last_match(1)) || {}
    body = Regexp.last_match(2)
  else
    frontmatter = {}
    body = content
  end
  cron = frontmatter["cron"] || frontmatter["schedule"]
  raise Error, "schedule #{name}: missing `cron:` frontmatter" if cron.nil?

  new(name: name, cron: cron.to_s, queue: (frontmatter["queue"] || Silas.config.queue_name).to_s,
      kind: :task, payload: body.strip)
end

.parse(path, root:) ⇒ Object



28
29
30
31
32
33
34
35
36
37
# File 'lib/silas/schedule.rb', line 28

def self.parse(path, root:)
  rel = path.relative_path_from(root.join("app/agent/schedules"))
  name = rel.sub_ext("").to_s
  case path.extname
  when ".md" then from_markdown(name, path)
  when ".rb"
    const = "Agent::Schedules::" + name.split("/").map(&:camelize).join("::")
    from_handler(name, const.constantize)
  end
end

Instance Method Details

#recurring_entryObject



78
79
80
# File 'lib/silas/schedule.rb', line 78

def recurring_entry
  { "class" => "Silas::ScheduleJob", "args" => [ name ], "schedule" => cron, "queue" => queue }
end

#recurring_keyObject



76
# File 'lib/silas/schedule.rb', line 76

def recurring_key = "silas_schedule_#{name.tr('/', '_')}"

#trigger!Object

The only behavioural difference between the two forms.



67
68
69
70
71
72
73
74
# File 'lib/silas/schedule.rb', line 67

def trigger!
  case kind
  when :task
    Silas.agent.start(input: payload, metadata: { "trigger" => "schedule", "schedule" => name })
  when :handler
    payload.new(schedule: self).call
  end
end