diff --git a/spine.el b/spine.el index 037d167..adaf2ba 100644 --- a/spine.el +++ b/spine.el @@ -227,13 +227,13 @@ SELECTED-ID expands the matching book's detail section." nil)))))))) ))))) (defun spine-add-book (&rest args) - "Add a new WANT book to `spine-org-file'. -Keyword arguments: :title :author :category :format :isbn :cover + "Add a new book to shelf `:shelf' in `spine-org-file'. +Keyword arguments: :title :author :shelf :format :isbn :cover :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)) + (shelf (plist-get args :shelf)) (author (plist-get args :author)) - (category (plist-get args :category)) (format (plist-get args :format)) (isbn (plist-get args :isbn)) (cover (plist-get args :cover)) @@ -243,20 +243,32 @@ Signals an error on failure." (initial-note (plist-get args :initial_note))) (unless title (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))) - ;; Create file if it doesn't exist (unless (file-exists-p org-file) - (with-temp-file org-file - (insert "#+TODO: WANT(w) READING(r) | READ(d) ABANDONED(a)\n\n"))) + (error "spine-add-book: file %s does not exist; create shelves first" + org-file)) (with-current-buffer (find-file-noselect org-file) (unwind-protect (progn - (goto-char (point-max)) - ;; Insert new headline + ;; Find the shelf 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-demote-subtree) (insert title) - ;; Set TODO state - (org-todo "WANT") ;; Set properties (only non-empty) (when (and author (> (length author) 0)) (org-set-property "AUTHOR" author)) @@ -279,9 +291,6 @@ Signals an error on failure." (substring (sha1 (concat title (number-to-string (random)))) 0 4))) ;; Set last modified (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 (when (and initial-note (> (length initial-note) 0)) (let ((date (if (and date-added (> (length date-added) 0)) diff --git a/test/spine-add-book-test.el b/test/spine-add-book-test.el index de05b4b..c562999 100644 --- a/test/spine-add-book-test.el +++ b/test/spine-add-book-test.el @@ -16,18 +16,20 @@ (ignore-errors (delete-file spine-org-file))) (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) (unwind-protect (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))) (should books) (should (= (length books) 1)) (let ((book (car books))) (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 :shelf) "Fiction")) (should (plist-get book :id))))) (spine-add-book-test--cleanup))) @@ -36,6 +38,8 @@ (spine-add-book-test--cleanup) (unwind-protect (progn + (with-temp-file spine-org-file + (insert "* Fiction\n\n")) (spine-add-book :title "Full Test" :author "Jane Doe" @@ -45,8 +49,8 @@ :rec_by "Friend" :rec_note "You'll love this" :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)) (book (car books))) (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_note) "You'll love this")) (should (equal (plist-get book :added) "[2026-06-21]")) - (should (equal (plist-get book :tags) '("scifi"))) (should (equal (plist-get book :notes) '(("2026-06-21" "Looking forward to this")))))) (spine-add-book-test--cleanup))) @@ -67,10 +70,13 @@ (spine-add-book-test--cleanup) (unwind-protect (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)) (book (car books))) (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 :format)) (should-not (plist-get book :isbn)) @@ -81,7 +87,14 @@ "spine-add-book signals error when title is missing." (spine-add-book-test--cleanup) (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))) (ert-deftest spine-add-book-generates-unique-ids () @@ -89,10 +102,27 @@ (spine-add-book-test--cleanup) (unwind-protect (progn - (spine-add-book :title "Book One") - (spine-add-book :title "Book Two") + (with-temp-file spine-org-file + (insert "* Fiction\n\n")) + (spine-add-book :title "Book One" :shelf "Fiction") + (spine-add-book :title "Book Two" :shelf "Fiction") (let* ((books (spine-books)) (id1 (plist-get (nth 0 books) :id)) (id2 (plist-get (nth 1 books) :id))) (should (not (equal id1 id2))))) (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)))