Author: hhubner Date: Sat Feb 16 15:00:17 2008 New Revision: 2516
Modified: branches/trunk-reorg/bknr/web/src/web/handlers.lisp Log: Docstrings
Modified: branches/trunk-reorg/bknr/web/src/web/handlers.lisp ============================================================================== --- branches/trunk-reorg/bknr/web/src/web/handlers.lisp (original) +++ branches/trunk-reorg/bknr/web/src/web/handlers.lisp Sat Feb 16 15:00:17 2008 @@ -66,7 +66,11 @@ :import-spool-directory #p"/home/bknr/spool/" :template-base-directory nil :template-command-packages nil - :rss-feed-url nil)) + :rss-feed-url nil) + (:documentation "Class to hold all information on a web server that +is served within BKNR. Currently, this is a singleton object, and +*WEBSITE* will point to the only instance. Eventually, multiple +WEBSITE instances for virtual hosts may be supported."))
(defmethod initialize-instance :after ((website website) &key &allow-other-keys) (when *website* @@ -86,9 +90,20 @@ (defmethod website-make-path ((website website) path) (format nil "~A~A" (website-base-href website) (relative path)))
-(defgeneric publish-handler (website handler)) -(defgeneric handler-matches (handler)) -(defgeneric publish-site (website)) +(defgeneric publish-handler (website handler) + (:documentation "Publish HANDLER on WEBSITE, thereby adding it to +the chain of handlers that is searched to handle an incoming +request.")) + +(defgeneric handler-matches-p (handler) + (:documentation "Determine whether HANDLER is willing to handle the +current request. Returns non-NIL if the HANDLER wants to handle the request +NIL otherwise.")) + +(defgeneric publish-site (website) + (:documentation "Publish all handlers defined in WEBSITE. + +XXX When is this called?"))
(defun handler-definition-name (handler-definition) (first handler-definition)) @@ -191,9 +206,21 @@ (print-unreadable-object (handler stream :type t) (format stream "~A" (page-handler-prefix handler))))
-(defgeneric handle (page-handler)) -(defgeneric authorized-p (page-handler)) -(defgeneric page-handler-url (page-handler)) +(defgeneric handle (page-handler) + (:documentation "Handle an incoming HTTP request, returning either a +string or an (array (unsigned-byte 8) (*)) with the response +contents. Alternatively, the handler may call (SEND-HEADERS) to +get access to the response stream and output the data to it.")) + +(defgeneric authorized-p (page-handler) + (:documentation "Return non-nil if the request is authorized to be +executed on PAGE-HANDLER + +XXX wouldn't it be better if handler-matches-p checked +authorization?")) + +(defgeneric page-handler-url (page-handler) + (:documentation "Return the full base URL for PAGE-HANDLER."))
(defmethod handler-path ((handler page-handler)) (subseq (script-name) @@ -266,7 +293,7 @@
(defun bknr-dispatch (request) (declare (ignore request)) - (let ((handler (find-if #'handler-matches (website-handlers *website*)))) + (let ((handler (find-if #'handler-matches-p (website-handlers *website*)))) (cond (handler (start-session) @@ -279,7 +306,7 @@ (defmethod publish-handler ((website website) (handler page-handler)) (setf *handlers* (append *handlers* (list handler))))
-(defmethod handler-matches ((handler page-handler)) +(defmethod handler-matches-p ((handler page-handler)) (string-equal (page-handler-prefix handler) (script-name)))
@@ -296,7 +323,10 @@ (redirect (random-elt (redirect-handler-to page-handler))))
(defclass form-handler (page-handler) - ()) + () + (:documentation "A FORM-HANDLER is a handler that processes form +submissions. The handler generic function for FORM-HANDLER subclasses +is called HANDLE-FORM."))
(define-condition form-condition (condition) ((reason :initarg :reason @@ -315,7 +345,12 @@ (signal (make-condition 'form-field-missing-condition :field ',field-name))))
-(defgeneric handle-form (page-handler action)) +(defgeneric handle-form (page-handler action) + (:documentation "Handle form submission for PAGE-HANDLER. The +form variable "action" will be parsed into a keyword and passwd to +the invocation of this generic function as ACTION. Methods are meant +to specialize on individual keywords to handle different actions that +the form supports."))
(defmethod handle ((page-handler form-handler)) (let* ((form (query-param "action")) @@ -323,7 +358,11 @@ (handle-form page-handler form-keyword)))
(defclass prefix-handler (page-handler) - ()) + () + (:documentation "A PREFIX-HANDLER is a handler that is invoked for +URLs with a certain prefix, as determined by the :PREFIX +initialization argument. It is used as a mixin class and only +provides for a HANDLER-MATCHES-P method."))
#+(or) (defmethod initialize-instance :after ((handler prefix-handler) &key) @@ -331,7 +370,7 @@ (1- (length (page-handler-prefix handler))))) (warn "prefix handler ~A does not have prefix ending with / - may match unexpectedly" handler)))
-(defmethod handler-matches ((handler prefix-handler)) +(defmethod handler-matches-p ((handler prefix-handler)) (and (>= (length (script-name)) (length (page-handler-prefix handler))) (string-equal (page-handler-prefix handler) @@ -340,20 +379,26 @@
(defclass directory-handler (prefix-handler) ((destination :initarg :destination - :reader page-handler-destination))) + :reader page-handler-destination)) + (:documentation + "Handler for a directory in the file system. Publishes all files +in the directory DESTINATION under their relative path name.")) + +(defgeneric request-relative-pathname (directory-handler) + (:documentation "Return the relative pathname for the current +request as determined by DIRECTORY-HANDLER.") + (:method ((handler directory-handler)) + (or (aux-request-value 'request-relative-pathname) + (setf (aux-request-value 'request-relative-pathname) + (pathname (subseq (script-name) (1+ (length (page-handler-prefix handler)))))))))
-(defmethod request-pathname ((handler directory-handler)) - (or (aux-request-value 'request-pathname) - (setf (aux-request-value 'request-pathname) - (subseq (script-name) (1+ (length (page-handler-prefix handler))))))) - -(defmethod handler-matches ((handler directory-handler)) +(defmethod handler-matches-p ((handler directory-handler)) (and (call-next-method) - (probe-file (merge-pathnames (request-pathname handler) + (probe-file (merge-pathnames (request-relative-pathname handler) (page-handler-destination handler)))))
(defmethod handle ((handler directory-handler)) - (handle-static-file (merge-pathnames (request-pathname handler) + (handle-static-file (merge-pathnames (request-relative-pathname handler) (page-handler-destination handler))))
(defclass file-handler (page-handler) @@ -361,34 +406,64 @@ :reader page-handler-destination) (content-type :initarg :content-type :reader page-handler-content-type)) - (:default-initargs :content-type "text/plain")) + (:default-initargs :content-type "text/plain") + (:documentation "A FILE-HANDLER is used to publish a single file +under an URL. :DESTINATION is the pathname of the file to publish, +:CONTENT-TYPE is the content type to use."))
(defmethod handle ((handler file-handler)) - (handle-static-file (page-handler-destination handler))) + (handle-static-file (page-handler-destination handler) (page-handler-content-type handler)))
(defclass object-handler (prefix-handler) ((query-function :initarg :query-function :reader object-handler-query-function) (object-class :initarg :object-class :reader object-handler-object-class)) - (:default-initargs :object-class t :query-function nil)) - -(defgeneric object-handler-get-object (object-handler)) -(defgeneric handle-object (object-handler object)) - -(defmethod object-handler-get-object ((handler object-handler)) - (let ((id (parse-url))) - (when id - (find-store-object id - :class (object-handler-object-class handler) - :query-function (object-handler-query-function handler))))) + (:default-initargs :object-class t :query-function nil) + (:documentation "An OBJECT-HANDLER handles requests for a persistent +object in the BKNR datastore. The first component of the relative +path of the request is used as the key to locate the object. The +object is looked up using the OBJECT-HANDLER-GET-OBJECT generic +function, which will, by default, call FIND-STORE-OBJECT with the key +as argument. + +The :QUERY-FUNCTION initarg may be supplied to set up a query function +that maps from the key to an object. It is typically used to +implement lookup by name. + +The :OBJECT-CLASS initarg may be supplied to restrict the handler to +operate on objects of that class or its subclass. If the key +specifies the ID of an object that has a different class or if the +QUERY-FUNCTION returns an object of a different class, a run-time +error is signaled.")) + +(defgeneric object-handler-get-object (object-handler) + (:documentation "Implement object lookup. Methods return the object +that the current request should operate upon. The default method for +the OBJECT handler class performs a lookup using FIND-STORE-OBJECT.") + (:method ((handler object-handler)) + (let ((id (parse-url))) + (when id + (find-store-object id + :class (object-handler-object-class handler) + :query-function (object-handler-query-function handler)))))) + +(defgeneric handle-object (object-handler object) + (:documentation "Handle the current to the OBJECT-HANDLER. OBJECT +is the object as looked up by OBJECT-HANDLER-GET-OBJECT."))
(defmethod handle ((handler object-handler)) (let ((object (object-handler-get-object handler))) (handle-object handler object)))
(defclass edit-object-handler (form-handler object-handler) - ()) - -(defgeneric handle-object-form (handler action object)) + () + (:documentation "Combined FORM-HANDLER and OBJECT-HANDLER class that +is used as a base class for handlers that edit an object using a HTML +form.")) + +(defgeneric handle-object-form (handler action object) + (:documentation "Perform ACTION, a keyword that has been determined by +parsing the "action" input element of the current HTML form, on +OBJECT, which is parsed using the mechanism of an OBJECT-HANDLER."))
(defmethod handle-form ((handler edit-object-handler) action) (let ((object (object-handler-get-object handler)))