( in-package :cl-user )
( defpackage rails-to-caveman.web
( :use :cl
:caveman2
:rails-to-caveman.config
:rails-to-caveman.view
:rails-to-caveman.db
:datafly
:sxql )
( :export :*web* ) )
( in-package :rails-to-caveman.web )
;; for @route annotation
( syntax:use-syntax :annot )
;;
;; Application
( defclass <web> ( <app> ) ( ) )
( defvar *web* ( make-instance '<web> ) )
( clear-routing-rules *web* )
;;
;; Routing rules
( defroute "/" ( )
( render #P"index.html" ) )
( defroute "/about" ( )
( render #P"about.html" ' ( :page-title "About" ) ) )
( defroute "/hello/:name" ( &key name )
;; Substitutes `:name' with `name'.
( format nil "Hello, ~A" name ) )
( defroute "/say/*/to/*" ( &key splat )
;; If route is /say/hello/to/world (in the browser). It will match
;; to /say/hello/to/world. 'hello' and 'world' are the wildcard
;; values in this example.
( format nil "~A" splat ) )
( defroute ( "/hello/([\\w]+)" :regexp t ) ( &key captures )
;; Parse the second part of the URL via a regular expression
;; (regexp). The result of the parsed regexp text is stored in
;; `captures', hence the use of 'first' in the format string.
( format nil "Hello, ~A!" ( first captures ) ) )
( defroute "/hello/?:name?" ( &key name )
;; Generates two types of routes:
;; 1. /hello/earth
;; 2. /hello?NAME=earth
;; The query string must be in UPPERCASE. Otherwise, the `name' will
;; be bound to `nil'.
( format nil "Hello, ~A" name ) )
( defroute "/hello/?:name?" ( &key |name| )
;; If you want the query string in lowercase, enclose `name' (in
;; this case) with vertical bars '|'. This will force you to have
;; only one route (unlike the route above). `NAME' will now bind to
;; `nil'.
;; 1. /hello?name=earth
;; 2. /hello?NAME=earth <--- no longer works ('earth' binds to nil).
;; 3. /hello/earth <--- no longer works
( format nil "Hello, ~A" |name| ) )
( defroute "/lesson/step*/:name" ( &key splat name )
;; Directory style: Working with Parameters.
;; Example URL: /lesson/step1/Sato
( case ( parse-integer ( car splat ) :junk-allowed t )
( 1 ( format nil "Hello, ~A" name ) ) ) )
( defroute "/lesson/step*" ( &key splat ( |name| "Anonymous" ) )
;; 'Anonymous' is the default value for `|name|'.
;; Query style: Working with Parameters.
;; Example URL: lesson/step1?name=Sato
( case ( parse-integer ( car splat ) :junk-allowed t )
( 1 ( format nil "Hello, ~A" |name| ) ) ) )
( defroute "/lesson/step*" ( &key splat name )
;; If /lesson/step1 is used, it will be redirected to /lesson/step2,
;; then /lesson/step3 and finally /lesson/step4. No matter where you
;; start (along as it is below 4), the redirects will always take
;; you wot /lesson/step4.
;; The example includes `name' but it is never used. I am keeping it
;; here for consistency between the reference material and this code
;; base.
( case ( parse-integer ( car splat ) :junk-allowed t )
( 1 ' ( 302 ( :location "/lesson/step2" ) ) )
( 2 ' ( 302 ( :location "/lesson/step3" ) ) )
( 3 ' ( 302 ( :location "/lesson/step4" ) ) )
( 4 "Moved to step4" ) ) )
;;
;; Error pages
( defmethod on-exception ( ( app <web> ) ( code ( eql 404 ) ) )
( declare ( ignore app ) )
( merge-pathnames #P"_errors/404.html"
*template-directory* ) )