A website built in Common Lisp using Caveman2 as its framework. It is based on a collection of tutorials written by hyotang666. Those tutorials are based on chapters from the book 'Basic Ruby on Rails'. hyotang666 ported the Ruby code to Common Lisp.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

210 lines
7.1 KiB

(in-package #:cl-user)
(defpackage rails-to-caveman.web
(:use #:cl
#:caveman2
#:rails-to-caveman.config
#:rails-to-caveman.view
#:rails-to-caveman.db
#:rails-to-caveman.model
#:sxql
#:mito)
(: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"
;; Use to pass message to view -- it is expecting a
;; `MESSAGE' item. You do not need to add it, though. Or,
;; you can pass it an empty value/string if you do not want
;; to leave the code commented out.
;; '(:message "This is not a message") ; Added Chapter 3.
'(:numbers (1 2 3 4 5))
))
(defroute "/users/index"()
(render "users/index.html"
`(:users ,(with-connection (db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:order-by :number))))))
(defroute "/users/search" (&key |q|)
(render "users/index.html"
`(:users ,(with-connection (db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where
`(:or (:like :name ,|q|)
(:like :full-name ,|q|)))
(sxql:order-by :number))))))
(defroute "/users/:id" (&key id)
(setf id (parse-integer id))
(render "users/show.html"
`(:user ,(with-connection (db)
(mito:find-dao 'rails-to-caveman.model::user
:id id)))))
(defroute "/about" ()
;; about.html should be in the /templates directory.
(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 to /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")
;; To be honest, I do not know what this is actually doing. It is
;; mentioned in 'STEP5 Flash' in Chapter 3 (I.E. Tutorial 3:
;; Routing). This applies to case's 5 and 6.
(5 (setf (gethash :notice *session*) "Move to step6")
`(302 (:location "/lesson/step6")))
(6 (let((notice (gethash :notice *session*)))
(remhash :notice *session*) notice))
;; This is part of an example about using djula (which is a
;; template engine used by Caveman2. djula is a port of Python's
;; Django template engine.
;; step7.html should be in the /templates directory.
(7 (render "step7.html"
`(:price ,(floor(* 2000 1.08)))))
(8 (render "step7.html" '(:price 1000)))
(9 (render "step9.html"
'(:comment "<script>alert('danger')</script>Hello")))
;; If you dare to embed HTML, add safe after the variable
;; reference on the View side (step10.html).
(10 (render "step10.html"
'(:comment "<strong>safe html</strong>")))
(11 `(200 (:content-type "text/html; charset=utf-8")
(, (let ((population 704414) (surface 141.31))
(render "step11.html"
`(:contents
,(format nil
;; ~D = Decimal
;; ~,,2F = Fixed-Format
;; Floating-Point
;; ~,,2F = Print exactly two
;; digits after the decimal
;; point and many as necessary
;; before the decimal point.
"Population: ~D Floor Surface: ~D Population/Surface: ~,,2F"
population
(floor surface)
(/ population surface))))))))
(12 (render "step11.html"
`(:contents ,(local-time:format-timestring
nil
(local-time:now)
:format '((:year 4)"/"(:month 2)"/"(:day 2)"(" :short-weekday ") "
(:hour 2)":"(:min 2)":"(:sec 2))))))
(13 `(200 (:content-type "text/html; charset=utf-8")
;; This view uses 'format' in step13.html (view in
;; /templates directory).
(,(render "step13.html" '(:population 127767944)))))
(14 (render "step14.html"
;; The view uses a custom filter which was added to
;; view.lisp.
`(:contents ,(format nil "foo~%bar~%bazz"))))
;; The view demonstrates how to form links.
(15 (render "step15.html"))
;; The view demonstrates how to display images.
;; Images are stored in the /static/images directory.
(16 (render "step16.html"))
;; Provides control branches which you can navigate via djula in
;; step17.html (/templates directory).
(17 `(200 (:content-type "text/html; charset=utf-8")
;; Adjust `STOCK' to adjust what is viewed in step17.html.
(,(let((stock 10))
(render "step17.html"
`(:stock-zerop ,(< 0 stock) :stock ,stock))))))
;; This creates a list (cons list) which is then cycled through in
;; /templates/step18.html using the djula template engine..
(18 (render "step18.html"
'(:items
((:pan . 2680)
(:glass . 2550)
(:pepper-mill . 4515)
(:peeler . 945)))))
))
;;
;; Error pages
(defmethod on-exception ((app <web>) (code (eql 404)))
(declare (ignore app))
(merge-pathnames #P"_errors/404.html"
*template-directory*))