Lisp ninja 2 - Refactoring 2
Last post I talked about creating an abstraction using Lisp macros to assist me with my unit tests. If you look at the code, there is an obvious flaw. get-result-for-test-case hard codes the target PHP function in it. It wouldn't be very nice to rewrite a new version of it every time I need to call a different PHP function.
In the original code from last post, the target PHP function was defined in string format. Since I'm working in Lisp I would prefer to define it in sexp format to gain the most flexibility. For example, calling the explode function in PHP would look like the following:
(explode " " "aaron feng")Let's start the refactoring:
; helper function
(defun quote-string (input)
(if (typep input 'string)
(format nil "\"~a\"" input)
input))
(defun get-function-signature (function-info)
(let ((function (first function-info))
(parameters (rest function-info))
(signature "")
(delimiter ""))
(setf signature (format nil "~a~a" function "("))
(loop for parameter in parameters do
(setf signature (format nil "~a~a~a"
signature
delimiter
(quote-string parameter)))
(setf delimiter ", "))
(setf signature (format nil "~a~a" signature ")"))))get-function-signature does all the dirty work by converting input function in sexp format to a string that we can eventually send to PHP. Let's incorporate this function back into get-result-for-test-case, so it can accept other functions.
(defun get-result-for-test-case (func-info)
(with-output-to-string (str)
(let ((func-string
(concatenate 'string "print_r(" (get-function-signature func-info) ");")))
(run-program *php-bin* (list
"-r"
func-string)
:output str))))With these changes, it will make get-result-for-test-case much more generic. However, there is another problem, not all PHP functions return a string. If the function returns an array, the output will always be "Array". It would be nice if it could convert PHP array back into Lisp list, so it can be used for comparison in the tests. That is exactly what I'm going to do next. Notice that I replaced the "echo" with "print_r", so it will output human readable values for data structure such as array.
(defun parse-php-array (array)
(let ((parts (split-sequence #\Newline array))
(output))
(dolist (part parts)
(let ((value (get-array-value part)))
(format t "~a" part)
(when value (push value output))))
(reverse output)))Now if I wrap get-result-for-test-case with parse-php-array, it should convert the output into a Lisp list.
(parse-php-array (get-result-for-test-case `(explode " " "aaron feng")))Of course, more work can be done to make the code more intelligent, such as, only try to parse array if the output is an array type. Currently the code can only handle Array and String as return type, but it can be extended further if desired.

Wow, lots of spam here. Maybe you should turn on comment approval or some sort of CAPTCHA...
Anyway, here's my Shinobi take of GET-FUNCTION-SIGNATURE:
And here's another take on PARSE-PHP-ARRAY using LOOP:
I'm not sure why you want to reverse the values at the end, but here it is.