aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuchen Pei <id@ypei.org>2023-10-15 00:18:54 +1100
committerYuchen Pei <id@ypei.org>2023-10-15 00:18:54 +1100
commit40d24a500b6a9062122147be332a41fe48a8371a (patch)
treed6bba0e4449c415383d295154fdec40910cf1343
parent098928e78521f01ae08656c0979a0e27d5178933 (diff)
Unify emacs packages with webapps: use bom as an exampleemacspackage2webapp
Emacs commands: bom; bom-state Paths in webapps: /bom; /bom-state/<state> Next step: generalise the transformation with macros etc.
-rw-r--r--bom.el105
1 files changed, 89 insertions, 16 deletions
diff --git a/bom.el b/bom.el
index 06b9784..d70226d 100644
--- a/bom.el
+++ b/bom.el
@@ -125,25 +125,62 @@ Used as parentfn for hierarchy."
""))
(defun bom-format-areas-org (areas)
- "Format hierarchy of AREAS with forecasts.
+ "Format hierarchy of AREAS with forecasts into an org string.
-We use the description of the first root as the state name."
+We use the description of the first root as the state name.
+
+The org string is ready to be inserted into an empty buffer for
+display or export."
(let ((state-name (dom-attr (car (hierarchy-roots areas)) 'description)))
- (format "#+title: %s weather forecast\n\n%s"
+ (format "#+title: %s weather forecast
+
+[[elisp:(bom)][Back]]
+
+%s"
state-name
(string-join
(hierarchy-map 'bom-format-area areas 1)
"\n"))))
+(defun bom-to-path (sexp)
+ "Given an SEXP, return the corresponding path.
+
+For example, (foo \"bar\" \"baz\") is converted to /foo/bar/baz."
+ (pcase-let ((`(,func . ,args) (read sexp)))
+ (unless (and (symbolp func)
+ (memq func '(bom bom-state))
+ (every 'stringp args))
+ (user-error "Invalid sexp: %s" sexp))
+ (format "/%s"
+ (string-join (cons (format "%s" func) args) "/"))))
+
+(defun bom-elisp-link-export (link desc format)
+ "An :export function for elisp: links.
+
+Takes a LINK with description DESC, assuming FORMAT is html,
+output the http path."
+ (cl-assert (eq format 'html))
+ (format "<a href=\"%s\">%s</a>"
+ (bom-to-path link)
+ (or desc link)))
+
(defun bom-org-to-html (s)
"Export org string S to html and return the html string."
(with-temp-buffer
(insert s)
- (let ((org-html-postamble))
- (org-export-as 'html))))
+ (let ((org-html-postamble)
+ (org-link-parameters
+ (cons '("elisp" :export bom-elisp-link-export)
+ org-link-parameters)))
+ (let ((result
+ (org-export-as 'html)))
+ (pop org-link-parameters)
+ result))))
(defun bom-format-areas-html (areas)
- "Format hierarchy of AREAS with forecasts to html."
+ "Format hierarchy of AREAS with forecasts to an html string.
+
+The html string can be sent as a web server response."
(bom-org-to-html (bom-format-areas-org areas)))
(defun bom-get-forecasts (state)
@@ -156,26 +193,62 @@ We use the description of the first root as the state name."
(hierarchy-sort areas 'bom-area-lessp)
areas))
-(defun bom-format-state-lists-org ()
+(defun bom-state-org (state)
+ (bom-format-areas-org
+ (bom-get-forecasts state)))
+
+(defun bom-org ()
+ "Format the \"landing page\" org string.
+
+The string is ready for display or export."
(format
- "#+title: Australia weather forecast\n%s"
+ "#+title: Australia weather forecast
+
+%s"
(mapconcat
- (lambda (pair) (format "[[file:%s][%s]]"
+ (lambda (pair) (format "[[elisp:(bom-state \"%s\")][%s]]"
(car pair)
(upcase (format "%s" (car pair)))))
bom-state-files
" ")))
-(defun bom-format-state-lists-html ()
- (bom-org-to-html (bom-format-state-lists-org)))
+;;;###autoload
+(defun bom-state (state)
+ "Open bom forecast for STATE."
+ (interactive "sState: ")
+ (with-current-buffer (get-buffer-create "*bom*")
+ (erase-buffer)
+ (insert (bom-states-org state))
+ (goto-char (point-min))
+ (org-mode))
+ (display-buffer "*bom*"))
+
+;;;###autoload
+(defun bom ()
+ "Entrypoint - open bom landing page."
+ (interactive)
+ (with-current-buffer (get-buffer-create "*bom*")
+ (erase-buffer)
+ (insert (bom-org))
+ (goto-char (point-min))
+ (org-mode))
+ (display-buffer "*bom*"))
+
+(defun bom-from-path (path)
+ "Translate a GET PATH to a pair of function and string args.
+
+For example, if path is /foo/bar/baz, then we return
+(foo . (\"bar\" \"baz\"))"
+ (unless (string-prefix-p "/" path)
+ (user-error "Invalid path: %s" path))
+ (pcase-let ((`(,func-name . ,args) (cdr (split-string path "/"))))
+ (unless (memq (intern func-name) '(bom bom-state))
+ (user-error "Invalid path: %s" path))
+ (cons (intern (format "%s-org" func-name)) args)))
(defun bom-serve (path)
"Serve based on PATH."
- (setq path (substring path 1))
- (if (string-empty-p path)
- (bom-format-state-lists-html)
- (bom-format-areas-html
- (bom-get-forecasts path))))
+ (bom-org-to-html (apply (bom-from-path path))))
;;;###autoload
(defun bom-start ()