Event Driven Architecture Programming in Lisp Part 2

References for our discussion:

Requirements for our EDA:

  • Storage of events to be executed when events triggered.
  • Events.
  • Push/Pull Model.
  • Broadcast of events. One trigger can execute more than one handler.
    • Ex. When User Clicks Save Button, data is saved and the event is logged as executed.


The storage mechanism is nothing more than hash-table.

(defobj handler-table
  (:members ((events (make-hash-table))))
  (:methods ((:can-fire (event)
               (let ((handler (make-events-handler)))
                 (if (null (gethash event events))
                     (setf (gethash event events) handler))))

             (:when-fire (event expression)
               (let ((handler (gethash event events)))
                 (subscribe handler expression)
                 (setf (gethash event events) handler)))

             (:listeners? (event)
               (let ((handler (gethash event events)))
                 (if (eq (h-count handler) 0) nil t)))

             (:fire (event &optional args)
               (let ((handler (gethash event events)))
                 (cond
                   (args (trigger handler args))
                   (t (trigger handler)))))))

The methods utilized by the table:

  • can-fire
    • This creates a hash for the event name.
  • when-fire
    • This subscribes the event.
  • listeners?
    • Asks if there are any functions stored in events-handler (list of events).
  • fire
    • Triggers all events in the events list.

Here is the unit tests:

(test-fixture handler-table
    (:setup
      ((handlers (make-handler-table))))

  (:tests

    (should-have-zero-listeners
     (can-fire handlers :whatever-event)
     (assert-false (listeners? handlers :whatever-event)))

    (should-not-make-new-events-handler-or-overwrite-events-handler
     (can-fire handlers :whatever-event)
     (when-fire handlers :whatever-event (lambda () (+ 1 1)))
     (can-fire handlers :whatever-event)
     (assert-true (listeners? handlers :whatever-event)))

    (should-trigger-events
     (can-fire handlers :whatever-event)
     (assert-false (listeners? handlers :whatever-event))
     (when-fire handlers :whatever-event (lambda () (+ 1 1)))
     (assert-equal t (listeners? handlers :whatever-event))
     (assert-equal 1 (fire handlers :whatever-event))
     (when-fire handlers :whatever-event (lambda () (+ 1 20)))
     (assert-equal 2 (fire handlers :whatever-event)))

    (should-fire-push-model-events
     (can-fire handlers :whatever-event)
     (when-fire handlers :whatever-event (lambda (x) (+ x 1)))
     (assert-equal 1 (fire handlers :whatever-event 21))))

The items stored in the table are lists of closures.

The events list:

(defobj events-handler
  (:members ((events (make-list 0))))
  (:methods ((:h-count () (length events))
             (:subscribe (event) (cond ((functionp event)
                                        (push event events))))
             (:trigger (&optional args)
               (let ((trigger-count 0))
                 (dolist (event events)
                   (cond
                     (args (funcall event args))
                     (t (funcall event)))
                   (incf trigger-count))
                 trigger-count))))

The list utilizes several methods.

  • h-count
    • Returns the number events in the list.
  • subscribe (event)
    • Stores the event (closure/function). It verifies that the event is actually a function before storing it.
  • trigger (&optional args)
    • Iterates and executes each function stored in list. It returns the actual number of functions triggered.

Here are the unit tests:

(test-fixture events
    (:setup
      ((handler (make-events-handler))))

  (:tests

    (should-retrieve-handlers-count-of-zero
     (assert-equal 0 (h-count handler)))

    (should-not-add-handler
     (subscribe handler :nothing)
     (assert-equal 0 (h-count handler)))

    (should-add-handler
     (subscribe handler (lambda () 1))
     (assert-equal 1 (h-count handler))
     (subscribe handler (lambda () 1))
     (assert-equal 2 (h-count handler)))

    (should-add-push-model-handler
     (subscribe handler (lambda (x) x))
     (assert-equal 1 (h-count handler)))

    (should-not-trigger-with-no-events
     (assert-equal 0 (trigger handler)))

    (should-trigger-push-model
     (subscribe handler (lambda (x) x))
     (assert-equal 1 (trigger handler 1)))

    (should-trigger-two-events-in-handler
     (subscribe handler (lambda () 1))
     (subscribe handler (lambda () 1))
     (assert-equal 2 (h-count handler))
     (assert-equal 2 (trigger handler))))

Happy Days!

Advertisements

One Response to Event Driven Architecture Programming in Lisp Part 2

  1. […] Event Driven Architecture Programming in Lisp Part 2 […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: