From 8a4759ccf4b78dd5d841d412c9361669c39713fe Mon Sep 17 00:00:00 2001 From: natalie roentgen connolly Date: Sat, 5 Jul 2025 03:58:24 -0700 Subject: [PATCH] add code for arpa web n-gon --- srv/content/arpa_n_gon.hy | 57 ++++++++++++++++++ srv/content/file_io.hy | 7 ++- srv/content/router.hy | 48 +++++++++++++-- srv/http_utils/request.hy | 3 +- www/src/assets/arpa-n-gon.css | 25 ++++++++ www/src/assets/style.css | 10 ++++ www/src/data/arpa-n-gon-linking-anchors.html | 3 + www/src/data/arpa-n-gon-linking-iframe.html | 1 + www/src/data/arpa-n-gon.yaml | 5 ++ www/src/pages/arpa-n-gon-nav.hy | 8 +++ www/src/pages/arpa-n-gon.hy | 62 ++++++++++++++++++++ www/src/templates/footer.hy | 6 +- 12 files changed, 225 insertions(+), 10 deletions(-) create mode 100644 srv/content/arpa_n_gon.hy create mode 100644 www/src/assets/arpa-n-gon.css create mode 100644 www/src/data/arpa-n-gon-linking-anchors.html create mode 100644 www/src/data/arpa-n-gon-linking-iframe.html create mode 100644 www/src/data/arpa-n-gon.yaml create mode 100644 www/src/pages/arpa-n-gon-nav.hy create mode 100644 www/src/pages/arpa-n-gon.hy diff --git a/srv/content/arpa_n_gon.hy b/srv/content/arpa_n_gon.hy new file mode 100644 index 0000000..d5d8dba --- /dev/null +++ b/srv/content/arpa_n_gon.hy @@ -0,0 +1,57 @@ +(import yaml [safe-load]) + +(setv ones-names [ + "hena" + "di" + "tri" + "tetra" + "penta" + "hexa" + "hepta" + "octa" + "ennea"]) + +(setv tens-names [ + "decagon" + "icosi" + "triconta" + "tetraconta" + "pentaconta" + "hexaconta" + "heptaconta" + "octaconta" + "enneaconta"]) + +(defn n-gon-name [n] + (when (> 10 n) + (return (+ (get ones-names (- n 1)) "gon"))) + + (when (= n 11) + (return "undecagon")) + + (when (= n 12) + (return "dodecagon")) + + (when (> 20 n) + (return (+ (get ones-names (- n 1)) "decagon"))) + + (when (= 20 n) + (return "icosagon")) + + (let [[modc r] (divmod n 10)] + (+ (get tens-names (- modc 1)) "kai" (get ones-names (- r 1)) "gon"))) + +(defn get-members [] + (with [f (open "www/src/data/arpa-n-gon.yaml" "r")] + (return (get (safe-load f) "members")))) + +(defn get-member-by-domain [members domain] + (for [[idx member] (enumerate members)] + (when (= (get member "arpa-domain") domain) + (return #(idx member))))) + +(defn next-member [members domain] + (get members (% (+ (get (get-member-by-domain members domain) 0) 1) (len members)))) + +(defn prev-member [members domain] + (get members (% (- (get (get-member-by-domain members domain) 0) 1) (len members)))) diff --git a/srv/content/file_io.hy b/srv/content/file_io.hy index d11f4ca..e04f3ba 100644 --- a/srv/content/file_io.hy +++ b/srv/content/file_io.hy @@ -11,13 +11,16 @@ (. (check-output (.group sequence 1) :shell True :executable "/bin/bash" :env hy-env) (decode) (strip))) data)) -(defn parse-html-file [path #** kwargs] +(defn parse-html-file [path [no-exec False] #** kwargs] (with [f (open path "r")] (setv data (.read f))) (for [[k v] (.items kwargs)] (setv data (.replace data f"{"{"}{k}{"}"}" (str v)))) - + + (when no-exec + (return data)) + (execute-bash data)) (defn send-raw-file [path] diff --git a/srv/content/router.hy b/srv/content/router.hy index 8c23a4e..5830ff2 100644 --- a/srv/content/router.hy +++ b/srv/content/router.hy @@ -4,6 +4,7 @@ (import hyrule.collections [assoc]) (import content.file-io [parse-html-file send-raw-file]) (import content.comments [create-comment]) +(import content.arpa-n-gon :as arpa-n-gon) (import re) (import functools [lru-cache]) (import os.path [isdir :as dir? isfile :as file? abspath]) @@ -81,15 +82,36 @@ :if (setx value (. request (get "route") (get "parameters") (get param None))) #(param value))))))) -(defn shtml-file-response [file [code 200] [template-params {}]] +(defn require-params [#* params [otherwise (fn [#* _] (error 409 "missing required parameters"))]] + (fn [f] + (fn [method post request #** kwargs] + (if (all (lfor param params (in param (. request (get "route") (get "parameters") (keys))))) + (f method post request #** kwargs) + (otherwise method post request #** kwargs))))) + +(defn 303-if-not-arpa [f] + (fn [method path request] + (if (. request (get "headers") (get "Host") (endswith "arpa")) + (f method path request) + (dict + :code 303 + :headers {"Location" f"http://natalieee.net.8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa/{(. request (get "route") (get "unparsed_route"))}"})))) + +(defn shtml-file-response [file [code 200] [no-exec False] [template-params {}]] (dict :code code :headers {"Connection" "keep-alive" "Keep-Alive" "timeout=5 max=200"} - :body (parse-html-file f"./www/site/html/{file}" #** template-params))) + :body (parse-html-file f"./www/site/html/{file}" :no-exec no-exec #** template-params))) (defn raw-file-response [file [code 200]] (dict :code code #** (dict (zip ["headers" "body"] (send-raw-file f"./www/site/{file}"))))) +(defn+ match-request [{method "method" {path "path"} "route" :as request}] + ;; (try + ((get (routes.get-route-by-path path) method) method path request)) + ;; (except [Exception] + ;; (return (error 500 "server error"))))) + (defn [(route "/" "GET")] /home #route-args (shtml-file-response "home.html")) (defn [(route "/html/*" "GET") (if-file-exists :base-path "./www/site/html" :otherwise (error 404 "not found"))] /html/* #route-args (shtml-file-response path)) (defn [lru-cache (route "/assets/*" "GET") (if-file-exists :base-path "./www/site/" :otherwise (error 404 "not found"))] /assets/* #route-args (raw-file-response path)) @@ -97,6 +119,22 @@ (defn [(route "/comment" "POST")] /comments #route-args (create-comment request)) (defn [lru-cache (route "/robots.txt" "GET") ] /robots #route-args (dict :code 200 :headers {"Content-Type" "text/plain"} :body "User-agent *\nDisallow: /\n")) -(defn+ match-request [{method "method" {path "path"} "route" :as request}] - ((get (routes.get-route-by-path path) method) method path request)) - +;; *.arpa web n-gon +(setv members (arpa-n-gon.get-members)) + +(defn [(route "/arpa-n-gon" GET) 303-if-not-arpa] /arpa-n-gon #route-args (shtml-file-response "arpa-n-gon.html" :template-params (dict :n_gon (arpa-n-gon.n-gon-name (len members)) :n_gon_inc (arpa-n-gon.n-gon-name (+ (len members) 1)) :n (len members)))) + +(defn [(route "/arpa-n-gon/nav" GET) 303-if-not-arpa (forward-params "current" "style") (require-params "current") ] /arpa-n-gon/nav #route-args [current [style None]] + (shtml-file-response "arpa-n-gon-nav.html" :no-exec True :template-params (dict + :style style + :next (+ "http://" (get (arpa-n-gon.next-member members current) "arpa-domain")) + :prev (+ "http://" (get (arpa-n-gon.prev-member members current) "arpa-domain")) + :n_gon (arpa-n-gon.n-gon-name (len members))))) + +(defn [(route "/arpa-n-gon/next" GET) 303-if-not-arpa (forward-params "current" )(require-params "current")] /arpa-n-gon/next #route-args [current] (dict + :code 303 + :headers {"Location" (+ "http://" (get (arpa-n-gon.next-member members current) "arpa-domain"))})) + +(defn [(route "/arpa-n-gon/prev" GET) 303-if-not-arpa (forward-params "current") (require-params "current")] /arpa-n-gon/next #route-args [current] (dict + :code 303 + :headers {"Location" (+ "http://" (get (arpa-n-gon.prev-member members current) "arpa-domain"))})) diff --git a/srv/http_utils/request.hy b/srv/http_utils/request.hy index 8fbe7f7..360370f 100644 --- a/srv/http_utils/request.hy +++ b/srv/http_utils/request.hy @@ -43,7 +43,8 @@ segments)) - :parameters (parse-url-encoded query)))) + :parameters (parse-url-encoded query) + :unparsed-route route))) (assoc "version" http-version) (assoc "headers" (hashable-dict diff --git a/www/src/assets/arpa-n-gon.css b/www/src/assets/arpa-n-gon.css new file mode 100644 index 0000000..4adb060 --- /dev/null +++ b/www/src/assets/arpa-n-gon.css @@ -0,0 +1,25 @@ +:root { + --link: #33b1ff; + --visited-link: #be95ff; +} + +body { + margin: 0; + display: flex; + + div { + display: flex; + gap: 1ch; + font-size: 13px; + font-family: 'Liberation Mono', monospace; + + > a { + text-underline-offset: 2px; + color: var(--link); + + &:visited { + color: var(--visited-link); + } + } + } +} diff --git a/www/src/assets/style.css b/www/src/assets/style.css index a0527db..6841903 100644 --- a/www/src/assets/style.css +++ b/www/src/assets/style.css @@ -191,6 +191,9 @@ body { justify-content: flex-start; > footnote > * { margin-top: 2px; } + > footnote > iframe { + border: none; + } > a { margin: 0 !important; @@ -415,3 +418,10 @@ div.thought-list{ } } } + +.arpa-n-gon-list { + > li { + background: var(--alt-bg); + margin: 10px; + } +} diff --git a/www/src/data/arpa-n-gon-linking-anchors.html b/www/src/data/arpa-n-gon-linking-anchors.html new file mode 100644 index 0000000..7fb76b2 --- /dev/null +++ b/www/src/data/arpa-n-gon-linking-anchors.html @@ -0,0 +1,3 @@ +prev +*.arpa web n-gon +next diff --git a/www/src/data/arpa-n-gon-linking-iframe.html b/www/src/data/arpa-n-gon-linking-iframe.html new file mode 100644 index 0000000..7d3d65b --- /dev/null +++ b/www/src/data/arpa-n-gon-linking-iframe.html @@ -0,0 +1 @@ + diff --git a/www/src/data/arpa-n-gon.yaml b/www/src/data/arpa-n-gon.yaml new file mode 100644 index 0000000..18a448b --- /dev/null +++ b/www/src/data/arpa-n-gon.yaml @@ -0,0 +1,5 @@ +members: + - name: natalie + arpa-domain: natalieee.net.8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa + description: natalieee.net arpa domain + diff --git a/www/src/pages/arpa-n-gon-nav.hy b/www/src/pages/arpa-n-gon-nav.hy new file mode 100644 index 0000000..3b558d7 --- /dev/null +++ b/www/src/pages/arpa-n-gon-nav.hy @@ -0,0 +1,8 @@ +'(html + (head + (link (:rel stylesheet :href "{style}" :type text/css))) + (body + (div + (a (:href "{prev}" :target _parent) <-) + (a (:href "http://natalieee.net.8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa/arpa-n-gon" :target _parent) "*.arpa web {n_gon}") + (a (:href "{next}" :target _parent) ->)))) diff --git a/www/src/pages/arpa-n-gon.hy b/www/src/pages/arpa-n-gon.hy new file mode 100644 index 0000000..80edafc --- /dev/null +++ b/www/src/pages/arpa-n-gon.hy @@ -0,0 +1,62 @@ +(page + :title ".arpa web {n_gon}" + :description "the homepage for the .arpa web {n_gon}" + `((section + (h1 "the *.arpa web {n_gon}") + (p "a web {n_gon} (read: webring with {n} members) for anyone with a domain under the arpa tld or any subdomain thereof.")) + + (section + (h2 "members") + (ol (:class "arpa-n-gon-list") + ~(do + (import yaml [safe-load]) + (with [f (open "data/arpa-n-gon.yaml")] + (setv members (get (safe-load f) "members"))) + + (hy.models.Expression (lfor member members + `(li (div + (div "member: " (a (:href ~(get member "arpa-domain") :title ~(get member "arpa-domain")) ~(get member "name"))) + (div "domain: " ~(get member "arpa-domain") + (div "description: " ~(get member "description")))))))))) + + (section + (h2 "joining") + (p "anyone meeting all of the following criteria is permitted to join this webring:") + (ul + (li "own a domain under the arpa tld") + (li "have a site accessible under that domain") + (li "on that site, have at least one instance of page elements complying with the section on acceptable notation of membership") + (li "be deemed by natalie to be generally acceptable; this is a low bar, and functionally translates to "don't be an asshole"")) + + (p "should $READER align with the above conditions, and is interested in joining this web {n_gon} (thus making it a web {n_gon_inc}), $READER may send an e-mail containing their website's url to " + (a (:href "mailto:{n_gon}@8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa") "{n_gon}@8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa"))) + + (section + (h2 "acceptable notation of membership") + (p "there are two main modes of noting one's affiliation with this web {n_gon}, what is appropriate for $READER's website will depend on their preferences regarding loading of external resources.") + + (br) + (h3 "1: iframe") + (p "the first option is to include an iframe linking to the navigation for the webring. this option is funnier, because requesting that natalieee.net renders the iframe contents allows + this server to return html containing the appropriate n-gon for the current member count. in order to conform with the css of some site, a style param can be passed in the iframe's src attribute, + which will get loaded as a stylesheet.") + + (figure + (figcaption "iframe web {n_gon} html") + (code ~(run #[[syntax-hl data/arpa-n-gon-linking-iframe.html]]))) + + (br) + (h3 "2: anchors") + (p "the second option is to include various anchor tags. this option is less funny, because instead of asking natalieee.net for the appropriate n-gon, it simply labels the web {n_gon} as a web n-gon.") + + (figure + (figcaption "anchor web {n_gon} html") + (code ~(run #[[syntax-hl data/arpa-n-gon-linking-anchors.html]]))) + + (p "of the above methods, one must appear anywhere within a site a number of times greater than 0. a valid website can include one of these + only once (on a webring page, should the site have a page dedicated to that), or on each page as with this site's footer.") + + (br) + (h3 "proper linking") + (p "the `current` parameter in any given web {n_gon} related url should be set to the domain of one's site. in this one's case, this parameter gets set to natalieee.net.8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa. this is + the mechanism that allows the server to properly link to the next or previous site in the {n_gon}.")))) diff --git a/www/src/templates/footer.hy b/www/src/templates/footer.hy index be4cfa0..321970d 100644 --- a/www/src/templates/footer.hy +++ b/www/src/templates/footer.hy @@ -15,8 +15,10 @@ (div (footnote (:style "margin-top: 5px") "▖▖▖▖▘▖▖▖▖▘▌▖▖▖▘") (footnote (:style "margin-top: 5px") - (div (:style "flex-direction: row; width: 14ch; gap: 2ch; margin-right: 5px;") + (div (:style "display: flex; flex-direction: row; width: 14ch; gap: 1ch; margin-right: 5px;") (a (:href "https://stellophiliac.github.io/roboring/0x6e6174/previous") "<-") (a (:href "https://stellophiliac.github.io/roboring") "roboring") - (a (:href "https://stellophiliac.github.io/roboring/0x6e6174/next") "->")))) + (a (:href "https://stellophiliac.github.io/roboring/0x6e6174/next") "->")) + + (iframe (:src "/arpa-n-gon/nav?current=natalieee.net.8.f.9.e.0.7.4.0.1.0.0.2.ip6.arpa&style=/assets/arpa-n-gon.css")))) (span ~(run "git log -1 --format=%h"))))