[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/ebdb 7bed578 11/33: Provide more careful control of fin
From: |
Eric Abrahamsen |
Subject: |
[elpa] externals/ebdb 7bed578 11/33: Provide more careful control of finding related records |
Date: |
Sun, 3 Sep 2017 17:02:21 -0400 (EDT) |
branch: externals/ebdb
commit 7bed578e827be49a337ad715f6c9d67b3a98eb9c
Author: Eric Abrahamsen <address@hidden>
Commit: Eric Abrahamsen <address@hidden>
Provide more careful control of finding related records
* ebdb.el (ebdb-related-unfound): New error.
(ebdb-record-related): Raise the new error in this method, if no
related record is found. Also, raise it in the base method.
(ebdb-init-field, ebdb-delete-field, ebdb-string): Use method and
error in these methods for role fields.
(ebdb-record-related): Raise the error in all implementations of
this method.
* ebdb-com.el (ebdb-fmt-field): We can now collapse formatting of
roles-on-people and roles-on-organizations into one method.
(ebdb-follow-related): Handle error here.
* ebdb-test.el (ebdb-test-with-records): Also shadow the
ebdb-org-hashtable variable, even though we didn't end up using it
here.
(ebdb-cant-find-related-role): New sanity test for raising the
error.
(ebdb-unload-db-with-relations): New test for breaking cross-db
relations.
---
ebdb-com.el | 58 +++++++++++++------------------
ebdb-test.el | 44 ++++++++++++++++++++++-
ebdb.el | 112 ++++++++++++++++++++++++++++++++++++-----------------------
3 files changed, 135 insertions(+), 79 deletions(-)
diff --git a/ebdb-com.el b/ebdb-com.el
index 3310fc0..c42cc08 100644
--- a/ebdb-com.el
+++ b/ebdb-com.el
@@ -471,32 +471,21 @@ property is the field instance itself."
(cl-defmethod ebdb-fmt-field ((fmt ebdb-formatter-ebdb)
(field ebdb-field-role)
_style
- (record ebdb-record-organization))
- (let* ((person (ebdb-gethash (slot-value field 'record-uuid) 'uuid))
- (mail (slot-value field 'mail))
- (value (if mail
- (format "%s (%s)"
- (ebdb-string person)
- (ebdb-fmt-field fmt mail 'oneline record))
- (ebdb-string person))))
- (if (slot-value field 'defunct)
- (propertize value 'face 'ebdb-defunct)
- value)))
-
-(cl-defmethod ebdb-fmt-field ((fmt ebdb-formatter-ebdb)
- (field ebdb-field-role)
- _style
- (record ebdb-record-person))
- (let* ((org (ebdb-gethash (slot-value field 'org-uuid) 'uuid))
- (mail (slot-value field 'mail))
- (value (if mail
- (format "%s (%s)"
- (ebdb-string org)
- (ebdb-fmt-field fmt mail 'oneline record))
- (ebdb-string org))))
- (if (slot-value field 'defunct)
- (propertize value 'face 'ebdb-defunct)
- value)))
+ (record ebdb-record))
+ (with-slots (mail defunct) field
+ (let* ((rec-string
+ (condition-case nil
+ (ebdb-record-name
+ (ebdb-record-related record field))
+ (ebdb-related-unfound "record not loaded")))
+ (value (if mail
+ (format "%s (%s)"
+ rec-string
+ (ebdb-fmt-field fmt mail 'oneline record))
+ rec-string)))
+ (if defunct
+ (propertize value 'face 'ebdb-defunct)
+ value))))
(defsubst ebdb-indent-string (string column)
"Indent nonempty lines in STRING to COLUMN (except first line).
@@ -1192,14 +1181,15 @@ With prefix N move backwards N (sub)fields."
related record."
(interactive (list (ebdb-current-record)
(ebdb-current-field)))
- (let ((related (ebdb-record-related record field)))
- (if related
- (ebdb-display-records (cons related
- (mapcar #'car ebdb-records))
- ebdb-default-multiline-formatter
- t)
- (message "Field %s provides no relationships"
- (ebdb-field-readable-name field)))))
+ (condition-case nil
+ (ebdb-display-records
+ (cons (ebdb-record-related record field)
+ (mapcar #'car ebdb-records))
+ ebdb-default-multiline-formatter
+ t)
+ (ebdb-related-unfound
+ (message "Field %s provides no relationships"
+ (ebdb-field-readable-name field)))))
(defun ebdb-toggle-record-mark (record &optional mark)
"Mark or unmark RECORD."
diff --git a/ebdb-test.el b/ebdb-test.el
index f46c7a5..d676759 100644
--- a/ebdb-test.el
+++ b/ebdb-test.el
@@ -27,6 +27,7 @@
(require 'ert)
(require 'ebdb)
+(require 'ebdb-com)
(require 'ebdb-snarf)
(require 'ebdb-vcard)
@@ -40,7 +41,7 @@
:dirty t))
ebdb-db-list)
;; Save sets sync-time.
- (ebdb-db-save ,(car db-and-filename))
+ (ebdb-db-save ,(car db-and-filename) nil t)
;; Load adds to `ebdb-db-list'.
(ebdb-db-load ,(car db-and-filename))
(unwind-protect
@@ -52,6 +53,7 @@
"Don't let EBDB tests pollute `ebdb-record-tracker'."
(declare (indent 0) (debug t))
`(let ((ebdb-hashtable (make-hash-table :test 'equal))
+ (ebdb-org-hashtable (make-hash-table :test 'equal))
ebdb-record-tracker)
,@body))
@@ -130,6 +132,46 @@
rec (ebdb-parse 'ebdb-field-mail "address@hidden"))
:type 'ebdb-readonly-db))))))
+(ert-deftest ebdb-cant-find-related-role ()
+ "Find org record from a role field.
+If it doesn't exist, raise `ebdb-related-unfound'."
+ (ebdb-test-with-records
+ (let ((rec (make-instance
+ 'ebdb-record-person
+ :uuid (make-instance 'ebdb-field-uuid :uuid "bob")))
+ (role
+ (make-instance
+ 'ebdb-field-role :record-uuid "bob"
+ :org-uuid "bogus")))
+ (ebdb-record-insert-field rec role)
+ (should-error
+ (ebdb-record-related rec role)
+ :type 'ebdb-related-unfound))))
+
+(ert-deftest ebdb-unload-db-with-relations ()
+ "Cross-db relations break correctly when a db is unloaded."
+ (ebdb-test-with-records
+ (ebdb-test-with-database (db1 ebdb-test-database-1)
+ (ebdb-test-with-database (db2 ebdb-test-database-2)
+ (let ((rec1 (make-instance 'ebdb-record-person))
+ (rec2 (make-instance 'ebdb-record-person))
+ rel-f)
+ (ebdb-db-add-record db1 rec1)
+ (ebdb-db-add-record db2 rec2)
+ (setq rel-f (make-instance
+ 'ebdb-field-relation :rel-uuid (ebdb-record-uuid rec2)))
+ (ebdb-record-insert-field rec1 rel-f)
+ (ebdb-db-unload db2)
+ (should-error
+ (ebdb-record-related rec1 rel-f)
+ :type 'ebdb-related-unfound)
+ (should
+ (string=
+ (ebdb-fmt-field
+ ebdb-default-multiline-formatter
+ rel-f 'oneline rec1)
+ "record not loaded")))))))
+
(ert-deftest ebdb-test-with-record-edits ()
"Test the `ebdb-with-record-edits' macro."
(ebdb-test-with-records
diff --git a/ebdb.el b/ebdb.el
index 1b3ebe3..184f90f 100644
--- a/ebdb.el
+++ b/ebdb.el
@@ -801,6 +801,8 @@ if you want to call `ebdb-change-hook' and update the
record unconditionally.")
(define-error 'ebdb-duplicate-uuid "Duplicate EBDB UUID" 'ebdb-error)
+(define-error 'ebdb-related-unfound "Could not find related record"
'ebdb-error)
+
(define-error 'ebdb-unsynced-db "EBDB DB unsynced" 'ebdb-error)
(define-error 'ebdb-disabled-db "EBDB DB disabled" 'ebdb-error)
@@ -1399,51 +1401,59 @@ first one."
(cl-defmethod ebdb-init-field ((role ebdb-field-role) &optional record)
(when record
- (let* ((org-uuid (slot-value role 'org-uuid))
- (org (ebdb-gethash org-uuid 'uuid))
- (org-string (if org (ebdb-record-name org)
- "record not loaded"))
- ;; TODO: Guard against org-entry not being found.
- (org-entry (gethash org-uuid ebdb-org-hashtable))
- (record-uuid (ebdb-record-uuid record))
- (role-mail (slot-value role 'mail)))
- ;; Setting the 'record-uuid slot value when it wasn't set before
- ;; technically means that the record is now "dirty". That's
- ;; okay in our current database implementation, because
- ;; `ebdb-record-insert-field' first calls
- ;; `ebdb-db-add-record-field', which sets the record "dirty",
- ;; and then calls this `ebdb-init' method -- ie, record is
- ;; "dirty" when we get here. Theoretically, however, nothing in
- ;; `ebdb-init-field' should change a record's slots.
- (unless (slot-value role 'record-uuid)
- (setf (slot-value role 'record-uuid) record-uuid))
- (object-add-to-list (ebdb-record-cache record) 'organizations org-string)
- ;; Init the role mail against the record.
- (when (and role-mail (slot-value role-mail 'mail))
- (ebdb-init-field role-mail record))
- ;; Make sure this role is in the `ebdb-org-hashtable'.
- (unless (member role org-entry)
- (push role org-entry))
- (puthash org-uuid org-entry ebdb-org-hashtable)))
+ (with-slots (org-uuid mail (role-record-uuid record-uuid)) role
+ (let* (;; TODO: Guard against org-entry not being found.
+ (org-entry (gethash org-uuid ebdb-org-hashtable))
+ (record-uuid (ebdb-record-uuid record))
+ (org-string
+ (condition-case nil
+ (ebdb-record-name
+ (ebdb-record-related record role))
+ (ebdb-related-unfound
+ "record not loaded"))))
+
+ ;; Setting the 'record-uuid slot value when it wasn't set before
+ ;; technically means that the record is now "dirty". That's
+ ;; okay in our current database implementation, because
+ ;; `ebdb-record-insert-field' first calls
+ ;; `ebdb-db-add-record-field', which sets the record "dirty",
+ ;; and then calls this `ebdb-init' method -- ie, record is
+ ;; "dirty" when we get here. Theoretically, however, nothing in
+ ;; `ebdb-init-field' should change a record's slots.
+ (unless role-record-uuid
+ (setf role-record-uuid record-uuid))
+ (object-add-to-list (ebdb-record-cache record) 'organizations
org-string)
+ ;; Init the role mail against the record.
+ (when (and mail (slot-value mail 'mail))
+ (ebdb-init-field mail record))
+ ;; Make sure this role is in the `ebdb-org-hashtable'.
+ (unless (member role org-entry)
+ (push role org-entry))
+ (puthash org-uuid org-entry ebdb-org-hashtable))))
(cl-call-next-method))
(cl-defmethod ebdb-delete-field ((role ebdb-field-role) &optional record
unload)
(when record
(let* ((org-uuid (slot-value role 'org-uuid))
- (org (ebdb-gethash org-uuid 'uuid))
(org-string
- (if org
- (ebdb-record-name org)
- "bogus"))
+ (condition-case nil
+ (ebdb-record-name
+ (ebdb-record-related record role))
+ (ebdb-related-unfound
+ nil)))
(org-entry (gethash org-uuid ebdb-org-hashtable))
(record-uuid (ebdb-record-uuid record)))
(setq org-entry (delete role org-entry))
(if org-entry
(puthash org-uuid org-entry ebdb-org-hashtable)
(remhash org-uuid ebdb-org-hashtable))
- (when (null (assoc-string record-uuid (object-assoc-list 'record-uuid
org-entry)))
+ (when (and org-string
+ (null (assoc-string
+ record-uuid
+ (object-assoc-list 'record-uuid org-entry))))
;; RECORD no long has any roles at ORG.
- (object-remove-from-list (ebdb-record-cache record) 'organizations
org-string))))
+ (object-remove-from-list (ebdb-record-cache record)
+ 'organizations org-string))))
(when (slot-value role 'mail)
(ebdb-delete-field (slot-value role 'mail) record unload))
(cl-call-next-method))
@@ -1464,10 +1474,12 @@ first one."
;; This is used in person records headers, so it just shows the
;; organization name. Perhaps this could have a multi-line option
;; later.
- (let ((org (ebdb-gethash (slot-value role 'org-uuid) 'uuid)))
- (if org
- (ebdb-string org)
- "record not loaded")))
+ (let ((rec (ebdb-gethash (slot-value role 'record-uuid) 'uuid)))
+ (condition-case nil
+ (ebdb-record-name
+ (ebdb-record-related rec role))
+ (ebdb-related-unfound
+ "record not loaded"))))
;;; Mail fields.
@@ -2596,11 +2608,6 @@ only return fields that are suitable for user editing.")
(cl-defmethod ebdb-record-alt-names ((record ebdb-record))
(slot-value (ebdb-record-cache record) 'alt-names))
-(cl-defmethod ebdb-record-related ((_record ebdb-record)
- (_field ebdb-field))
- "Provide a base method that does nothing."
- nil)
-
(when (fboundp 'cl-print-object)
(cl-defmethod cl-print-object ((record ebdb-record) stream)
(princ (format "#<%S %s>"
@@ -2608,6 +2615,17 @@ only return fields that are suitable for user editing.")
(ebdb-string record))
stream)))
+(cl-defgeneric ebdb-record-related (record field)
+ "Return the record related to RECORD, according to FIELD.
+This method is implemented for role fields, and relation fields.
+It is responsible for returning the related record as specified
+by the field, or else raising the error `ebdb-related-unfound'.")
+
+(cl-defmethod ebdb-record-related ((_record ebdb-record)
+ (_field ebdb-field))
+ "Provide a base method that raises `ebdb-related-unfound'."
+ (signal 'ebdb-related-unfound))
+
;; The following functions are here because they need to come after
;; `ebdb-record' has been defined.
@@ -2958,11 +2976,15 @@ priority."
(cl-defmethod ebdb-record-related ((_record ebdb-record-person)
(field ebdb-field-relation))
- (ebdb-gethash (slot-value field 'rel-uuid) 'uuid))
+ (or
+ (ebdb-gethash (slot-value field 'rel-uuid) 'uuid)
+ (signal 'ebdb-related-unfound (list (slot-value field 'rel-uuid)))))
(cl-defmethod ebdb-record-related ((_record ebdb-record-person)
(field ebdb-field-role))
- (ebdb-gethash (slot-value field 'org-uuid) 'uuid))
+ (or
+ (ebdb-gethash (slot-value field 'org-uuid) 'uuid)
+ (signal 'ebdb-related-unfound (list (slot-value field 'org-uuid)))))
(cl-defmethod ebdb-record-organizations ((record ebdb-record-person))
"Return a list of organization string names from RECORD's cache."
@@ -3202,7 +3224,9 @@ appropriate person record."
(cl-defmethod ebdb-record-related ((_record ebdb-record-organization)
(field ebdb-field-role))
- (ebdb-gethash (slot-value field 'record-uuid) 'uuid))
+ (or
+ (ebdb-gethash (slot-value field 'record-uuid) 'uuid)
+ (signal 'ebdb-related-unfound (list (slot-value field 'record-uuid)))))
(cl-defmethod ebdb-record-add-org-role ((record ebdb-record-person)
(org ebdb-record-organization)
- [elpa] externals/ebdb 235dad2 10/33: Fix to database disabling, (continued)
- [elpa] externals/ebdb 235dad2 10/33: Fix to database disabling, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 3d819bc 17/33: Have ebdb-do-records filter out stub uuids, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 423be4d 14/33: Add ebdb-field-singleton abstract field class, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 9b07ad6 15/33: Add gender field class, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 123ebae 09/33: Base ebdb-db-save method should be on ebdb-db class, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 63509c6 01/33: Work on getting ebdb-vm.el back into place, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 8291f3d 13/33: Refine database disabling/re-enabling, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 21ed4b7 19/33: Don't hide cl-print-object definitions, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 99a1563 12/33: Fix custom type for database buffer-char slot, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb aa668d7 08/33: Add "force" argument to ebdb-db-save, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 7bed578 11/33: Provide more careful control of finding related records,
Eric Abrahamsen <=
- [elpa] externals/ebdb 9b1a2cf 25/33: Use simpler home-grown version of char-fold-to-regexp, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb c362c2a 23/33: Protect against searching labeled fields with no label, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 9ff8795 30/33: Alter migration process to convert various folder fields, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb af264e3 18/33: Stop pretending we don't depend on calendar.el, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 764d89d 21/33: Provide ebdb-load guard in mua-auto-update, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 70ef68e 22/33: Fix compiler warnings, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 886c134 27/33: Add new ebdb-field-mail-folder fieldclass, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb 823a7d4 29/33: Use value of ebdb-mua-folder-list in VM splitting, Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb a5ffda9 33/33: Merge remote-tracking branch 'elpa/externals/ebdb', Eric Abrahamsen, 2017/09/03
- [elpa] externals/ebdb d6b9b77 06/33: Re-remove ebdb-vm, Eric Abrahamsen, 2017/09/03