Rat docs

Services

A service is a named singleton declared in main.rat with its own state and methods. Pages call it as auth.login(...) and every call hits the same underlying frame — perfect for authentication, shopping carts, anything you want a single instance of for the duration of the app.

Declare a service

main.rat

> service [auth] defines the namespace. The block has state lines (user: null) and method definitions, each of which can read and write the state via <<. A method returns with << on its own.

> service [auth]
user: null
token: null

login[u, p]
    user << u
    token << 'tok-' + p
    << true

logout[]
    user << null
    token << null

is_authed[] >> token is not null

Call it from a page

auth.login(...) reads like any other call

Any page (or pup) can call the service methods. Reads of the service's state (auth.user) work too — the page participates in the reactive graph against the service's frame, so a successful login causes every [auth.user] span in the app to repaint.

> page
name: ''
pw: ''
who >> auth.user

<p> signed in as: [who]
<input value[name] on_input[name << event.target.value]>
<input value[pw]   on_input[pw << event.target.value]>
<button on_click[auth.login(name, pw)]> sign in
<button on_click[auth.logout()]>        sign out

Why services over plain functions

State that survives across pages

A top-level > name[] function is stateless — it returns a value and forgets. A service carries state, so auth.user stays around for as long as the app runs. The same mechanism backs database handles (main_db.user.add(...)) and Tail-style namespaces — see the unified dispatch doc for the deeper story.

# stateless: forgets between calls
> add[a, b] >> a + b

# stateful: the singleton remembers
> service [counter]
value: 0
bump[] >> value << value + 1

Live: a counter service

demo_counter is declared in main.rat

Two buttons calling the same service. The displayed value is read from demo_counter.clicks, so the span updates whenever any caller bumps the service — try refreshing the page and the count survives the navigation because the singleton lives in the server frame, not in this page.

# main.rat (already declared)
> service [demo_counter]
clicks: 0
bump[]
    clicks << clicks + 1
reset[]
    clicks << 0

# this page
<p> clicks: [demo_counter.clicks]
<button on_click[demo_counter.bump()]>  +1
<button on_click[demo_counter.reset()]> reset
Result

clicks: 0

That wraps the basics. Head back to the docs index for the curated example list, or dive into the about page for context on how the docs site itself is built.