Rat docs

Script mode (rat run)

rat run script.rat [args...] executes a single .rat file's > main[args] against the same eval engine the dev server uses. No HTTP, no page render, no client. The substrate stands on its own — useful for seed scripts, one-off data fixes, CLI utilities, anything that wants Rat's dispatch and builtins without spinning up a browser.

Hello, args

Positional CLI args land as a Rat array

The script declares > main[args]. args is a Rat array of strings — the CLI command and the script path are stripped before binding, so rat run hello.rat one two hands main ['one', 'two']. << N returns the exit code.

> main[args]
log('hello from rat run', args)
<< 0

Output (stdout):

hello from rat run [one, two]

Exit codes via <<

main returns an int; rat exits with it

Any integer returned via << becomes the process exit code (clamped to a byte). A non-int return — or no return at all — falls through to 0. Use this to signal pass / fail to whatever shell or CI step invoked the script.

> main[args]
(len(args) == 0)
    error('usage: rat run check.rat <name>')
    << 2
log('checking', args[0])
<< 0

log vs error

Two streams, like every other Unix script

In script mode log(...) and debug(...) write to stdout; error(...) writes to stderr. Pipe stdout to a log file while errors still show on the terminal, exactly as you'd expect from a normal CLI tool.

> main[args]
log('processing', len(args), 'items')
[a] in args
    (a == '')
        error('skipping empty arg')
    log(' ->', upper(a))
<< 0

Server state + helper functions

Substrate, minus the page tiers

Top-level > server declarations and > name[params] functions are available to main, exactly like a page handler. Use them to factor a script the same way you'd factor a page: state at the top, helpers below, main at the bottom.

> server
greeting: 'hi'

> shout[s] >> upper(s)

> main[args]
log(shout(greeting), '— args:', args)
<< 0

Services work too

Singletons hold state across calls inside one script run

A > service [name] declared in the script is a singleton for that run. Methods share their state frame; reads from outside the service see the live values. Drop a counter, a registry, or any other "carry state across helpers" piece into a service and call it the usual way.

> service [counter]
n: 0
bump[]
    n << n + 1
    << n

> main[args]
log(counter.bump())   # 1
log(counter.bump())   # 2
<< 0

Forbidden tiers

page / session / local — no client, nothing to react against

Script mode refuses > page, > session, > local declarations at load with a script-mode hint. These tiers exist because a browser is on the other end of the wire; without one, there's nothing to react against. Move shared state to > server or a service.

The two canonical examples live under site/scripts/ in the source tree: hello.rat is the smallest possible script; seed_users.rat shows args parsing, a helper function, a stateful service, and a usage-or-exit path. Run them from the project root with rat run scripts/hello.rat alpha beta and rat run scripts/seed_users.rat alice bob. Or head back to the CLI page for the rest of the subcommand surface.