From a0e5edfb3d77f34d0f71bdc9bb8bc3a563ee6436 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 10 May 2026 15:23:23 -0500 Subject: refactor(cj-cache): rename to cj-cache-lib for naming consistency Library files in this codebase are suffixed `-lib' (system-lib.el is the established precedent). The Phase 5 cache helper landed as cj-cache.el; the spec's table proposed names without the suffix and I followed it without checking against convention. Fix the inconsistency now while there are only two consumers and one test. Rename modules/cj-cache.el -> modules/cj-cache-lib.el; update provide form, file header, and the three (require 'cj-cache) call sites in org-agenda-config, org-refile-config, and test-cj-cache. No behavior change. --- modules/cj-cache-lib.el | 95 ++++++++++++++++++++++++++++++++++++++++++++ modules/cj-cache.el | 95 -------------------------------------------- modules/org-agenda-config.el | 2 +- modules/org-refile-config.el | 2 +- 4 files changed, 97 insertions(+), 97 deletions(-) create mode 100644 modules/cj-cache-lib.el delete mode 100644 modules/cj-cache.el (limited to 'modules') diff --git a/modules/cj-cache-lib.el b/modules/cj-cache-lib.el new file mode 100644 index 00000000..9aad51a3 --- /dev/null +++ b/modules/cj-cache-lib.el @@ -0,0 +1,95 @@ +;;; cj-cache-lib.el --- Generic TTL cache with build-guard -*- lexical-binding: t; -*- + +;; Author: Craig Jennings + +;;; Commentary: + +;; Generic "rebuild a long-running computation behind a TTL" cache, +;; with a building-flag guard that prevents duplicate concurrent +;; rebuilds in async-build scenarios. +;; +;; Used by org-agenda-config and org-refile-config which previously +;; carried parallel hand-rolled implementations of this exact shape. +;; See docs/design/cache-helper-design.org for the API contract, +;; consumer migration shape, and rationale for the deliberate "nil +;; cached value reads as invalid" decision. +;; +;; Out of scope: buffer-local key-based caches like the modeline VC +;; cache (different lifecycle). + +;;; Code: + +(require 'cl-lib) + +(defun cj/cache-make (&rest plist) + "Return a fresh cache state. +PLIST keywords: +- =:ttl= seconds to retain a built value (default 3600)." + (let ((ttl (or (plist-get plist :ttl) 3600))) + (list :value nil :time nil :ttl ttl :building nil))) + +(defun cj/cache-valid-p (cache) + "Return non-nil when CACHE has a fresh, non-nil value within its TTL. +A nil cached value reads as invalid by design -- a build that legitimately +returns nil rebuilds on the next request, matching the prior agenda/refile +contract." + (let ((value (plist-get cache :value)) + (time (plist-get cache :time)) + (ttl (plist-get cache :ttl))) + (and value + time + (< (- (float-time) time) ttl)))) + +(defun cj/cache-building-p (cache) + "Return non-nil when a build is currently in progress on CACHE." + (plist-get cache :building)) + +(defun cj/cache-invalidate (cache) + "Clear CACHE's value and timestamp. TTL is preserved." + (plist-put cache :value nil) + (plist-put cache :time nil)) + +(cl-defun cj/cache-value-or-rebuild (cache build-fn + &key force-rebuild + on-hit + on-build-start + on-build-success + on-build-error) + "Return CACHE's value, calling BUILD-FN to rebuild when invalid. + +When CACHE is valid and FORCE-REBUILD is nil, return the stored value +and call ON-HIT (if given) with the value. Otherwise call BUILD-FN, +store its result, and return it. + +The four callbacks let the consumer log without this helper printing on +its behalf: +- ON-HIT (value) +- ON-BUILD-START () +- ON-BUILD-SUCCESS (value) +- ON-BUILD-ERROR (err) + +The :building flag is set before BUILD-FN runs and cleared inside an +`unwind-protect' regardless of outcome. Errors from BUILD-FN are +rethrown after ON-BUILD-ERROR fires." + (cond + ((and (not force-rebuild) (cj/cache-valid-p cache)) + (let ((value (plist-get cache :value))) + (when on-hit (funcall on-hit value)) + value)) + (t + (when on-build-start (funcall on-build-start)) + (plist-put cache :building t) + (unwind-protect + (condition-case err + (let ((value (funcall build-fn))) + (plist-put cache :value value) + (plist-put cache :time (float-time)) + (when on-build-success (funcall on-build-success value)) + value) + (error + (when on-build-error (funcall on-build-error err)) + (signal (car err) (cdr err)))) + (plist-put cache :building nil))))) + +(provide 'cj-cache-lib) +;;; cj-cache-lib.el ends here diff --git a/modules/cj-cache.el b/modules/cj-cache.el deleted file mode 100644 index b7d048c9..00000000 --- a/modules/cj-cache.el +++ /dev/null @@ -1,95 +0,0 @@ -;;; cj-cache.el --- Generic TTL cache with build-guard -*- lexical-binding: t; -*- - -;; Author: Craig Jennings - -;;; Commentary: - -;; Generic "rebuild a long-running computation behind a TTL" cache, -;; with a building-flag guard that prevents duplicate concurrent -;; rebuilds in async-build scenarios. -;; -;; Used by org-agenda-config and org-refile-config which previously -;; carried parallel hand-rolled implementations of this exact shape. -;; See docs/design/cache-helper-design.org for the API contract, -;; consumer migration shape, and rationale for the deliberate "nil -;; cached value reads as invalid" decision. -;; -;; Out of scope: buffer-local key-based caches like the modeline VC -;; cache (different lifecycle). - -;;; Code: - -(require 'cl-lib) - -(defun cj/cache-make (&rest plist) - "Return a fresh cache state. -PLIST keywords: -- =:ttl= seconds to retain a built value (default 3600)." - (let ((ttl (or (plist-get plist :ttl) 3600))) - (list :value nil :time nil :ttl ttl :building nil))) - -(defun cj/cache-valid-p (cache) - "Return non-nil when CACHE has a fresh, non-nil value within its TTL. -A nil cached value reads as invalid by design -- a build that legitimately -returns nil rebuilds on the next request, matching the prior agenda/refile -contract." - (let ((value (plist-get cache :value)) - (time (plist-get cache :time)) - (ttl (plist-get cache :ttl))) - (and value - time - (< (- (float-time) time) ttl)))) - -(defun cj/cache-building-p (cache) - "Return non-nil when a build is currently in progress on CACHE." - (plist-get cache :building)) - -(defun cj/cache-invalidate (cache) - "Clear CACHE's value and timestamp. TTL is preserved." - (plist-put cache :value nil) - (plist-put cache :time nil)) - -(cl-defun cj/cache-value-or-rebuild (cache build-fn - &key force-rebuild - on-hit - on-build-start - on-build-success - on-build-error) - "Return CACHE's value, calling BUILD-FN to rebuild when invalid. - -When CACHE is valid and FORCE-REBUILD is nil, return the stored value -and call ON-HIT (if given) with the value. Otherwise call BUILD-FN, -store its result, and return it. - -The four callbacks let the consumer log without this helper printing on -its behalf: -- ON-HIT (value) -- ON-BUILD-START () -- ON-BUILD-SUCCESS (value) -- ON-BUILD-ERROR (err) - -The :building flag is set before BUILD-FN runs and cleared inside an -`unwind-protect' regardless of outcome. Errors from BUILD-FN are -rethrown after ON-BUILD-ERROR fires." - (cond - ((and (not force-rebuild) (cj/cache-valid-p cache)) - (let ((value (plist-get cache :value))) - (when on-hit (funcall on-hit value)) - value)) - (t - (when on-build-start (funcall on-build-start)) - (plist-put cache :building t) - (unwind-protect - (condition-case err - (let ((value (funcall build-fn))) - (plist-put cache :value value) - (plist-put cache :time (float-time)) - (when on-build-success (funcall on-build-success value)) - value) - (error - (when on-build-error (funcall on-build-error err)) - (signal (car err) (cdr err)))) - (plist-put cache :building nil))))) - -(provide 'cj-cache) -;;; cj-cache.el ends here diff --git a/modules/org-agenda-config.el b/modules/org-agenda-config.el index 049ece7b..6c60e8dd 100644 --- a/modules/org-agenda-config.el +++ b/modules/org-agenda-config.el @@ -44,7 +44,7 @@ ;;; Code: (require 'user-constants) (require 'system-lib) -(require 'cj-cache) +(require 'cj-cache-lib) ;; Load debug functions if enabled (when (or (eq cj/debug-modules t) diff --git a/modules/org-refile-config.el b/modules/org-refile-config.el index 8d989e4b..63343e9f 100644 --- a/modules/org-refile-config.el +++ b/modules/org-refile-config.el @@ -14,7 +14,7 @@ ;;; Code: (require 'system-lib) -(require 'cj-cache) +(require 'cj-cache-lib) ;; ----------------------------- Org Refile Targets ---------------------------- ;; sets refile targets -- cgit v1.2.3