emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

scratch/sqlite e4c2283 1/2: Rearrange multisession to allow several back


From: Lars Ingebrigtsen
Subject: scratch/sqlite e4c2283 1/2: Rearrange multisession to allow several backends
Date: Mon, 13 Dec 2021 21:03:21 -0500 (EST)

branch: scratch/sqlite
commit e4c2283162b174c100b55ae07efb3ad425be3719
Author: Lars Ingebrigtsen <larsi@gnus.org>
Commit: Lars Ingebrigtsen <larsi@gnus.org>

    Rearrange multisession to allow several backends
---
 lisp/emacs-lisp/multisession.el            | 199 +++++++++++++++++------------
 test/lisp/emacs-lisp/multisession-tests.el | 124 +++++++++---------
 2 files changed, 175 insertions(+), 148 deletions(-)

diff --git a/lisp/emacs-lisp/multisession.el b/lisp/emacs-lisp/multisession.el
index b3119e7..a75134f 100644
--- a/lisp/emacs-lisp/multisession.el
+++ b/lisp/emacs-lisp/multisession.el
@@ -27,10 +27,17 @@
 (require 'eieio)
 (require 'sqlite)
 
-(defcustom multisession-database-file
-  (expand-file-name "multisession/sqlite/multisession.sqlite"
-                    user-emacs-directory)
-  "File to store multisession variables."
+(defcustom multisession-storage 'sqlite
+  "Storage method for multisession variables.
+Valid methods are `sqlite' and `files'."
+  :type '(choice (const :tag "SQLite" sqlite)
+                 (const :tag "Files" files))
+  :version "29.1"
+  :group 'files)
+
+(defcustom multisession-directory (expand-file-name "multisession/"
+                                                    user-emacs-directory)
+  "Directory to store multisession variables."
   :type 'file
   :version "29.1"
   :group 'files)
@@ -74,14 +81,40 @@ DOC should be a doc string, and ARGS are keywords as 
applicable to
    :initial-value initial-value
    :package package))
 
+(defun multisession-value (object)
+  "Return the value of the multisession OBJECT."
+  (if (or (null user-init-file)
+          (not (sqlite-available-p)))
+      ;; If we don't have storage, then just return the value from the
+      ;; object.
+      (if (markerp (multisession--cached-value object))
+          (multisession--initial-value object)
+        (multisession--cached-value object))
+    ;; We have storage, so we update from storage.
+    (multisession-backend-value multisession-storage object)))
+
+(defun multisession--set-value (object value)
+  (if (or (null user-init-file)
+          (not (sqlite-available-p)))
+      ;; We have no backend, so just store the value.
+      (setf (multisession--cached-value object) value)
+    ;; We have a backend.
+    (multisession--backend-set-value multisession-storage object value)))
+
+(gv-define-simple-setter multisession-value multisession--set-value)
+
+;; SQLite Backend
+
 (defvar multisession--db nil)
 
 (defun multisession--ensure-db ()
   (unless multisession--db
-    (let ((dir (file-name-directory multisession-database-file)))
+    (let* ((file (expand-file-name "sqlite/multisession.sqlite"
+                                   multisession-directory))
+           (dir (file-name-directory file)))
       (unless (file-exists-p dir)
-        (make-directory dir t)))
-    (setq multisession--db (sqlite-open multisession-database-file))
+        (make-directory dir t))
+      (setq multisession--db (sqlite-open file)))
     (with-sqlite-transaction multisession--db
       (unless (sqlite-select
                multisession--db
@@ -100,95 +133,92 @@ DOC should be a doc string, and ARGS are keywords as 
applicable to
          multisession--db
          "create unique index multisession_idx on multisession (package, 
key)")))))
 
-(defun multisession-value (object)
-  "Return the value of the multisession OBJECT."
-  (if (or (null user-init-file)
-          (not (sqlite-available-p)))
-      ;; If we don't have storage, then just return the value from the
-      ;; object.
-      (if (markerp (multisession--cached-value object))
-          (multisession--initial-value object)
-        (multisession--cached-value object))
-    ;; We have storage, so we update from storage.
-    (multisession--ensure-db)
-    (let ((id (list (symbol-name (multisession--package object))
-                    (symbol-name (multisession--key object)))))
-      (cond
-       ;; We have no value yet; check the database.
-       ((markerp (multisession--cached-value object))
-        (let ((stored
-               (car
-                (sqlite-select
-                 multisession--db
-                 "select value, sequence from multisession where package = ? 
and key = ?"
-                 id))))
-          (if stored
-              (let ((value (car (read-from-string (car stored)))))
-                (setf (multisession--cached-value object) value
-                      (multisession--cached-sequence object) (cadr stored))
-                value)
-            ;; Nothing; return the initial value.
-            (multisession--initial-value object))))
-       ;; We have a value, but we want to update in case some other
-       ;; Emacs instance has updated.
-       ((multisession--synchronized object)
-        (let ((stored
-               (car
-                (sqlite-select
-                 multisession--db
-                 "select value, sequence from multisession where sequence > ? 
and package = ? and key = ?"
-                 (cons (multisession--cached-sequence object) id)))))
-          (if stored
-              (let ((value (car (read-from-string (car stored)))))
-                (setf (multisession--cached-value object) value
-                      (multisession--cached-sequence object) (cadr stored))
-                value)
-            ;; Nothing, return the cached value.
-            (multisession--cached-value object))))
-       ;; Just return the cached value.
-       (t
-        (multisession--cached-value object))))))
+(cl-defmethod multisession-backend-value ((_type (eql sqlite)) object)
+  (multisession--ensure-db)
+  (let ((id (list (symbol-name (multisession--package object))
+                  (symbol-name (multisession--key object)))))
+    (cond
+     ;; We have no value yet; check the database.
+     ((markerp (multisession--cached-value object))
+      (let ((stored
+             (car
+              (sqlite-select
+               multisession--db
+               "select value, sequence from multisession where package = ? and 
key = ?"
+               id))))
+        (if stored
+            (let ((value (car (read-from-string (car stored)))))
+              (setf (multisession--cached-value object) value
+                    (multisession--cached-sequence object) (cadr stored))
+              value)
+          ;; Nothing; return the initial value.
+          (multisession--initial-value object))))
+     ;; We have a value, but we want to update in case some other
+     ;; Emacs instance has updated.
+     ((multisession--synchronized object)
+      (let ((stored
+             (car
+              (sqlite-select
+               multisession--db
+               "select value, sequence from multisession where sequence > ? 
and package = ? and key = ?"
+               (cons (multisession--cached-sequence object) id)))))
+        (if stored
+            (let ((value (car (read-from-string (car stored)))))
+              (setf (multisession--cached-value object) value
+                    (multisession--cached-sequence object) (cadr stored))
+              value)
+          ;; Nothing, return the cached value.
+          (multisession--cached-value object))))
+     ;; Just return the cached value.
+     (t
+      (multisession--cached-value object)))))
 
-(defun multisession--set-value (object value)
+(cl-defmethod multisession--backend-set-value ((_type (eql sqlite))
+                                               object value)
   (catch 'done
     (let ((i 0))
       (while (< i 10)
         (condition-case nil
-            (throw 'done (multisession--set-value-1 object value))
+            (throw 'done (multisession--set-value-sqlite object value))
           (sqlite-locked-error
            (setq i (1+ i))
            (sleep-for (+ 0.1 (/ (float (random 10)) 10))))))
       (signal 'sqlite-locked-error "Database is locked"))))
 
-(defun multisession--set-value-1 (object value)
-  (if (or (null user-init-file)
-          (not (sqlite-available-p)))
-      ;; We have no backend, so just store the value.
-      (setf (multisession--cached-value object) value)
-    ;; We have a backend.
-    (multisession--ensure-db)
-    (with-sqlite-transaction multisession--db
-      (let ((id (list (symbol-name (multisession--package object))
-                      (symbol-name (multisession--key object))))
-            (pvalue (prin1-to-string value)))
-        (sqlite-execute
-         multisession--db
-         "insert into multisession(package, key, sequence, value) values(?, ?, 
1, ?) on conflict(package, key) do update set sequence = sequence + 1, value = 
?"
-         (append id (list pvalue pvalue)))
-        (setf (multisession--cached-sequence object)
-              (caar (sqlite-select
-                     multisession--db
-                     "select sequence from multisession where package = ? and 
key = ?"
-                     id)))
-        (setf (multisession--cached-value object) value)))))
+(defun multisession--set-value-sqlite (object value)
+  (multisession--ensure-db)
+  (with-sqlite-transaction multisession--db
+    (let ((id (list (symbol-name (multisession--package object))
+                    (symbol-name (multisession--key object))))
+          (pvalue (prin1-to-string value)))
+      (sqlite-execute
+       multisession--db
+       "insert into multisession(package, key, sequence, value) values(?, ?, 
1, ?) on conflict(package, key) do update set sequence = sequence + 1, value = 
?"
+       (append id (list pvalue pvalue)))
+      (setf (multisession--cached-sequence object)
+            (caar (sqlite-select
+                   multisession--db
+                   "select sequence from multisession where package = ? and 
key = ?"
+                   id)))
+      (setf (multisession--cached-value object) value))))
 
-(gv-define-simple-setter multisession-value multisession--set-value)
+(cl-defmethod multisession--backend-values ((_type (eql sqlite)))
+  (sqlite-select
+   multisession--db
+   "select package, key, value from multisession order by package, key"))
+
+(cl-defmethod multisession--backend-delete ((_type (eql sqlite)) id)
+  (sqlite-execute multisession--db
+                  "delete from multisession where package = ? and key = ?"
+                  id))
 
 ;; (define-multisession-variable foo 'bar)
 ;; (multisession-value foo)
 ;; (multisession--set-value foo 'zot)
 ;; (setf (multisession-value foo) 'gazonk)
 
+;; Mode for editing.
+
 (defvar-keymap multisession-edit-mode-map
   "d" #'multisession-delete-value)
 
@@ -206,9 +236,8 @@ DOC should be a doc string, and ARGS are keywords as 
applicable to
   (pop-to-buffer (get-buffer-create "*Multisession*"))
   (let ((inhibit-read-only t))
     (erase-buffer)
-    (cl-loop for (package key value) in (sqlite-select
-                                         multisession--db
-                                         "select package, key, value from 
multisession order by package, key")
+    (cl-loop for (package key value)
+             in (multisession--backend-values multisession-storage)
              do (insert (propertize (format "%s %s %s\n"
                                             package key value)
                                     'multisession--id (list package key))))
@@ -217,11 +246,11 @@ DOC should be a doc string, and ARGS are keywords as 
applicable to
 
 (defun multisession-delete-value (id)
   "Delete the value at point."
-  (interactive (list (get-text-property (point) 'multisession--id)) 
multisession-edit-mode)
+  (interactive (list (get-text-property (point) 'multisession--id))
+               multisession-edit-mode)
   (unless id
     (error "No value on the current line"))
-  (sqlite-execute multisession--db "delete from multisession where package = ? 
and key = ?"
-                  id)
+  (multisession--backend-delete multisession-storage id)
   (let ((inhibit-read-only t))
     (beginning-of-line)
     (delete-region (point) (progn (forward-line 1) (point)))))
diff --git a/test/lisp/emacs-lisp/multisession-tests.el 
b/test/lisp/emacs-lisp/multisession-tests.el
index b26a8e0..c08fa20 100644
--- a/test/lisp/emacs-lisp/multisession-tests.el
+++ b/test/lisp/emacs-lisp/multisession-tests.el
@@ -28,73 +28,71 @@
 
 (ert-deftest multi-test-simple ()
   (skip-unless (sqlite-available-p))
-  (let ((multisession-database-file (make-temp-name "/tmp/multi"))
-        (user-init-file "/tmp/foo.el"))
-    (unwind-protect
-        (progn
-          (define-multisession-variable foo 0
-            ""
-            :synchronized t)
-          (should (= (multisession-value foo) 0))
-          (cl-incf (multisession-value foo))
-          (should (= (multisession-value foo) 1))
-          (call-process
-           (concat invocation-directory invocation-name)
-           nil t nil
-           "-Q" "-batch"
-           "--eval" (prin1-to-string
-                     `(progn
-                        (require 'multisession)
-                        (let ((multisession-database-file
-                               ,multisession-database-file)
-                              (user-init-file "/tmp/foo.el"))
-                          (define-multisession-variable foo 0
-                            ""
-                            :synchronized t)
-                          (cl-incf (multisession-value foo))))))
-          (should (= (multisession-value foo) 2)))
-      (when (file-exists-p multisession-database-file)
-        (delete-file multisession-database-file)
+  (ert-with-temp-file dir
+    :directory t
+    (let ((user-init-file "/tmp/foo.el")
+          (multisession-directory dir))
+      (unwind-protect
+          (progn
+            (define-multisession-variable foo 0
+              ""
+              :synchronized t)
+            (should (= (multisession-value foo) 0))
+            (cl-incf (multisession-value foo))
+            (should (= (multisession-value foo) 1))
+            (call-process
+             (concat invocation-directory invocation-name)
+             nil t nil
+             "-Q" "-batch"
+             "--eval" (prin1-to-string
+                       `(progn
+                          (require 'multisession)
+                          (let ((multisession-directory ,dir)
+                                (user-init-file "/tmp/foo.el"))
+                            (define-multisession-variable foo 0
+                              ""
+                              :synchronized t)
+                            (cl-incf (multisession-value foo))))))
+            (should (= (multisession-value foo) 2)))
         (sqlite-close multisession--db)
         (setq multisession--db nil)))))
 
 (ert-deftest multi-test-busy ()
-  (skip-unless (sqlite-available-p))
-  (let ((multisession-database-file (make-temp-name "/tmp/multi"))
-        (user-init-file "/tmp/bar.el")
-        proc)
-    (unwind-protect
-        (progn
-          (define-multisession-variable bar 0
-            ""
-            :synchronized t)
-          (should (= (multisession-value bar) 0))
-          (cl-incf (multisession-value bar))
-          (should (= (multisession-value bar) 1))
-          (setq proc
-                (start-process
-                 "other-emacs"
-                 nil
-                 (concat invocation-directory invocation-name)
-                 "-Q" "-batch"
-                 "--eval" (prin1-to-string
-                           `(progn
-                              (require 'multisession)
-                              (let ((multisession-database-file
-                                     ,multisession-database-file)
-                                    (user-init-file "/tmp/bar.el"))
-                                (define-multisession-variable bar 0
-                                  "" :synchronized t)
-                                (dotimes (i 1000)
-                                  (cl-incf (multisession-value bar))))))))
-          (while (process-live-p proc)
-            (ignore-error 'sqlite-locked-error
-              (cl-incf (multisession-value bar)))
-            (sleep-for 0.1))
-          (message "bar ends up as %s" (multisession-value bar))
-          (should (< (multisession-value bar) 1003)))
-      (when (file-exists-p multisession-database-file)
-        (delete-file multisession-database-file)
+  (skip-unless (and t (sqlite-available-p)))
+  (ert-with-temp-file dir
+    :directory t
+    (let ((user-init-file "/tmp/foo.el")
+          (multisession-directory dir)
+          proc)
+      (unwind-protect
+          (progn
+            (define-multisession-variable bar 0
+              ""
+              :synchronized t)
+            (should (= (multisession-value bar) 0))
+            (cl-incf (multisession-value bar))
+            (should (= (multisession-value bar) 1))
+            (setq proc
+                  (start-process
+                   "other-emacs"
+                   nil
+                   (concat invocation-directory invocation-name)
+                   "-Q" "-batch"
+                   "--eval" (prin1-to-string
+                             `(progn
+                                (require 'multisession)
+                                (let ((multisession-database-file ,dir)
+                                      (user-init-file "/tmp/bar.el"))
+                                  (define-multisession-variable bar 0
+                                    "" :synchronized t)
+                                  (dotimes (i 1000)
+                                    (cl-incf (multisession-value bar))))))))
+            (while (process-live-p proc)
+              (ignore-error 'sqlite-locked-error
+                (cl-incf (multisession-value bar)))
+              (sleep-for 0.1))
+            (message "bar ends up as %s" (multisession-value bar))
+            (should (< (multisession-value bar) 1003)))
         (sqlite-close multisession--db)
         (setq multisession--db nil)))))
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]