148 lines
5.3 KiB
EmacsLisp
148 lines
5.3 KiB
EmacsLisp
;;; etaf-resource-example.el --- Resource lifecycle example -*- lexical-binding: t; -*-
|
|
|
|
;; SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
;;; Commentary:
|
|
|
|
;; This example keeps a synchronous Resource inside Component setup, exposes
|
|
;; success and error states, and lets Scope disposal own loader cleanup.
|
|
|
|
;;; Code:
|
|
|
|
(require 'etaf)
|
|
|
|
(defconst etaf-resource-example-buffer-name "*ETAF Resource Example*"
|
|
"Buffer opened by `etaf-resource-example-open'.")
|
|
|
|
(defun etaf-resource-example--message (resource)
|
|
"Return a display message for RESOURCE."
|
|
(pcase (etaf-resource-status resource)
|
|
('success (etaf-resource-value resource))
|
|
('error (error-message-string (etaf-resource-error resource)))
|
|
('loading "Loading service snapshot…")
|
|
(_ "Resource has not loaded.")))
|
|
|
|
(etaf-define-component etaf-resource-example-app ()
|
|
"Render a reloadable Resource with visible cleanup and error state."
|
|
:setup
|
|
(let ((fail-next (etaf-ref nil :name 'etaf-resource-example-fail))
|
|
(cleanup-count (etaf-ref 0 :name 'etaf-resource-example-cleanups))
|
|
(generation 0)
|
|
resource)
|
|
(setq resource
|
|
(etaf-resource
|
|
(lambda ()
|
|
(cl-incf generation)
|
|
(if (etaf-value fail-next)
|
|
(progn
|
|
(etaf-set-value fail-next nil)
|
|
(error "Synthetic service failure %d" generation))
|
|
(etaf-resource-result
|
|
(format "Generation %d is healthy." generation)
|
|
:cleanup
|
|
(lambda ()
|
|
(etaf-set-value
|
|
cleanup-count
|
|
(1+ (etaf-value cleanup-count)))))))
|
|
:immediate nil
|
|
:name 'etaf-resource-example))
|
|
(etaf-on-mounted (lambda () (etaf-resource-load resource)))
|
|
(list :resource resource :fail-next fail-next
|
|
:cleanup-count cleanup-count))
|
|
:view
|
|
(column :width '(680) :color "#252A2E" :bgcolor "#F8F5EE"
|
|
(box :width '(680) :padding '(1 (18)) :border "#8F432F"
|
|
:bgcolor "#FFFDF8" :text-align 'center
|
|
(column
|
|
(text :color "#8F432F" :font-weight 'bold
|
|
"BEST PRACTICE / RESOURCE LIFECYCLE")
|
|
(text :font-weight 'bold "Service health")
|
|
(text :color "#66706A"
|
|
"Loader errors become explicit state; cleanup stays scoped.")))
|
|
(box :height 1)
|
|
(box :width '(680) :padding '(2 (18))
|
|
:border
|
|
(if (eq (etaf-resource-status
|
|
(plist-get (etaf-state) :resource)) 'success)
|
|
"#6D8A73" "#C97252")
|
|
:bgcolor
|
|
(if (eq (etaf-resource-status
|
|
(plist-get (etaf-state) :resource)) 'success)
|
|
"#DCEBDD" "#F1D4C9")
|
|
:color
|
|
(if (eq (etaf-resource-status
|
|
(plist-get (etaf-state) :resource)) 'success)
|
|
"#24422D" "#6B3020")
|
|
:text-align 'center
|
|
(column
|
|
(text :font-weight 'bold
|
|
(expr
|
|
(upcase
|
|
(symbol-name
|
|
(etaf-resource-status (plist-get (etaf-state) :resource))))))
|
|
(text
|
|
(expr
|
|
(etaf-resource-example--message
|
|
(plist-get (etaf-state) :resource))))
|
|
(text :color "#66706A"
|
|
(expr
|
|
(format "CLEANUPS %d"
|
|
(etaf-value (plist-get (etaf-state) :cleanup-count)))))))
|
|
(box :height 1)
|
|
(flex :width '(680) :flex-flow '(row wrap) :gap '(1 (12))
|
|
(box :flex-grow 1 :flex-shrink 1 :flex-basis '(200)
|
|
:min-width '(180) :padding '(1 (12))
|
|
:border "#4E7890" :bgcolor "#D9EAF2" :font-weight 'bold
|
|
:text-align 'center :ref 'resource-reload :role 'button
|
|
:use (list (etaf-focusable))
|
|
:on-press
|
|
(let ((resource (plist-get (etaf-state) :resource)))
|
|
(lambda () (etaf-resource-load resource)))
|
|
(text "RELOAD"))
|
|
(box :flex-grow 1 :flex-shrink 1 :flex-basis '(200)
|
|
:min-width '(180) :padding '(1 (12))
|
|
:border "#C97252" :bgcolor "#F1D4C9" :font-weight 'bold
|
|
:text-align 'center :ref 'resource-fail :role 'button
|
|
:use (list (etaf-focusable))
|
|
:on-press
|
|
(let ((resource (plist-get (etaf-state) :resource))
|
|
(fail-next (plist-get (etaf-state) :fail-next)))
|
|
(lambda ()
|
|
(etaf-set-value fail-next t)
|
|
(etaf-resource-load resource)))
|
|
(text "SIMULATE FAILURE")))
|
|
(box :height 1)
|
|
(box :width '(680) :padding '(1 (16)) :border "#8D887F"
|
|
:color "#4D5651" :bgcolor "#EEEAE2"
|
|
(text
|
|
"Scope rule: reload releases the old value; unmount releases the last one"))))
|
|
|
|
;;;###autoload
|
|
(defun etaf-resource-example-view ()
|
|
"Return the Resource lifecycle example View."
|
|
(etaf-view (etaf-resource-example-app)))
|
|
|
|
;;;###autoload
|
|
(defun etaf-resource-example-open ()
|
|
"Mount the Resource lifecycle example and return its buffer."
|
|
(interactive)
|
|
(let ((buffer (etaf-mount etaf-resource-example-buffer-name
|
|
(etaf-resource-example-view))))
|
|
(when (called-interactively-p 'interactive)
|
|
(pop-to-buffer buffer))
|
|
buffer))
|
|
|
|
;;;###autoload
|
|
(defun etaf-resource-example-close ()
|
|
"Unmount and kill the Resource lifecycle example buffer."
|
|
(interactive)
|
|
(when-let* ((runtime
|
|
(etaf-runtime-for-buffer etaf-resource-example-buffer-name)))
|
|
(etaf-unmount runtime))
|
|
(when-let* ((buffer (get-buffer etaf-resource-example-buffer-name)))
|
|
(kill-buffer buffer)))
|
|
|
|
(provide 'etaf-resource-example)
|
|
|
|
;;; etaf-resource-example.el ends here
|