Component relations

You are here: All Systems / webutils / relations

(defpackage :webutils.relations (:use :cl :webutils.simple-serialized-classes)
            (:export :define-object-relations)
)

(in-package :webutils.relations)
(webutils::export-all :webutils.relations)

;; Define a relation between objects. one-to-one and one-to-many
;; relations are supported, but many-to-many relations are not.

(defmacro define-object-relations
    (from-class &body relations)
  (let (registered-actions deleted-actions setf-before-actions setf-after-actions)
    (loop for relation in relations
          do
          (destructuring-bind (from-accessor to-accessor &key
                                             (type :one-to-one)
                                             (aggregator 'cons)
                                             (disaggregator 'remove)
)

              relation
            (push
             `(let ((value (funcall #',from-accessor a)))
               (when value
                 ,(ecase type
                         (:one-to-one
                          `(funcall #'(setf ,to-accessor) a value)
)

                         (:one-to-many
                          `(funcall #'(setf ,to-accessor)
                            (funcall (load-time-value (function ,aggregator))
                             a (funcall #',to-accessor value)
)

                            value
)
)
)

                 (when (typep value 'webutils.simple-serialized-classes:simple-serialized-class)
                   (serialize-instance value)
)
)
)
registered-actions
)

            (push
             `(let ((value (funcall #',from-accessor a)))
               (when value
                 ,(ecase type
                         (:one-to-one
                          `(funcall #'(setf ,to-accessor) nil value)
)

                         (:one-to-many
                          `(funcall #'(setf ,to-accessor)
                            (funcall (load-time-value (function ,disaggregator)) a (funcall #',to-accessor value))
                            value
)
)
)

                 (when (typep value 'webutils.simple-serialized-classes:simple-serialized-class)
                   (serialize-instance value)
)
)
)
deleted-actions
)

            (push
             `(let ((old-value (funcall #',from-accessor a)))
               (when old-value
                 ,(ecase type
                         (:one-to-one
                          `(funcall #'(setf ,to-accessor) nil old-value)
)

                         (:one-to-many
                          `(funcall #'(setf ,to-accessor)
                            (funcall (load-time-value (function ,disaggregator)) a (funcall #',to-accessor old-value))
                            old-value
)
)
)

                 (when (typep old-value 'webutils.simple-serialized-classes:simple-serialized-class)
                   (serialize-instance old-value)
)
)
)

             (cdr (or (assoc from-accessor setf-before-actions)
                      (car (push (cons from-accessor nil) setf-before-actions))
)
)
)

            (push
             `(when new-value
               ,(ecase type
                       (:one-to-one
                        `(funcall #'(setf ,to-accessor) a new-value)
)

                       (:one-to-many
                        `(funcall #'(setf ,to-accessor)
                          (funcall (load-time-value (function ,aggregator)) a (funcall #',to-accessor new-value))
                          new-value
)
)
)

               (when (typep new-value 'webutils.simple-serialized-classes:simple-serialized-class)
                 (serialize-instance new-value)
)
)

             (cdr (or (assoc from-accessor setf-after-actions)
                      (car (push (cons from-accessor nil) setf-after-actions))
)
)
)
)
)

    `(progn
      (defmethod note-registered-instance ((a ,from-class))
        ,@registered-actions
)

      (defmethod note-deleted-instance ((a ,from-class))
        ,@deleted-actions
)

      ,@ (loop for (from-accessor . actions) in setf-before-actions
          collect
          `(defmethod (setf ,from-accessor) :before (b (a ,from-class))
            ,@actions
)
)

      ,@ (loop for (from-accessor . actions) in setf-after-actions
          collect
          `(defmethod (setf ,from-accessor) :after (new-value (a ,from-class))
            ,@actions
)
)
)
)
)

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.