Browse Source

apply Chapter 5 changes (working with mito and templates).

This is an extention of Chapter 4. In Chapter 4, the aim was to set-up
a database and successfully connect to it. This chapter was more about
querying the database and displaying the results in the '.html'
templates.

I do not know why, but I was having a lot of trouble with naming
conflicts for hours. I could not work it out and in the end I do not
what I changed which caused the code to start working. One of the
tactics I used was replace ':' with '#:' in the 'defpackages' and
'.asd' file. Like I said, I do not know if this change caused the code
to start working. But, if you are wondering why the sudden change in
the code base, that is why.
master
Craig Oates 2 years ago
parent
commit
2c4c9fa410
  1. 26
      app.lisp
  2. 26
      rails-to-caveman.asd
  3. 31
      src/config.lisp
  4. 22
      src/db.lisp
  5. 18
      src/main.lisp
  6. 137
      src/model.lisp
  7. 55
      src/view.lisp
  8. 43
      src/web.lisp
  9. 37
      static/css/main.css
  10. 2
      templates/shared/header.html

26
app.lisp

@ -1,19 +1,19 @@
(ql:quickload :rails-to-caveman)
(defpackage rails-to-caveman.app
(:use :cl)
(:import-from :lack.builder
:builder)
(:import-from :ppcre
:scan
:regex-replace)
(:import-from :rails-to-caveman.web
:*web*)
(:import-from :rails-to-caveman.config
:config
:productionp
:*static-directory*))
(in-package :rails-to-caveman.app)
(:use #:cl)
(:import-from #:lack.builder
#:builder)
(:import-from #:ppcre
#:scan
#:regex-replace)
(:import-from #:rails-to-caveman.web
#:*web*)
(:import-from #:rails-to-caveman.config
#:config
#:productionp
#:*static-directory*))
(in-package #:rails-to-caveman.app)
;;; ORIGINAL -- WITHOUT CLACK-ERRORS PACKAGE ADDED
;; (builder

26
rails-to-caveman.asd

@ -1,25 +1,25 @@
(defsystem "rails-to-caveman"
(defsystem #:rails-to-caveman
:version "0.1.0"
:author "Craig Oates"
:license "MIT"
:depends-on ("clack"
"lack"
"clack-errors" ; <--- Added in Chapter 1.
"caveman2"
"envy"
"cl-ppcre"
"uiop"
:depends-on (#:clack
#:lack
#:clack-errors ; <--- Added in Chapter 1.
#:caveman2
#:envy
#:cl-ppcre
#:uiop
;; for @route annotation
"cl-syntax-annot"
#:cl-syntax-annot
;; HTML Template
"djula"
#:djula
;; for DB
"datafly"
"sxql"
"mito" ; <--- Added in Chapter 4.
#:mito ; <--- Added in Chapter 4.
#:datafly
#:sxql
)
:components ((:module "src"
:components

31
src/config.lisp

@ -1,17 +1,17 @@
(in-package :cl-user)
(in-package #:cl-user)
(defpackage rails-to-caveman.config
(:use :cl)
(:import-from :envy
:config-env-var
:defconfig)
(:export :config
:*application-root*
:*static-directory*
:*template-directory*
:appenv
:developmentp
:productionp))
(in-package :rails-to-caveman.config)
(:use #:cl)
(:import-from #:envy
#:config-env-var
#:defconfig)
(:export #:config
#:*application-root*
#:*static-directory*
#:*template-directory*
#:appenv
#:developmentp
#:productionp))
(in-package #:rails-to-caveman.config)
(setf (config-env-var) "APP_ENV")
@ -34,7 +34,7 @@
#| EXCERPT TAKEN FROM TUTORIAL (CHAPTER 4):
============================================
Initially: I set the database-name to your-app, but later when I
tried to access the database, I got angry with CANTOPEN. So I
tried to access the database, I got angry with CAN'T OPEN. So I
changed the name to your_app and it worked fine. Apparently, words
separated by . Dashes Are Capitalized At The Beginning . The Name
Specified In The Code Here Is "Your-App", But The Registered File
@ -51,7 +51,8 @@
also a "db "directory in the project directory, so I don't wonder if
it will do it well. I think it's unfriendly to have no documentation.
|#
`(:databases ((:maindb :sqlite3 :database-name "rails-to-caveman.db"))))
`(:databases ((:maindb :sqlite3 :database-name ,(merge-pathnames #P"db/rails_to_caveman.db"
*application-root*)))))
(defconfig |development|
'())

22
src/db.lisp

@ -1,16 +1,16 @@
(in-package :cl-user)
(in-package #:cl-user)
(defpackage rails-to-caveman.db
(:use :cl)
(:import-from :rails-to-caveman.config
:config)
(:import-from :datafly
:*connection*)
(:import-from :cl-dbi
:connect-cached)
(:export :connection-settings
:db
:with-connection))
(in-package :rails-to-caveman.db)
(:import-from #:rails-to-caveman.config
#:config)
(:import-from #:datafly
#:*connection*)
(:import-from #:cl-dbi
#:connect-cached)
(:export #:connection-settings
#:db
#:with-connection))
(in-package #:rails-to-caveman.db)
(defun connection-settings (&optional (db :maindb))
(cdr (assoc db (config :databases))))

18
src/main.lisp

@ -1,13 +1,13 @@
(in-package :cl-user)
(in-package #:cl-user)
(defpackage rails-to-caveman
(:use :cl)
(:import-from :rails-to-caveman.config
:config)
(:import-from :clack
:clackup)
(:export :start
:stop))
(in-package :rails-to-caveman)
(:use #:cl)
(:import-from #:rails-to-caveman.config
#:config)
(:import-from #:clack
#:clackup)
(:export #:start
#:stop))
(in-package #:rails-to-caveman)
(defvar *appfile-path*
(asdf:system-relative-pathname :rails-to-caveman #P"app.lisp"))

137
src/model.lisp

@ -1,9 +1,8 @@
;; (in-package :cl-user) ; Not sure if this needs to exist (Chapter 4)
(defpackage :rails-to-caveman.model
;;(in-package #:cl-user) ; Not sure if this needs to exist (Chapter 4)
(defpackage rails-to-caveman.model
(:use #:cl
#:rails-to-caveman.db)
(:export #:seeds
#:ids))
#:rails-to-caveman.db
#:mito))
(in-package #:rails-to-caveman.model)
;;; Defines the USER table class for the database.
@ -106,131 +105,3 @@ this tutorial was translated/ported from."
(mapcar #'mito:object-id
(mito:retrieve-dao 'rails-to-caveman.model::user))))
#| STAND ALONE BITS OF CODE
===========================
The code below is to use in a live coding environment. Think of it as
a persistent scratch pad -- for when you close Emacs but still want
those little snippets of code which do not have a place in the main
code base but are useful for little tests/proof-of-concepts.
When you have loaded the system in SLIME, use c-c c-c to run the code
below.
|#
(with-connection (db) ; Creates a table in the database called 'user'.
(mito:ensure-table-exists 'user))
;; Retrieves the record with the specified Id. Change ':id' to
;; retrieve different entries from the database.
;; NOTE: EVAL. THIS IN SLIME BEFORE USING (DESCRIBE *) FURTHER DOWN
;; THE FILE.
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:find-dao 'rails-to-caveman.model::user :id 3))
;; Retrieves the database entry with the specified name.
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:find-dao 'rails-to-caveman.model::user :name "Taro"))
#| SQLITE DATABASE BOOLEAN TYPES
================================
Use multiple columns to specify with data you want to retrieve.
Because SQLite database does not have a Boolean type,
:administrator : 0 = false
1 = true
:sex 1 = male
2 = female
SQLite type quirk and how the tutorial decided to model the data in
the database.
|#
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:find-dao 'rails-to-caveman.model::user :sex 1 :administrator 1))
;; A common strategy to deal with SQLite Boolean types is to set
;; constants and refer to them instead of passing hard coded 1 & 0.
(defconstant +false+ 0)
(defconstant +true+ 1)
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:retrieve-dao 'rails-to-caveman.model::user :administrator +false+))
(ids) ; Lists out all the Id's (in SLIME) in the 'users' table.
(seeds) ; Populates the database with seeded data (in 'users')
;; Drops the current table, creates a new one and populates it with
;; seeded data -- in the 'users' tables.
(rebuild)
#| SEEING THE CONTENTS OF A DATABASE ENTRY IN SLIME.
====================================================
This is a bit picky. First of all, you need to retrieve a database
entry (in SLIME) before checking the contents. Search for 'NOTE:
EVAL.' for the code. After you have retrieved the entry from the
database, calling (describe *) will produce something like the
following,
[standard-object] Slots with
:INSTANCE allocation:
CREATED-AT = @yyyy-mm-ddThh:mm:ss.ms+tz
UPDATED-AT = @yyyy-mm-ddThh:mm:ss.ms+tz
SYNCED = T
ID = 3
NUMBER = 12
NAME = "Hana"
FULL-NAME = "高橋 花子"
EMAIL = "Hana@example.com"
BIRTHDAY = @yyyy-mm-ddThh:mm:ss.ms+tz
SEX = 2
ADMINISTRATOR = NIL
THIS IS SPECIFIC TO SBCL -- OTHER IMPLEMENTATIONS MIGHT PRODUCE
DIFFERENT OUTPUTS
|#
(describe *)
#| BUILD QUERIES WITH SXQL AND MITO
===================================
If you want to build a complex query, use it in combination
MITO.DAO:SELECT-DAO with sxql .
The one in the previous section MITO:RETRIEVE-DAOis equivalent to the
following code.
COPY THE CODE BELOW INTO SLIME OR WORK THEM INTO A FUNCTION.
|#
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where '(:= :administrator 0))))
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where
'(:and (:= :name "Taro")
(:< :number 20)))))
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where '(:= :sex 2))
(sxql:order-by :number)))
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where '(:= :sex 2))
(sxql:order-by (:desc :number))))
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where
`(:or ,@(mapcar (lambda(num)
`(:= :number ,num))
'(15 17 19))))))
(rails-to-caveman.db:with-connection (rails-to-caveman.db:db)
(mito:select-dao 'rails-to-caveman.model::user
(sxql:where
`(:and (:<= 12 :number)
(:<= :number 14)))))

55
src/view.lisp

@ -1,21 +1,21 @@
(in-package :cl-user)
(in-package #:cl-user)
(defpackage rails-to-caveman.view
(:use :cl)
(:import-from :rails-to-caveman.config
:*template-directory*)
(:import-from :caveman2
:*response*
:response-headers)
(:import-from :djula
:add-template-directory
:compile-template*
:render-template*
:*djula-execute-package*)
(:import-from :datafly
:encode-json)
(:export :render
:render-json))
(in-package :rails-to-caveman.view)
(:use #:cl)
(:import-from #:rails-to-caveman.config
#:*template-directory*)
(:import-from #:caveman2
#:*response*
#:response-headers)
(:import-from #:djula
#:add-template-directory
#:compile-template*
#:render-template*
#:*djula-execute-package*)
(:import-from #:datafly
#:encode-json)
(:export #:render
#:render-json))
(in-package #:rails-to-caveman.view)
(djula:add-template-directory *template-directory*)
@ -32,27 +32,26 @@
(defun render-json (object)
(setf (getf (response-headers *response*) :content-type) "application/json")
(encode-json object))
(datafly:encode-json object))
;;
;; Execute package definition
(defpackage rails-to-caveman.djula
(:use :cl)
(:import-from :rails-to-caveman.config
:config
:appenv
:developmentp
:productionp)
(:import-from :caveman2
:url-for)
(:use #:cl)
(:import-from #:rails-to-caveman.config
#:config
#:appenv
#:developmentp
#:productionp)
(:import-from #:caveman2
#: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.
(in-package #:rails-to-caveman.djula) ; Make sure this states your app name.
(let(title)
(defun title! (&optional sub)
(if sub

43
src/web.lisp

@ -1,14 +1,15 @@
(in-package :cl-user)
(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)
(: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)
@ -34,6 +35,28 @@
'(: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")))

37
static/css/main.css

@ -43,3 +43,40 @@ nav.menubar a:link { color: #ccc; }
/* link of menubar (visited) */
nav.menubar a:visited { color: #ccc; }
/* table */
table.list, table.attr {
font-size: 90%;
width: 100%;
}
table.list th, table.attr th {
background-color: #499;
color: white;
font-weight: normal;
}
table.list td, table.list th,
table.attr td, table.attr th {
padding: 4px;
}
table.list th {
text-align: left;
}
table.attr th {
text-align: right;
}
table.list td, table.attr td {
background-color: #cee;
}
/* toolbar */
ul.toolbar,
div.toolbar {
padding: 15px 0;
font-size: 90%;
text-align: right;
}

2
templates/shared/header.html

@ -5,7 +5,7 @@
<li><a href="/">Home</a></li>
<li><a href="#">News</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Members</a></li>
<li><a href="/users/index">Members</a></li>
<li><a href="#">Settings</a></li>
</ul>
</nav>

Loading…
Cancel
Save