I'm working on a hypermedia application that I am calling Wave. The idea is that it will be a some combination of rss reader, link log and blogmarking tool. I want to basically use Wave as a dumping ground of interesting content.
I am using the project to learn more about htmx and hypermedia driven applications in general.
I'm currently on the Web 1.0 chapter of Hypermedia Systems and I've already found the first sidequest. I want to use BASIC and my own custom webserver to do this project but I also want to make the application as small as possible. My usual way of building a web project using SERAPHIM is to create a file for each route. This simplifies some things and I think this should be the true solution for most projects.
However for Wave I want to have everything be in just one file. This requires some acrobatics to get everything to work right. I've currently got WAVE.APP program that is able to have multiple routes inside of it. It's still a bit verbose for my taste and I'm trying to cut some of the code down.
This is similar to what I would do in a simple flask project where I only have a routes.py file. I could split the routes out and that probably is good practice but for simple applications it makes more sense to keep everything to one file. I want to make this true for SERAPHIM as well.
Once I have a base, I'll link it here and move on to actually reading the chapter.
I have cut up my WAVE.APP program now into something I think is decently split up. It's superficially small as I moved code into INCLUDE blocks but to be fair that is what flask is doing in essence. It's hiding some of the code in the library to focus on just what matters which is the routes.
I'm doing the same here, I'm not sure if it's a good idea but it is something to try. The big things will be is this understand in the future when I come back to this project.
I'll first show the python program that I'm trying to mimic:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def greet():
return "Hello, World!"
The core of flask is the ability to use decorator functions to do routing and to run a function and return a string for a request.
Now let's look at how I think this will look in BASIC.
WAVE.APP
SUBROUTINE WAVE.APP(MAT REQUEST,MAT RESPONSE)
*
$DEFINE DATABASE.QM
*
$CATALOGUE LOCAL
*
$INCLUDE SERAPHIM.HEADER
*
ROUTES = ''
ROUTES<1,-1> = 'GET /'
*
$INCLUDE SERAPHIM.INIT
*
ON ROUTE.POS GOSUB GET.INDEX
*
RETURN
*
********************* S U B R O U T I N E *********************
*
* GET /
*
GET.INDEX:NULL
*
RESPONSE(RESPONSE.STATUS.ATTRIBUTE) = 200
RESPONSE(RESPONSE.CONTENT.ATTRIBUTE) = 'Hello, 100!'
*
RETURN
*
* END OF PROGRAM
*
END
*
The idea is similar, I build up a list of routes with the method type as part of the string. This ultimately runs an internal subroutine that will return a string for a request.
SERAPHIM.HEADER and SERAPHIM.INIT are doing the heavy lifting here.
SERAPHIM.HEADER
*
EQU TRUE TO 1
EQU FALSE TO 0
*
DIM REQUEST(12)
*
EQU REQUEST.TYPE.ATTRIBUTE TO 1
EQU REQUEST.VERSION.ATTRIBUTE TO 2
EQU REQUEST.URL.ATTRIBUTE TO 3
EQU REQUEST.SLUGS.ATTRIBUTE TO 4
EQU REQUEST.QUERY.ATTRIBUTE TO 5
EQU REQUEST.HEADERS.ATTRIBUTE TO 6
EQU REQUEST.COOKIES.ATTRIBUTE TO 7
EQU REQUEST.RAW.BODY.ATTRIBUTE TO 8
EQU REQUEST.FORM.ATTRIBUTE TO 9
EQU REQUEST.FILES.ATTRIBUTE TO 10
EQU REQUEST.JSON.ATTRIBUTE TO 11
EQU REQUEST.RAW.REQUEST.ATTRIBUTE TO 12
*
DIM RESPONSE(4)
*
EQU RESPONSE.STATUS.ATTRIBUTE TO 1
EQU RESPONSE.HEADERS.ATTRIBUTE TO 2
EQU RESPONSE.CONTENT.ATTRIBUTE TO 3
EQU RESPONSE.CONTENT.TYPE.ATTRIBUTE TO 4
*
This file contains the REQUEST and RESPONSE parameters that are available to a SERAPHIM application.
SERAPHIM.INIT is a purpose built include file for my WAVE.APP application. This may become a standard or it may not.
*
OPEN '','HTML-TEMPLATE-FILE' TO HTML.TEMPLATE.FILE ELSE
RESPONSE(RESPONSE.STATUS.ATTRIBUTE) = 500
RESPONSE(RESPONSE.CONTENT.ATTRIBUTE) = 'Unable to open file: HTML-TEMPLATE-FILE'
RETURN
END
*
* GET SESSION
*
USERNAME = ''
LOGGED.IN = FALSE
*
LOCATE('session_id',REQUEST(REQUEST.COOKIES.ATTRIBUTE)<1>,1;ANYPOS) THEN
SESSION.ID = REQUEST(REQUEST.COOKIES.ATTRIBUTE)<2,ANYPOS>
*
OPEN '','SESSION-FILE' TO SESSION.FILE ELSE LOGGED.IN = FALSE
READ SESSION.ITEM FROM SESSION.FILE,SESSION.ID THEN
LOGGED.IN = TRUE
END
END
*
IF LOGGED.IN THEN
USERNAME = SESSION.ITEM<1>
END
*
* FORMAT URL
*
LOCATE('url',REQUEST(REQUEST.SLUGS.ATTRIBUTE)<1>,1;SLUG.POS) ELSE SLUG.POS = 1
REQUEST.URL = REQUEST(REQUEST.SLUGS.ATTRIBUTE)<2,SLUG.POS>
REQUEST.URL = '/' : REQUEST.URL
*
* ROUTE HANDLER
*
CALL PARSE.ROUTES(REQUEST.URL,ROUTES,ROUTE.POS,MAT REQUEST)
*
IF ROUTE.POS = '' THEN
RESPONSE(RESPONSE.STATUS.ATTRIBUTE) = 404
RESPONSE(RESPONSE.CONTENT.ATTRIBUTE) = 'Page not found.'
RETURN
END
*
This file contains the template file that will contain the templates, it also has the session logic to get a username from a session.
This file sets up the URL so WAVE.APP can be a standalone file.
Finally the most important thing this include does is the route handling. It calls PARSE.ROUTES which will compare the url and method to the list of routes and find the best match. The best match will be returned as ROUTE.POS.
If ROUTE.POS is blank, the application will return a page not found error, otherwise the ROUTE.POS is used to go to the right internal subroutine.
This forms the base of building a simple one file SERAPHIM project.
With that it's time to start actually reading the next chapter.