;; (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 ) )
( in-package #:rails-to-caveman.model )
;;; Defines the USER table class for the database.
;;; Will be used by mito (an ORM).
( defclass user ( )
( ( number
:col-type
:integer
:initarg
:number
:reader number-of )
( name :col-type ( :varchar 64 )
:initarg
:name
:reader name-of )
( full-name :col-type
( or ( :varchar 128 ) :null )
:initarg
:full-name
:reader full-name-of )
( email :col-type
( or :null :text )
:initarg
:email
:accessor email-of )
( birthday :col-type
( or :null :date )
:initarg
:birthday
:reader birthday-of )
( sex :col-type
:integer
:initarg
:sex
:initform 1
:reader sex-of )
#| CHANGED :ACCESSOR VALUE FROM TUTORIAL (CHAPTER 4)
===================================================
The Chapter 4 tutorial has the :accessor value set to
'administratorp '. Unfortunately, this causes initialisation
argument errors when trying to seed the database. To fix this, I
had to remove the 'p ' part from that line. At the time of
writing, I do not know if that will have a negative effect on
future tutorials.
I have left a note in the 'seeds ' function below highlighting the
change to the :accessor value.
| #
( administrator :col-type
:boolean
:initarg
:administrator
:initform nil
:accessor administrator ) )
( :metaclass mito:dao-table-class ) )
( defun seeds ( )
;; '#(' are ARRAY LITERALS. I keep forgetting this and need to look
;; it up.
( let ( ( names
#( "Taro" "Jiro" "Hana" "John" "Mike" "Sophy" "Bill" "Alex" "Mary" "Tom" ) )
( fnames ; First Names
#( "Hippo" "Darling" "Lopez" "Jerry" ) )
( gnames ; Given Names
#( "Orange" "Fox" "Snake" ) ) )
( with-connection ( db )
( dotimes ( x 10 )
( mito:create-dao 'user
:number ( + x 10 )
:name ( aref names x )
:full-name ( format nil "~A ~A"
( aref fnames ( rem x 4 ) )
( aref gnames ( rem x 3 ) ) )
:email ( format nil "~A@example.com" ( aref names x ) )
:birthday "1981-12-01"
:sex ( nth ( rem x 3 ) ' ( 1 1 2 ) )
;; Removed 'p' from end of :administrator --
;; so the code differs from the code in the
;; tutorial (Chapter 4). I had to change it
;; because it produced errors when trying to
;; seed the database (using 'seeds'
;; function. I have, also, left a note in the
;; 'user' class definition highlighting this.
:administrator ( zerop 0 ) ) ) ) ) )
( defun rebuild ( )
"Drops the current database table, recreates it and populates it using seeded data."
( with-connection ( db )
( mito:recreate-table 'user ) )
( seeds ) )
( defun ids ( )
" Produces a list of all the Id 's in the database. Part of Chapter 4
tutorial and is a port of the 'ids method ' in the Ruby on Rails book
this tutorial was translated/ported from. "
( rails-to-caveman.db:with-connection ( rails-to-caveman.db:db )
( 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 ) ) ) ) )