Hello all,
I propose that the types for easy-handlers be extensible by the users. I have pieces of markup which are used throughout our site which collect information from several fields into a class (in our case tied to a database with clsql). The names of the fields are related, but diffierent from the lists, arrays and hashes of the easy-handlers, Dates and addresses are two good examples, but there are others. I would like to be able to teach the easy handler to collect these for me and pass them as parameters. This cannot be done with functions as the :parameter-type because the functions passed as :parameter-type do not take the parameter name.
There are two ways I think this could be done:
- The easier way: add a :paramater-reader arg to the parameter description, and if passed use it in lieu of the ((post|get)-)?parameter functions in compute-parameter. The following might do it (untested):
(defun compute-parameter (parameter-name parameter-type request-type &optional parameter-reader) ; <===== "Computes and returns the parameter(s) called PARAMETER-NAME and converts it/them according to the value of PARAMETER-TYPE. REQUEST-TYPE is one of :GET, :POST, or :BOTH." (when (member parameter-type '(list array hash-table)) (setq parameter-type (list parameter-type 'string))) (let ((parameter-reader (or parameter-reader ; <==== (ecase request-type (:get #'get-parameter) (:post #'post-parameter) (:both #'parameter))) ) (parameters (and (listp parameter-type) (case request-type (:get (get-parameters)) (:post (post-parameters)) (:both (append (get-parameters) (post-parameters))))))) (cond ((atom parameter-type) (compute-simple-parameter parameter-name parameter-type parameter-reader)) ((and (null (cddr parameter-type)) (eq (first parameter-type) 'list)) (compute-list-parameter parameter-name (second parameter-type) parameters)) ((and (null (cddr parameter-type)) (eq (first parameter-type) 'array)) (compute-array-parameter parameter-name (second parameter-type) parameters)) ((and (null (cddddr parameter-type)) (eq (first parameter-type) 'hash-table)) (compute-hash-table-parameter parameter-name (second parameter-type) parameters (or (third parameter-type) 'string) (or (fourth parameter-type) 'equal))) (t (error "Don't know what to do with parameter type ~S." parameter-type)))))
(defun make-defun-parameter (description default-parameter-type default-request-type) "Creates a keyword parameter to be used by DEFINE-EASY-HANDLER. DESCRIPTION is one of the elements of DEFINE-EASY-HANDLER's LAMBDA-LIST and DEFAULT-PARAMETER-TYPE and DEFAULT-REQUEST-TYPE are the global default values." (when (atom description) (setq description (list description))) (destructuring-bind (parameter-name &key (real-name (compute-real-name parameter-name)) parameter-type init-form request-type parameter-reader ) ; <==== description `(,parameter-name (or (and (boundp '*request*) (compute-parameter ,real-name ,(or parameter-type default-parameter-type) ,(or request-type default-request-type))) ,init-form))))
(This just occurred to me: request-type could be a function, with an obvious change to the ecase. This is a little more confusing but consistent with what parameter-type does---so it has the force of precedent---and requires a smaller change to the code.).
- The extensive way: Change the cond in compute-parameter to a switch via a hash table, whose values are the conversion handlers (or a cons with reader and converter). It may need two hash tables: one for simple type names (type is a symbol) and one for aggregates, (type is a cons, and switch on the first element). Create a macro, define-easy-type, or something, to populate the tables, and use it within easy-handlers.lisp to populate it with those which are now hardcoded in the cond and case switches. Users can then use the macro to define their own types.
- Another way: like above, but use methods instead of hash tables. This seems possible, but I'd have to think some more about it.
BTW: why the checks for null CDDRs and CDDDDRs in compute-parameter? Is this just nice, or are they essential?
If your interested in the second, I will work something up and send in a patch. I do not think it would break any existing code which did not peak too far into the easy-handlers stuff.
Let me know what you think.
Tim S
P.S.: Hunchentoot is a pleasure to work with!