пятница, 26 февраля 2010 г.

Парсинг переменных в cl-routes

Я очень давно не трогал cl-routes, меня там далеко не всё устраивало с точки зрения реализации (имеет место некоторый бардак), но оно работало и мотивации для переработки у меня не было. Однако, хотя оно и работало, был один момент, который можно считать явным и серьёзным недостатком: не было возможности предъявить какие-либо требования к возможным значениям параметров шаблонов url. Например, в том же Django для задания шаблонов url используются регулярные выражения и это позволяет, например, задать что какой-нибудь параметр id может содержать только цифры.

Не знаю, почему я не сделал этого ещё год назад, но сейчас залез в исходный код, удалил несколько мертворождённых идей и добавил даже, как мне кажется, лучшую возможность: теперь можно при создании маршрута указывать пользовательскую функцию для парсинга переменной шаблона. Например:
CL-USER>(defparameter *map* (make-instance 'routes:mapper))
CL-USER>(routes:connect *map* (routes:make-route "/foo/:id" (list :id #'parse-integer)))
CL-USER>(routes:match *map* "/foo/1")
(#<ROUTES:ROUTE {...}> (:ID . 1))
CL-USER>(routes:match *map* "/foo/hello")
NIL
В данном примере используется стандартная функция #'parse-integer, что позволяет убедиться в том, что параметр id содержит только цифры, а кроме того - в случае успешного сопоставления url шаблону в параметре :id будет не строка, а целое число.

Подобным образом можно использовать любую функцию, которая должна принимать строку, а возвращать объект, который будет сопоставлен переменной шаблона. Если строка не соответствует требованиям, то эта функция должна вернуть nil или сигнализировать об ошибке (эта возможность добавлена для возможности непосредственного использования #'parse-integer)

Соответственно, тут же немного доработал RESTAS, например теперь возможно задавать маршруты следующим образом:
(define-route myroute ("/foo/:id" :parse-vars (list :id #'parse-integer))
...)
Для использования этой новой возможности необходимо использовать git-версии cl-routes и RESTAS. Впрочем, новые релизы я думаю будут довольно скоро.

4 комментария:

  1. Когда же я найду время попробовать всё это?...

    ОтветитьУдалить
  2. @andy128k
    Э... Ты хочешь у меня это узнать? ;)

    ОтветитьУдалить
  3. почему не?

    define-route myroute ("/foo/:id" :parse-vars (:id #'parse-integer))


    зачем лишний вызов list ?

    ОтветитьУдалить
  4. @kmmbvnr
    Что бы можно было один plist, определённый в другом месте, использовать для всех маршрутов. В этом есть смысл, если использовать стандартные имена переменных в маршрутах.

    ОтветитьУдалить