feat: adapt spine-add-book for shelf-based nesting
This commit is contained in:
@@ -227,13 +227,13 @@ SELECTED-ID expands the matching book's detail section."
|
|||||||
nil))))))))
|
nil))))))))
|
||||||
)))))
|
)))))
|
||||||
(defun spine-add-book (&rest args)
|
(defun spine-add-book (&rest args)
|
||||||
"Add a new WANT book to `spine-org-file'.
|
"Add a new book to shelf `:shelf' in `spine-org-file'.
|
||||||
Keyword arguments: :title :author :category :format :isbn :cover
|
Keyword arguments: :title :author :shelf :format :isbn :cover
|
||||||
:rec_by :rec_note :date_added :initial_note
|
:rec_by :rec_note :date_added :initial_note
|
||||||
Signals an error on failure."
|
:shelf is required. Signals an error if the shelf doesn't exist."
|
||||||
(let ((title (plist-get args :title))
|
(let ((title (plist-get args :title))
|
||||||
|
(shelf (plist-get args :shelf))
|
||||||
(author (plist-get args :author))
|
(author (plist-get args :author))
|
||||||
(category (plist-get args :category))
|
|
||||||
(format (plist-get args :format))
|
(format (plist-get args :format))
|
||||||
(isbn (plist-get args :isbn))
|
(isbn (plist-get args :isbn))
|
||||||
(cover (plist-get args :cover))
|
(cover (plist-get args :cover))
|
||||||
@@ -243,20 +243,32 @@ Signals an error on failure."
|
|||||||
(initial-note (plist-get args :initial_note)))
|
(initial-note (plist-get args :initial_note)))
|
||||||
(unless title
|
(unless title
|
||||||
(error "spine-add-book: title is required"))
|
(error "spine-add-book: title is required"))
|
||||||
|
(unless shelf
|
||||||
|
(error "spine-add-book: :shelf is required"))
|
||||||
(let ((org-file (expand-file-name spine-org-file)))
|
(let ((org-file (expand-file-name spine-org-file)))
|
||||||
;; Create file if it doesn't exist
|
|
||||||
(unless (file-exists-p org-file)
|
(unless (file-exists-p org-file)
|
||||||
(with-temp-file org-file
|
(error "spine-add-book: file %s does not exist; create shelves first"
|
||||||
(insert "#+TODO: WANT(w) READING(r) | READ(d) ABANDONED(a)\n\n")))
|
org-file))
|
||||||
(with-current-buffer (find-file-noselect org-file)
|
(with-current-buffer (find-file-noselect org-file)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
(goto-char (point-max))
|
;; Find the shelf headline
|
||||||
;; Insert new headline
|
(goto-char (point-min))
|
||||||
|
(let ((shelf-found nil))
|
||||||
|
(while (and (not shelf-found)
|
||||||
|
(re-search-forward
|
||||||
|
(format "^\\*+[ \t]+%s[ \t]*$" (regexp-quote shelf))
|
||||||
|
nil t))
|
||||||
|
(when (= (org-current-level) 1)
|
||||||
|
(setq shelf-found t)))
|
||||||
|
(unless shelf-found
|
||||||
|
(error "spine-add-book: shelf \"%s\" not found" shelf)))
|
||||||
|
;; Go to end of shelf section
|
||||||
|
(org-end-of-subtree)
|
||||||
|
;; Insert new book headline at level 2
|
||||||
(org-insert-heading nil t)
|
(org-insert-heading nil t)
|
||||||
|
(org-demote-subtree)
|
||||||
(insert title)
|
(insert title)
|
||||||
;; Set TODO state
|
|
||||||
(org-todo "WANT")
|
|
||||||
;; Set properties (only non-empty)
|
;; Set properties (only non-empty)
|
||||||
(when (and author (> (length author) 0))
|
(when (and author (> (length author) 0))
|
||||||
(org-set-property "AUTHOR" author))
|
(org-set-property "AUTHOR" author))
|
||||||
@@ -279,9 +291,6 @@ Signals an error on failure."
|
|||||||
(substring (sha1 (concat title (number-to-string (random)))) 0 4)))
|
(substring (sha1 (concat title (number-to-string (random)))) 0 4)))
|
||||||
;; Set last modified
|
;; Set last modified
|
||||||
(org-set-property "LAST_MODIFIED" (format-time-string "[%Y-%m-%d]"))
|
(org-set-property "LAST_MODIFIED" (format-time-string "[%Y-%m-%d]"))
|
||||||
;; Set tags
|
|
||||||
(when (and category (> (length category) 0))
|
|
||||||
(org-set-tags (list category)))
|
|
||||||
;; Add initial note
|
;; Add initial note
|
||||||
(when (and initial-note (> (length initial-note) 0))
|
(when (and initial-note (> (length initial-note) 0))
|
||||||
(let ((date (if (and date-added (> (length date-added) 0))
|
(let ((date (if (and date-added (> (length date-added) 0))
|
||||||
|
|||||||
+40
-10
@@ -16,18 +16,20 @@
|
|||||||
(ignore-errors (delete-file spine-org-file)))
|
(ignore-errors (delete-file spine-org-file)))
|
||||||
|
|
||||||
(ert-deftest spine-add-book-creates-headline ()
|
(ert-deftest spine-add-book-creates-headline ()
|
||||||
"spine-add-book creates a new WANT headline."
|
"spine-add-book creates a new book under the given shelf."
|
||||||
(spine-add-book-test--cleanup)
|
(spine-add-book-test--cleanup)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
(spine-add-book :title "Test Book" :author "Author Name")
|
(with-temp-file spine-org-file
|
||||||
|
(insert "* Fiction\n\n"))
|
||||||
|
(spine-add-book :title "Test Book" :author "Author Name" :shelf "Fiction")
|
||||||
(let ((books (spine-books)))
|
(let ((books (spine-books)))
|
||||||
(should books)
|
(should books)
|
||||||
(should (= (length books) 1))
|
(should (= (length books) 1))
|
||||||
(let ((book (car books)))
|
(let ((book (car books)))
|
||||||
(should (equal (plist-get book :title) "Test Book"))
|
(should (equal (plist-get book :title) "Test Book"))
|
||||||
(should (equal (plist-get book :status) "WANT"))
|
|
||||||
(should (equal (plist-get book :author) "Author Name"))
|
(should (equal (plist-get book :author) "Author Name"))
|
||||||
|
(should (equal (plist-get book :shelf) "Fiction"))
|
||||||
(should (plist-get book :id)))))
|
(should (plist-get book :id)))))
|
||||||
(spine-add-book-test--cleanup)))
|
(spine-add-book-test--cleanup)))
|
||||||
|
|
||||||
@@ -36,6 +38,8 @@
|
|||||||
(spine-add-book-test--cleanup)
|
(spine-add-book-test--cleanup)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
|
(with-temp-file spine-org-file
|
||||||
|
(insert "* Fiction\n\n"))
|
||||||
(spine-add-book
|
(spine-add-book
|
||||||
:title "Full Test"
|
:title "Full Test"
|
||||||
:author "Jane Doe"
|
:author "Jane Doe"
|
||||||
@@ -45,8 +49,8 @@
|
|||||||
:rec_by "Friend"
|
:rec_by "Friend"
|
||||||
:rec_note "You'll love this"
|
:rec_note "You'll love this"
|
||||||
:date_added "2026-06-21"
|
:date_added "2026-06-21"
|
||||||
:category "scifi"
|
:initial_note "Looking forward to this"
|
||||||
:initial_note "Looking forward to this")
|
:shelf "Fiction")
|
||||||
(let* ((books (spine-books))
|
(let* ((books (spine-books))
|
||||||
(book (car books)))
|
(book (car books)))
|
||||||
(should (equal (plist-get book :title) "Full Test"))
|
(should (equal (plist-get book :title) "Full Test"))
|
||||||
@@ -57,7 +61,6 @@
|
|||||||
(should (equal (plist-get book :rec_by) "Friend"))
|
(should (equal (plist-get book :rec_by) "Friend"))
|
||||||
(should (equal (plist-get book :rec_note) "You'll love this"))
|
(should (equal (plist-get book :rec_note) "You'll love this"))
|
||||||
(should (equal (plist-get book :added) "[2026-06-21]"))
|
(should (equal (plist-get book :added) "[2026-06-21]"))
|
||||||
(should (equal (plist-get book :tags) '("scifi")))
|
|
||||||
(should (equal (plist-get book :notes)
|
(should (equal (plist-get book :notes)
|
||||||
'(("2026-06-21" "Looking forward to this"))))))
|
'(("2026-06-21" "Looking forward to this"))))))
|
||||||
(spine-add-book-test--cleanup)))
|
(spine-add-book-test--cleanup)))
|
||||||
@@ -67,10 +70,13 @@
|
|||||||
(spine-add-book-test--cleanup)
|
(spine-add-book-test--cleanup)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
(spine-add-book :title "Minimal Book")
|
(with-temp-file spine-org-file
|
||||||
|
(insert "* Fiction\n\n"))
|
||||||
|
(spine-add-book :title "Minimal Book" :shelf "Fiction")
|
||||||
(let* ((books (spine-books))
|
(let* ((books (spine-books))
|
||||||
(book (car books)))
|
(book (car books)))
|
||||||
(should (equal (plist-get book :title) "Minimal Book"))
|
(should (equal (plist-get book :title) "Minimal Book"))
|
||||||
|
(should (equal (plist-get book :shelf) "Fiction"))
|
||||||
(should-not (plist-get book :author))
|
(should-not (plist-get book :author))
|
||||||
(should-not (plist-get book :format))
|
(should-not (plist-get book :format))
|
||||||
(should-not (plist-get book :isbn))
|
(should-not (plist-get book :isbn))
|
||||||
@@ -81,7 +87,14 @@
|
|||||||
"spine-add-book signals error when title is missing."
|
"spine-add-book signals error when title is missing."
|
||||||
(spine-add-book-test--cleanup)
|
(spine-add-book-test--cleanup)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(should-error (spine-add-book :author "No Title"))
|
(should-error (spine-add-book :author "No Title" :shelf "Fiction"))
|
||||||
|
(spine-add-book-test--cleanup)))
|
||||||
|
|
||||||
|
(ert-deftest spine-add-book-requires-shelf ()
|
||||||
|
"spine-add-book signals error when shelf is missing."
|
||||||
|
(spine-add-book-test--cleanup)
|
||||||
|
(unwind-protect
|
||||||
|
(should-error (spine-add-book :title "No Shelf"))
|
||||||
(spine-add-book-test--cleanup)))
|
(spine-add-book-test--cleanup)))
|
||||||
|
|
||||||
(ert-deftest spine-add-book-generates-unique-ids ()
|
(ert-deftest spine-add-book-generates-unique-ids ()
|
||||||
@@ -89,10 +102,27 @@
|
|||||||
(spine-add-book-test--cleanup)
|
(spine-add-book-test--cleanup)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(progn
|
(progn
|
||||||
(spine-add-book :title "Book One")
|
(with-temp-file spine-org-file
|
||||||
(spine-add-book :title "Book Two")
|
(insert "* Fiction\n\n"))
|
||||||
|
(spine-add-book :title "Book One" :shelf "Fiction")
|
||||||
|
(spine-add-book :title "Book Two" :shelf "Fiction")
|
||||||
(let* ((books (spine-books))
|
(let* ((books (spine-books))
|
||||||
(id1 (plist-get (nth 0 books) :id))
|
(id1 (plist-get (nth 0 books) :id))
|
||||||
(id2 (plist-get (nth 1 books) :id)))
|
(id2 (plist-get (nth 1 books) :id)))
|
||||||
(should (not (equal id1 id2)))))
|
(should (not (equal id1 id2)))))
|
||||||
(spine-add-book-test--cleanup)))
|
(spine-add-book-test--cleanup)))
|
||||||
|
|
||||||
|
(ert-deftest spine-add-book-creates-under-correct-shelf ()
|
||||||
|
"spine-add-book places the book under the named shelf."
|
||||||
|
(spine-add-book-test--cleanup)
|
||||||
|
(unwind-protect
|
||||||
|
(progn
|
||||||
|
(with-temp-file spine-org-file
|
||||||
|
(insert "* Fiction\n\n* Non-Fiction\n\n"))
|
||||||
|
(spine-add-book :title "Sci-Fi Book" :shelf "Fiction")
|
||||||
|
(spine-add-book :title "History Book" :shelf "Non-Fiction")
|
||||||
|
(let ((books (spine-books)))
|
||||||
|
(should (= (length books) 2))
|
||||||
|
(should (equal (plist-get (nth 0 books) :shelf) "Fiction"))
|
||||||
|
(should (equal (plist-get (nth 1 books) :shelf) "Non-Fiction"))))
|
||||||
|
(spine-add-book-test--cleanup)))
|
||||||
|
|||||||
Reference in New Issue
Block a user