Dokumentacija

Lastnosti

Avtomati stanj, ki se odzivajo na dogodke, preverjajo pogoje, izvajajo učinke in rišejo zaslon.

Lastnost je avtomat stanj, vezan na entiteto. Našteje stanja; vsako stanje našteje dogodke, ki jih sprejme, kam vsak vodi in kaj se zgodi na poti.

Stanja in prehodi

lolo
1trait Approval -> Expense [interaction, instance] { 2 initial: draft 3 4 state draft { 5 SUBMIT -> submitted when (> @entity.amount 0) 6 (render-ui main { type: typography, content: "Submitted", variant: h4 }) 7 } 8 9 state submitted { 10 APPROVE -> approved when (<= @entity.amount 500) 11 (set @entity.note "Approved") 12 REJECT -> rejected 13 (set @entity.note "Rejected") 14 } 15}
  • EVENT -> target je prehod. Učinki sledijo v svojih vrsticah in se izvedejo po vrsti.
  • when (…) je pogoj: če ne drži, je dogodek zavrnjen in nič se ne spremeni.
  • Prvo stanje je začetno, razen če initial: navede drugega.
  • Vsak prehod stoji sam. Dva prehoda, ki se razlikujeta le v izvornem stanju, se zapišeta dvakrat; nič se ne deli implicitno.
  • Stanje brez izhoda je napaka, razen če naj se program v njem konča.

Oklepaj v glavi vsebuje kategorijo (interaction, lifecycle, integration, …) in instance (en avtomat za vsako vrstico entitete, privzeto) ali collection (en avtomat za celo entiteto).

Tu teče celoten postopek odobritve. Poskusi oddati zahtevek nad 500 EUR in ga odobriti; pogoj ga zavrne:

Avtomat stanj

Approval

on Expense · states: 4 · transitions: 7
emitsMOVED
INIT
RAISE
SUBMIT
(> @entity.amount 0)
APPROVE
(<= @entity.amount 500)
REJECT
NEW_CLAIM
REVISE
nowdraft
submitted
approved
rejected
Teče v živo

Draft claim: EUR

Ista lastnost kot avtomat stanj: vsak okvir je stanje, vsaka puščica dogodek, ki ga premakne, ikone na puščici pa so učinki, ki jih ta dogodek izvede. Z miško pojdite čez simbol in videli boste, kaj naredi. Kako brati AVL

std-orb-docex-expense.lolololo
1app std-orb-docex-expense "1.0.0" 2"std-orb-docex-expense — an approval flow: four states, a guard on the amount, and a rule the shape of the program enforces." 3orbital ExpenseOrbital { 4 entity Expense [runtime] { 5 id : string! 6 amount : number = 120 7 note : string = "" 8 } 9 10 trait Approval -> Expense [interaction, instance] { 11 initial: draft 12 13 state draft { 14 INIT -> draft 15 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 16 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 17 { type: stack, direction: horizontal, gap: sm, children: [ 18 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 19 { type: button, label: "Submit", action: SUBMIT, variant: primary } 20 ] } 21 ] }) 22 RAISE -> draft 23 (emit MOVED { event: "RAISE" }) 24 (set @entity.amount (+ @entity.amount 50)) 25 SUBMIT -> submitted when (> @entity.amount 0) 26 (emit MOVED { event: "SUBMIT" }) 27 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 28 { type: typography, content: (str/concat "Submitted: " @entity.amount " EUR"), variant: h4 }, 29 { type: typography, content: "Claims over 500 EUR cannot be approved here.", variant: small, color: muted }, 30 { type: stack, direction: horizontal, gap: sm, children: [ 31 { type: button, label: "Approve", action: APPROVE, variant: primary }, 32 { type: button, label: "Reject", action: REJECT, variant: secondary } 33 ] } 34 ] }) 35 } 36 37 state submitted { 38 APPROVE -> approved when (<= @entity.amount 500) 39 (emit MOVED { event: "APPROVE" }) 40 (set @entity.note "Approved") 41 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 42 { type: badge, label: "Approved", variant: success }, 43 { type: button, label: "New claim", action: NEW_CLAIM, variant: ghost } 44 ] }) 45 REJECT -> rejected 46 (emit MOVED { event: "REJECT" }) 47 (set @entity.note "Rejected") 48 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 49 { type: badge, label: "Rejected", variant: error }, 50 { type: button, label: "Revise", action: REVISE, variant: primary } 51 ] }) 52 } 53 54 state approved { 55 NEW_CLAIM -> draft 56 (emit MOVED { event: "NEW_CLAIM" }) 57 (set @entity.amount 120) 58 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 59 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 60 { type: stack, direction: horizontal, gap: sm, children: [ 61 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 62 { type: button, label: "Submit", action: SUBMIT, variant: primary } 63 ] } 64 ] }) 65 } 66 67 state rejected { 68 REVISE -> draft 69 (emit MOVED { event: "REVISE" }) 70 (render-ui main { type: stack, direction: vertical, gap: sm, children: [ 71 { type: typography, content: (str/concat "Draft claim: " @entity.amount " EUR"), variant: h4 }, 72 { type: stack, direction: horizontal, gap: sm, children: [ 73 { type: button, label: "Add 50 EUR", action: RAISE, variant: ghost }, 74 { type: button, label: "Submit", action: SUBMIT, variant: primary } 75 ] } 76 ] }) 77 } 78 79 emits { 80 MOVED -> external { 81 event : string! 82 } 83 @description "Each move names the event it took, so the diagram beside the demo can follow it." 84 } 85 } 86 87 page "/expenses" -> Approval 88}

