Doing the tutorial on writing a blog using PLT-Scheme opened my eyes to some of the possibilities. Working lately primary on webapps using web.py, sqlalchemy I wanted to see what could be done using scheme. In this case browsing PLT's available libraries. After scanning PLT's PLaneT I have found some interesting libs opensourced by a company called untyped.
The libs I found interesting where:
I decided to take a closer look at them and how untyped implemented its magic.
Dispatch, declare what you want
Dispatch is essentially syntax to declare routes and controllers. I wanted to know more so I analysed an example given by the documentation.
Define your site
#lang scheme/base ;; automaticly grab packages from PLaneT (require (planet untyped/dispatch)) (define-site blog ([(url "/") index] [(url "/posts/" (string-arg)) review-post] [(url "/archive/" (integer-arg) "/" (integer-arg)) review-archive]))
Essentially stating that
/archive/10 will call
review-archive with one parameter
Controllers for the rules above may be defined with:
(define-controller (controller request args ...) exp ... )
An example being:
; request string -> html-response (define-controller (review-post request slug) `(html (head (title ,slug)) (body (h1 "You are viewing " ,(format "~s" slug)) (p "And now for some content..."))))
To find out what happens behind the syntax it helps to expand the syntax (or macro). The declarative statements above expand to:
(require (planet untyped/dispatch)) (begin (define-values (blog index review-post review-archive) (let-values (((blog controllers) (make-site 'blog '(index review-post review-archive)))) (let-values (((index review-post review-archive) (apply values controllers))) (set-site-rules! blog (list (make-rule (make-pattern "/") index) (make-rule (make-pattern "/posts/" (string-arg)) review-post) (make-rule (make-pattern "/archive/" (integer-arg) "/" (integer-arg)) review-archive))) (values blog index review-post review-archive)))))
blog and the controllers
review-archive on module level by the function
make-site. This procedure
also assigns an
undefined-controller to the controllers
review-archive to catch the undefined ones. These syntax-extentions are
One can use the export-helper in a provide as such
(provide (site-out blog))
to export the defined site and the configured controllers.
Rule based routing
There is a function defined
(controller-url controller args ..) which can be
used to translate back from controller to path.
[(url "/posts/" (string-arg)) review-post] are declared when configuring the site. This rule effectively states that
/posts/([^/]+) maps to controller
review-post. This rule will be used to
dispatch this request, but will also be used to translate
(controller-url 'review-post 10) to the following link
How is this implemented. In the files
struct-private.ss contain statements which describes elements
to build reqexp matches and encode and decode arguments between the url and scheme domain. The args and eventually custom args
are defined in the following struct. (i removed the display routines for clarity)
(define-struct arg (id ;; symbol to be used declaration pattern ;; regexp pattern decoder ;; how to decode from captured arg to lisp domain encoder ;; how to encode from lisp domain to captured arg #:transparent)
A working example is
(define (string-arg) (make-arg 'string ;; id "[^/]+" ;; regexp (lambda (raw) ;; decode (uri-decode raw)) (lambda (arg) ;; encode (if (string? arg) (uri-encode arg) (raise-exn exn:fail:dispatch (format "Expected string, given: ~s" arg))))))
Adding cutom arguments is made trivial by this setup since one can just build a structure with encode and decode routines and can use it.
Snooze, ORM for Postgresql or Sqlite
Will come later ..
Will come later ..