Class: Silas::Schedule
- Inherits:
-
Object
- Object
- Silas::Schedule
- 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
-
#cron ⇒ Object
readonly
Returns the value of attribute cron.
-
#kind ⇒ Object
readonly
Returns the value of attribute kind.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#payload ⇒ Object
readonly
Returns the value of attribute payload.
-
#queue ⇒ Object
readonly
Returns the value of attribute queue.
Class Method Summary collapse
- .from_handler(name, klass) ⇒ Object
- .from_markdown(name, path) ⇒ Object
- .parse(path, root:) ⇒ Object
Instance Method Summary collapse
-
#initialize(name:, cron:, queue:, kind:, payload:) ⇒ Schedule
constructor
A new instance of Schedule.
- #recurring_entry ⇒ Object
- #recurring_key ⇒ Object
-
#trigger! ⇒ Object
The only behavioural difference between the two forms.
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
#cron ⇒ Object (readonly)
Returns the value of attribute cron.
18 19 20 |
# File 'lib/silas/schedule.rb', line 18 def cron @cron end |
#kind ⇒ Object (readonly)
Returns the value of attribute kind.
18 19 20 |
# File 'lib/silas/schedule.rb', line 18 def kind @kind end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
18 19 20 |
# File 'lib/silas/schedule.rb', line 18 def name @name end |
#payload ⇒ Object (readonly)
Returns the value of attribute payload.
18 19 20 |
# File 'lib/silas/schedule.rb', line 18 def payload @payload end |
#queue ⇒ Object (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
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/silas/schedule.rb', line 57 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
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/silas/schedule.rb', line 39 def self.from_markdown(name, path) content = File.read(path) # Tolerate leading whitespace/blank lines (e.g. an ERB comment in a # generator template renders to an empty first line). if content.sub(/\A\s+/, "") =~ /\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_entry ⇒ Object
80 81 82 |
# File 'lib/silas/schedule.rb', line 80 def recurring_entry { "class" => "Silas::ScheduleJob", "args" => [ name ], "schedule" => cron, "queue" => queue } end |
#recurring_key ⇒ Object
78 |
# File 'lib/silas/schedule.rb', line 78 def recurring_key = "silas_schedule_#{name.tr('/', '_')}" |
#trigger! ⇒ Object
The only behavioural difference between the two forms.
69 70 71 72 73 74 75 76 |
# File 'lib/silas/schedule.rb', line 69 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 |