Extend defhandler with :path/:method/:csrf, migrate 12 ref endpoints to SX
defhandler now supports keyword options for public route registration: (defhandler name :path "/..." :method :post :csrf false (&key) body) Infrastructure: forms.sx parses options, HandlerDef stores path/method/csrf, register_route_handlers() mounts path-based handlers as app routes. New IO primitives (boundary.sx "Web interop" section): now, sleep, request-form, request-json, request-header, request-content-type. First migration: 12 reference API endpoints from Python f-string SX to declarative .sx handlers in sx/sx/handlers/ref-api.sx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
156
sx/sx/handlers/ref-api.sx
Normal file
156
sx/sx/handlers/ref-api.sx
Normal file
@@ -0,0 +1,156 @@
|
||||
;; Reference API endpoints — live demos for hypermedia attribute docs
|
||||
;;
|
||||
;; These replace the Python endpoints in bp/pages/routes.py.
|
||||
;; Each defhandler with :path registers as a public route automatically.
|
||||
|
||||
;; --- sx-get demo: server time ---
|
||||
|
||||
(defhandler ref-time
|
||||
:path "/geography/hypermedia/reference/api/time"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((now (now "%H:%M:%S")))
|
||||
(<>
|
||||
(span :class "text-stone-800 text-sm" "Server time: " (strong now))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-get"
|
||||
:text (str "(span :class \"text-stone-800 text-sm\" \"Server time: \" (strong \"" now "\"))")))))
|
||||
|
||||
;; --- sx-post demo: greet ---
|
||||
|
||||
(defhandler ref-greet
|
||||
:path "/geography/hypermedia/reference/api/greet"
|
||||
:method :post
|
||||
:csrf false
|
||||
(&key)
|
||||
(let ((name (request-form "name" "stranger")))
|
||||
(<>
|
||||
(span :class "text-stone-800 text-sm" "Hello, " (strong name) "!")
|
||||
(~doc-oob-code :target-id "ref-wire-sx-post"
|
||||
:text (str "(span :class \"text-stone-800 text-sm\" \"Hello, \" (strong \"" name "\") \"!\")")))))
|
||||
|
||||
;; --- sx-put demo: status update ---
|
||||
|
||||
(defhandler ref-status
|
||||
:path "/geography/hypermedia/reference/api/status"
|
||||
:method :put
|
||||
:csrf false
|
||||
(&key)
|
||||
(let ((status (request-form "status" "unknown")))
|
||||
(<>
|
||||
(span :class "text-stone-700 text-sm" "Status: " (strong status) " — updated via PUT")
|
||||
(~doc-oob-code :target-id "ref-wire-sx-put"
|
||||
:text (str "(span :class \"text-stone-700 text-sm\" \"Status: \" (strong \"" status "\") \" — updated via PUT\")")))))
|
||||
|
||||
;; --- sx-patch demo: theme ---
|
||||
|
||||
(defhandler ref-theme
|
||||
:path "/geography/hypermedia/reference/api/theme"
|
||||
:method :patch
|
||||
:csrf false
|
||||
(&key)
|
||||
(let ((theme (request-form "theme" "unknown")))
|
||||
(<>
|
||||
theme
|
||||
(~doc-oob-code :target-id "ref-wire-sx-patch"
|
||||
:text (str "\"" theme "\"")))))
|
||||
|
||||
;; --- sx-delete demo ---
|
||||
|
||||
(defhandler ref-delete-item
|
||||
:path "/geography/hypermedia/reference/api/item/<item_id>"
|
||||
:method :delete
|
||||
:csrf false
|
||||
(&key)
|
||||
(<>
|
||||
(~doc-oob-code :target-id "ref-wire-sx-delete" :text "\"\"")))
|
||||
|
||||
;; --- sx-trigger demo: search ---
|
||||
|
||||
(defhandler ref-trigger-search
|
||||
:path "/geography/hypermedia/reference/api/trigger-search"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((q (request-arg "q" "")))
|
||||
(let ((sx-text (if (= q "")
|
||||
"(span :class \"text-stone-400 text-sm\" \"Start typing to trigger a search.\")"
|
||||
(str "(span :class \"text-stone-800 text-sm\" \"Results for: \" (strong \"" q "\"))"))))
|
||||
(<>
|
||||
(if (= q "")
|
||||
(span :class "text-stone-400 text-sm" "Start typing to trigger a search.")
|
||||
(span :class "text-stone-800 text-sm" "Results for: " (strong q)))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-trigger" :text sx-text)))))
|
||||
|
||||
;; --- sx-swap demo ---
|
||||
|
||||
(defhandler ref-swap-item
|
||||
:path "/geography/hypermedia/reference/api/swap-item"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((now (now "%H:%M:%S")))
|
||||
(<>
|
||||
(div :class "text-sm text-violet-700" (str "New item (" now ")"))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-swap"
|
||||
:text (str "(div :class \"text-sm text-violet-700\" \"New item (" now ")\")")))))
|
||||
|
||||
;; --- sx-swap-oob demo ---
|
||||
|
||||
(defhandler ref-oob
|
||||
:path "/geography/hypermedia/reference/api/oob"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((now (now "%H:%M:%S")))
|
||||
(<>
|
||||
(span :class "text-emerald-700 text-sm" "Main updated at " now)
|
||||
(div :id "ref-oob-side" :sx-swap-oob "innerHTML"
|
||||
(span :class "text-violet-700 text-sm" "OOB updated at " now))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-swap-oob"
|
||||
:text (str "(<> (span ... \"" now "\") (div :id \"ref-oob-side\" :sx-swap-oob \"innerHTML\" ...))")))))
|
||||
|
||||
;; --- sx-select demo ---
|
||||
|
||||
(defhandler ref-select-page
|
||||
:path "/geography/hypermedia/reference/api/select-page"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((now (now "%H:%M:%S")))
|
||||
(<>
|
||||
(div :id "the-header" (h3 "Page header — not selected"))
|
||||
(div :id "the-content"
|
||||
(span :class "text-emerald-700 text-sm"
|
||||
"This fragment was selected from a larger response. Time: " now))
|
||||
(div :id "the-footer" (p "Page footer — not selected"))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-select"
|
||||
:text (str "(<> (div :id \"the-header\" ...) (div :id \"the-content\" ... \"" now "\") (div :id \"the-footer\" ...))")))))
|
||||
|
||||
;; --- sx-sync demo: slow echo ---
|
||||
|
||||
(defhandler ref-slow-echo
|
||||
:path "/geography/hypermedia/reference/api/slow-echo"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((q (request-arg "q" "")))
|
||||
(sleep 800)
|
||||
(<>
|
||||
(span :class "text-stone-800 text-sm" "Echo: " (strong q))
|
||||
(~doc-oob-code :target-id "ref-wire-sx-sync"
|
||||
:text (str "(span :class \"text-stone-800 text-sm\" \"Echo: \" (strong \"" q "\"))")))))
|
||||
|
||||
;; --- sx-prompt demo ---
|
||||
|
||||
(defhandler ref-prompt-echo
|
||||
:path "/geography/hypermedia/reference/api/prompt-echo"
|
||||
:method :get
|
||||
(&key)
|
||||
(let ((name (request-header "SX-Prompt" "anonymous")))
|
||||
(<>
|
||||
(span :class "text-stone-800 text-sm" "Hello, " (strong name) "!")
|
||||
(~doc-oob-code :target-id "ref-wire-sx-prompt"
|
||||
:text (str "(span :class \"text-stone-800 text-sm\" \"Hello, \" (strong \"" name "\") \"!\")")))))
|
||||
|
||||
;; --- Error demo ---
|
||||
|
||||
(defhandler ref-error-500
|
||||
:path "/geography/hypermedia/reference/api/error-500"
|
||||
:method :get
|
||||
(&key)
|
||||
(abort 500 "Server error"))
|
||||
Reference in New Issue
Block a user