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!