From a6164af6afb3ce3dd3dd29c74ebff3159b36f079 Mon Sep 17 00:00:00 2001 From: Craig Oates Date: Mon, 13 Dec 2021 01:29:56 +0000 Subject: [PATCH] Chapter 3 -- Routing, Templates and djula Examples. Demonstrates how (basic) routing works and how they connect to the templates (I.E. views). Provides examples of how to use djula in said templates and pass information to the views (accessed with djula). Partial/shared views are added and shown how to be included within other templates (views). How the Caveman2 displays Static assets (I.E. images) in the templates is shown and examples of links ( tags) are provided, too. --- src/view.lisp | 22 +- src/web.lisp | 108 +++++++- static/css/main.css | 52 ++-- static/images/lisplogo.svg | 406 +++++++++++++++++++++++++++++++ templates/index.html | 27 +- templates/layouts/app.html | 25 ++ templates/layouts/demo.html | 13 + templates/shared/footer.html | 1 + templates/shared/header.html | 11 + templates/shared/login_form.html | 14 ++ templates/shared/sidebar.html | 14 ++ templates/step10.html | 3 + templates/step11.html | 1 + templates/step13.html | 7 + templates/step14.html | 3 + templates/step15.html | 2 + templates/step16.html | 8 + templates/step17.html | 14 ++ templates/step18.html | 30 +++ templates/step7.html | 1 + templates/step9.html | 1 + 21 files changed, 731 insertions(+), 32 deletions(-) create mode 100644 static/images/lisplogo.svg create mode 100644 templates/layouts/app.html create mode 100644 templates/layouts/demo.html create mode 100644 templates/shared/footer.html create mode 100644 templates/shared/header.html create mode 100644 templates/shared/login_form.html create mode 100644 templates/shared/sidebar.html create mode 100644 templates/step10.html create mode 100644 templates/step11.html create mode 100644 templates/step13.html create mode 100644 templates/step14.html create mode 100644 templates/step15.html create mode 100644 templates/step16.html create mode 100644 templates/step17.html create mode 100644 templates/step18.html create mode 100644 templates/step7.html create mode 100644 templates/step9.html diff --git a/src/view.lisp b/src/view.lisp index 6061f53..1b91d5d 100644 --- a/src/view.lisp +++ b/src/view.lisp @@ -46,6 +46,26 @@ :developmentp :productionp) (:import-from :caveman2 - :url-for)) + :url-for) + (:export #:title!) ; Added in Chapter 3 (Mockup section). + ) +;; in-package and let code added in Chapter 3, also. This is +;; preperation for Switching Layout Templates section. +(in-package + :rails-to-caveman.djula) ; Make sure this states your app name. +(let(title) + (defun title! (&optional sub) + (if sub + (format nil "~@[~A - ~]~:(~A~)" + (setf title sub) + ;; Make sure find-system states your app name. + #.(asdf:coerce-name(asdf:find-system :rails-to-caveman))) + title))) ; End of Chapter 3 additional code. (setf djula:*djula-execute-package* (find-package :rails-to-caveman.djula)) + +;;; This is a custom filter which is was added in Chapter 3 +;;; (Routing). Apparently, this is a filter which is part of the djula +;;; project's TODOs. This filter is used in 'step13.html' (in +;;; /templates directory). +(djula::def-filter :break(it) (cl-ppcre:regex-replace-all #\newline it "
")) diff --git a/src/web.lisp b/src/web.lisp index 306457b..30e2de7 100644 --- a/src/web.lisp +++ b/src/web.lisp @@ -1,11 +1,11 @@ (in-package :cl-user) (defpackage rails-to-caveman.web (:use :cl - :caveman2 + :caveman2 :rails-to-caveman.config - :rails-to-caveman.view + :rails-to-caveman.view :rails-to-caveman.db - :datafly + :datafly :sxql) (:export :*web*)) (in-package :rails-to-caveman.web) @@ -24,13 +24,22 @@ ;; Routing rules (defroute "/" () - (render #P"index.html")) + (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 "/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'. + ;; Substitutes ':name' with `NAME'. (format nil "Hello, ~A" name)) (defroute "/say/*/to/*" (&key splat) @@ -42,7 +51,7 @@ (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. + ;; `CAPTURES', hence the use of 'first' in the format string. (format nil "Hello, ~A!" (first captures))) (defroute "/hello/?:name?" (&key name) @@ -51,7 +60,7 @@ ;; 1. /hello/earth ;; 2. /hello?NAME=earth - ;; The query string must be in UPPERCASE. Otherwise, the `name' will + ;; The query string must be in UPPERCASE. Otherwise, the `NAME' will ;; be bound to `nil'. (format nil "Hello, ~A" name)) @@ -59,7 +68,7 @@ ;; 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'. + ;; `NIL'. ;; 1. /hello?name=earth ;; 2. /hello?NAME=earth <--- no longer works ('earth' binds to nil). @@ -75,7 +84,7 @@ (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 + ;; Example URL: /lesson/step1?name=Sato (case (parse-integer (car splat) :junk-allowed t) (1 (format nil "Hello, ~A" |name|)))) @@ -83,16 +92,91 @@ ;; 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. + ;; you to /lesson/step4. - ;; The example includes `name' but it is never used. I am keeping it + ;; 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"))) + (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 "Hello"))) + ;; If you dare to embed HTML, add safe after the variable + ;; reference on the View side (step10.html). + (10 (render "step10.html" + '(:comment "safe html"))) + (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 diff --git a/static/css/main.css b/static/css/main.css index 2e05864..a0d04e0 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -1,21 +1,45 @@ -@charset "UTF-8"; - +/* whole pages */ body { - font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; + background-color: white; + color: black; + margin: 0; padding: 0; + font-family: Meiryo, sans-serif; } -a:link { - color: #005585; - text-decoration: none; -} -a:visited { - color: #485270; +/* link */ +a:link { color: #00c; } +a:visited { color: #00c; } +a:hover { color: #f00; } +a img { border: none; } + +/* whole border */ +div#container { + margin: 0 auto; + padding-top: 5px; + width: 780px; } -a:hover { - color: #b83800; - text-decoration: underline; + +/* left pane */ +main { + float: left; + width: 530px; + padding: 10px, 10px, 10px, 0; } -#main { - text-align: center; +/* right pane */ +aside#sidebar { + float: left; + width: 230px; + background-color: #e8ffff; + padding: 5px; + font-size: 86%; } + +/* link of menubar */ +nav.menubar a { text-decoration: none; } + +/* link of menubar (not visited) */ +nav.menubar a:link { color: #ccc; } + +/* link of menubar (visited) */ +nav.menubar a:visited { color: #ccc; } diff --git a/static/images/lisplogo.svg b/static/images/lisplogo.svg new file mode 100644 index 0000000..75c4f9b --- /dev/null +++ b/static/images/lisplogo.svg @@ -0,0 +1,406 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + Made with secret + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alien technology + + CAUTION: + + diff --git a/templates/index.html b/templates/index.html index 6a3c687..3fa348d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,7 +1,24 @@ -{% extends "layouts/default.html" %} -{% block title %}Welcome to Caveman2{% endblock %} +{% extends "layouts/app.html" %} + +{% comment "Set title using title function." %} + +{%block title %}{% lisp (title! "Bitchin!") %}{% endblock %} +{% endcomment %} + + +{% block title %}Rails to Caveman2 Demo.{% endblock %} + {% block content %} -
- Welcome to Caveman2! -
+ +

{{ message }}

+

Here we go!

{% endblock %} diff --git a/templates/layouts/app.html b/templates/layouts/app.html new file mode 100644 index 0000000..5bf206b --- /dev/null +++ b/templates/layouts/app.html @@ -0,0 +1,25 @@ + + + + + {% block title %}{% endblock %} + + + +
+
+ {% include "shared/header.html" %} +
+
+ {% block content %}{% endblock %} +
+ +
+ {% include "shared/footer.html" %} +
+ + diff --git a/templates/layouts/demo.html b/templates/layouts/demo.html new file mode 100644 index 0000000..679ff01 --- /dev/null +++ b/templates/layouts/demo.html @@ -0,0 +1,13 @@ + + + + + + + {% block title %}Your app{% endblock %} + + + + {% block content %}{% endblock %} + + diff --git a/templates/shared/footer.html b/templates/shared/footer.html new file mode 100644 index 0000000..6fb4cda --- /dev/null +++ b/templates/shared/footer.html @@ -0,0 +1 @@ +About this website| Copyright(C) 2021 craigoates.net diff --git a/templates/shared/header.html b/templates/shared/header.html new file mode 100644 index 0000000..7b58218 --- /dev/null +++ b/templates/shared/header.html @@ -0,0 +1,11 @@ +Your app + + diff --git a/templates/shared/login_form.html b/templates/shared/login_form.html new file mode 100644 index 0000000..48eb279 --- /dev/null +++ b/templates/shared/login_form.html @@ -0,0 +1,14 @@ +

Login

+
+
+ + +
+
+ + +
+
+ +
+
diff --git a/templates/shared/sidebar.html b/templates/shared/sidebar.html new file mode 100644 index 0000000..f47da9f --- /dev/null +++ b/templates/shared/sidebar.html @@ -0,0 +1,14 @@ +{% include "shared/login_form.html" %} +

Latest News

+ + +

Member Blog

+ diff --git a/templates/step10.html b/templates/step10.html new file mode 100644 index 0000000..1e989d1 --- /dev/null +++ b/templates/step10.html @@ -0,0 +1,3 @@ + +

{{comment | safe}}

diff --git a/templates/step11.html b/templates/step11.html new file mode 100644 index 0000000..d74c626 --- /dev/null +++ b/templates/step11.html @@ -0,0 +1 @@ +

{{contents}}

diff --git a/templates/step13.html b/templates/step13.html new file mode 100644 index 0000000..a2f34a6 --- /dev/null +++ b/templates/step13.html @@ -0,0 +1,7 @@ + +

Step 13 | Population: {{population | format: "~D"}}

diff --git a/templates/step14.html b/templates/step14.html new file mode 100644 index 0000000..50ff8d3 --- /dev/null +++ b/templates/step14.html @@ -0,0 +1,3 @@ + +

{{contents|break|safe}}

diff --git a/templates/step15.html b/templates/step15.html new file mode 100644 index 0000000..19750a7 --- /dev/null +++ b/templates/step15.html @@ -0,0 +1,2 @@ + +Home diff --git a/templates/step16.html b/templates/step16.html new file mode 100644 index 0000000..3efcbf8 --- /dev/null +++ b/templates/step16.html @@ -0,0 +1,8 @@ +

Powered by + + lisplogo +

diff --git a/templates/step17.html b/templates/step17.html new file mode 100644 index 0000000..61bcc3f --- /dev/null +++ b/templates/step17.html @@ -0,0 +1,14 @@ + + +{% if stock-zerop %} +Stock: {{stock}} +{% else %} +This is no stock. +{% endif %} diff --git a/templates/step18.html b/templates/step18.html new file mode 100644 index 0000000..ab4b1b3 --- /dev/null +++ b/templates/step18.html @@ -0,0 +1,30 @@ + + +

+ + {% for (key . val) in items %} + + + + + {% endfor %} +
{{ key }}{{ val | format: ~:D }} Currency Points
+

+ diff --git a/templates/step7.html b/templates/step7.html new file mode 100644 index 0000000..0816bdd --- /dev/null +++ b/templates/step7.html @@ -0,0 +1 @@ +

{{price}}

diff --git a/templates/step9.html b/templates/step9.html new file mode 100644 index 0000000..52ad2e2 --- /dev/null +++ b/templates/step9.html @@ -0,0 +1 @@ +

{{comment}}