Kaj se zgodi, ko se sproži APPROVE:

submittedAPPROVE
→
approved
GuardThe transition only fires when this check is true.
(<= @entity.amount 500)
@entity.amount500<=
Effects (2)What runs, in order, when the transition fires.
(set @entity.note "Approved")
@entity.note"Approved"set

Učinki

UčinekKaj naredi
(set @entity.f v)Nastavi polje. Edina oblika prirejanja.
(fetch E { … }) / (persist create|update|delete E data)Bere in piše shranjene vrstice.
(emit EVENT { … })Odda navedeni dogodek.
(render-ui slot { … })Nariše drevo vzorcev v režo (main, modal, drawer, toast, …); null jo izprazni.
(navigate "/path") / (navigate-back)Premik na drugo stran.
(notify info "Saved")Pokaže obvestilo.
(call-service provider action { … })Pokliče integracijo (jezikovni model, e-pošta, plačila, …).

Operatorji računajo vrednosti: (+ a b), (str/concat …), (array/map xs (fn x …)). Vsi so predponski; infiksnih ni. Referenca operatorjev našteje vsakega.

Vezave

ZnakPomen
@entity.fieldPolje vezane entitete
?fieldPolje tovora prihajajočega dogodka
@config.keyVrednost iz config lastnosti
@user.fieldPrijavljeni obiskovalec
now / stateČasovni žig te obdelave / trenutno stanje

Oddajanje in poslušanje

Lastnost navede, kaj oddaja, druga pa, na kaj se odziva. Tu gumb oddaja TARGET_CHANGED, stanje pa ga posluša in preslika v svoj dogodek SHOW:

Avtomat stanj

Dial

on Thermostat · states: 1 · transitions: 3
emitsTARGET_CHANGEDemitsMOVED
INIT
UP
(< @entity.target 28)
DOWN
(> @entity.target 16)
nowidle

Status

on Thermostat · states: 1 · transitions: 2
listensTARGET_CHANGEDemitsMOVED
INIT
SHOW
nowwatching
Teče v živo

21 °C

Set a temperature between 16 and 28 °C.

std-orb-docex-thermostat.lolololo
1app std-orb-docex-thermostat "1.0.0" 2"std-orb-docex-thermostat — two traits in one orbital: one guards and emits, the other listens and reacts." 3orbital ThermostatOrbital { 4 entity Thermostat [runtime, shared] { 5 id : string! 6 target : number = 21 7 } 8 9 trait Dial -> Thermostat [interaction, instance] { 10 initial: idle 11 12 state idle { 13 INIT -> idle 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: button, label: "Colder", action: DOWN, variant: secondary }, 16 { type: typography, content: (str/concat @entity.target " °C"), variant: h2 }, 17 { type: button, label: "Warmer", action: UP, variant: secondary } 18 ] }) 19 UP -> idle when (< @entity.target 28) 20 (emit MOVED { event: "UP" }) 21 (set @entity.target (+ @entity.target 1)) 22 (emit TARGET_CHANGED { target: @entity.target }) 23 DOWN -> idle when (> @entity.target 16) 24 (emit MOVED { event: "DOWN" }) 25 (set @entity.target (- @entity.target 1)) 26 (emit TARGET_CHANGED { target: @entity.target }) 27 } 28 29 emits { 30 TARGET_CHANGED { 31 target : number! 32 } 33 @description "The target temperature moved." 34 MOVED -> external { 35 event : string! 36 } 37 @description "Each move names the event it took, so the diagram beside the demo can follow it." 38 } 39 } 40 41 trait Status -> Thermostat [interaction, instance] { 42 initial: watching 43 44 state watching { 45 INIT -> watching 46 (render-ui main { type: typography, content: "Set a temperature between 16 and 28 °C.", variant: body, color: muted }) 47 SHOW -> watching 48 (emit MOVED { event: "SHOW" }) 49 (render-ui main { type: badge, label: (if (> ?target 23) "Heating up" (if (< ?target 19) "Cooling down" "Comfortable")), variant: (if (> ?target 23) warning (if (< ?target 19) info success)) }) 50 } 51 52 emits { 53 MOVED -> external { 54 event : string! 55 } 56 @description "Each move names the event it took, so the diagram beside the demo can follow it." 57 } 58 59 listens { 60 Dial.TARGET_CHANGED -> SHOW 61 SHOW { 62 target : number! 63 } 64 } 65 } 66 67 page "/thermostat" -> Dial, Status 68}

