2024-12-03

syntactic sugar for Wapp


From time to time I use the Wapp framework for some of my projects.
This little framework helps when writing web applications in TCL and is just 1 single file with about 1030 lines of code.

I really love the simplicity and resilience of the TCL programming language.
It's there for over 30 years now but you can still use it on old and new computers.
So I used the power of TCL to give me some syntactic sugar when using Wapp.

Usually you can define routes in Wapp like this:
proc wapp-page-hello {} {
wapp-trim {
# Content to deliver for page hello
}
}

But then you have to parse the HTTP method out of the variable "wapp-param REQUEST_METHOD" if you want to react on different HTTP methods like GET and POST.

I wrote some helpers to handle this:
set routes [list]

proc sort-route {} {
global routes
set routes [lsort -index 1 $routes]
}

proc setup-route {method path code} {
global routes
set routes [linsert $routes end [list $method $path $code]]
set procname "wapp-page-$method-[join [split $path /] -]"
eval "proc $procname {} {$code}"
sort-route
}

proc GET {path code} {
setup-route GET $path $code
}

proc POST {path code} {
setup-route POST $path $code
}

# Custom hook prior to dispatch of the HTTP request handler:
proc wapp-before-dispatch-hook {} {
global routes
foreach {route} $routes {
set method [lindex $route 0]
set path [lindex $route 1]
if {[wapp-param REQUEST_METHOD] != $method} continue
set notequal 0
# Compare route with current request:
foreach a [split [wapp-param PATH_INFO] /] b [split $path /] {
# Parse named route parameters out of the path to capture their values:
if [regexp {:([^:]+)} $b match var] {
wapp-set-param PATH_PARAMS [dict create $var $a]
} else {
if {$a != $b} {
set notequal 1
break
}
}
}
if {$notequal eq 1} continue
wapp-set-param PATH_HEAD "$method-[string map {/ -} $path]"
break
}
}


After including my helpers I can define routes like this:
GET /hello {
wapp-trim {
# Content to deliver after a GET request
}
}

GET /hello/:name {
set name [dict get [wapp-param PATH_PARAMS] name]
# Do something with name:
wapp-trim {
Hello %html($name)!
}
}

POST /hello {
wapp-trim {
# Content to deliver after a POST request
}
}


And if the syntax reminds you of the Node.js framework express, then this is not a coincidence.
I used it for a very long time but now I am on a quest to find simpler solutions.