Kaj se zgodi, ko se sproži UP:

idleUP
→
idle
GuardThe transition only fires when this check is true.
(< @entity.target 28)
@entity.target28<
Effects (2)What runs, in order, when the transition fires.
(set @entity.target (+ @entity.target 1))
@entity.target@entity.target1+set
(emit "TARGET_CHANGED" { target: @entity.target })
"TARGET_CHANGED"@entity.targettarget{ }emit
lolo
1emits { 2 TARGET_CHANGED { 3 target : number! 4 } 5} 6 7listens { 8 Dial.TARGET_CHANGED -> SHOW 9 SHOW { 10 target : number! 11 } 12}

Dogodki internal (privzeto) ostanejo v orbitalu; dogodki external prestopijo v druge orbitale in morajo navesti tovor. Trait.EVENT se razreši znotraj orbitala poslušalca; med orbitali pišeš Orbital.Trait.EVENT. Dogodki med orbitali pokažejo oboje.

Tiki

Tik je periodični učinek. Teče le, dokler drži njegov when, zato se tik, vezan na stanje, ustavi takoj, ko lastnost stanje zapusti:

Avtomat stanj

Timing

on Stopwatch · states: 2 · transitions: 4
emitsMOVED
INIT
START
RESET
STOP
nowstopped
running
Teče v živo

s

std-orb-docex-stopwatch.lolololo
1app std-orb-docex-stopwatch "1.0.0" 2"std-orb-docex-stopwatch — a tick: a periodic effect that runs only while the trait is in the running state." 3orbital StopwatchOrbital { 4 entity Stopwatch [runtime] { 5 id : string! 6 seconds : number = 0 7 } 8 9 trait Timing -> Stopwatch [interaction, instance] { 10 initial: stopped 11 12 state stopped { 13 INIT -> stopped 14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 15 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 16 { type: button, label: "Start", action: START, variant: primary }, 17 { type: button, label: "Reset", action: RESET, variant: ghost } 18 ] }) 19 START -> running 20 (emit MOVED { event: "START" }) 21 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 22 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 23 { type: button, label: "Stop", action: STOP, variant: secondary } 24 ] }) 25 RESET -> stopped 26 (emit MOVED { event: "RESET" }) 27 (set @entity.seconds 0) 28 } 29 30 state running { 31 STOP -> stopped 32 (emit MOVED { event: "STOP" }) 33 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [ 34 { type: typography, content: (str/concat @entity.seconds " s"), variant: h2 }, 35 { type: button, label: "Start", action: START, variant: primary }, 36 { type: button, label: "Reset", action: RESET, variant: ghost } 37 ] }) 38 } 39 40 emits { 41 MOVED -> external { 42 event : string! 43 } 44 @description "Each move names the event it took, so the diagram beside the demo can follow it." 45 } 46 47 ticks { 48 second every 1s when (= state "running") 49 (set @entity.seconds (+ @entity.seconds 1)) 50 } 51 } 52 53 page "/stopwatch" -> Timing 54}

Kaj se zgodi, ko se sproži START:

stoppedSTART
→
running
Effects (1)What runs, in order, when the transition fires.
lolo
1 2 3 4 ▼ticks { second every 1s when (= state "running") (set @entity.seconds (+ @entity.seconds 1)) }

Intervali so milisekunde, trajanje (5s, 1m, 1h, 1d) ali niz cron. Tik na shranjeni entiteti teče na strežniku, dokler ima lastnost odprta neka stran; z oznako [background] teče tudi, ko nihče ne gleda.

Nastavitve

config { pageSize : int = 10 @label "Items per page" } navede gumb, ki ga lastnost bere kot @config.pageSize. Gumbi so tisto, kar sme mesto klica spremeniti, ko lastnost uvozi; glej sestavljanje.

© 2026 Almadar. Ljubljana, Slovenija.
Not reported here