Commit bb415a59 authored by Ryszard Szopa's avatar Ryszard Szopa

quick-start

darcs-hash:76be20e849f97c3c4832616734b9e172a1e8c93c
parent e886a442
......@@ -134,9 +134,199 @@ makes it easier to maintain and to extend.</li>
<li><a href="darcs/mop-utils/doc/mop-utils-package/">MOP-utils</a></li>
</ul>
<p>There exists also a <a href="darcs/submarine/doc/example.lisp">poor
man's tutorial</a>.</p>
<a name="quick_start"<h1>Quick start</h1></a>
<p>
API references are great, but they are useful only when you already
have a basic idea about what can be done with the library. So, here's a
poor man's tutorial for Submarine. The code as presented here isn't
really suitable for copying and pasting directly into
the <abbr title="READ-EVAL-PRINT-LOOP">REPL</abbr>. However, you may
be interested in <a href="darcs/submarine/doc/example.lisp">this
file</a>, which is intended to be opened in Emacs with Slime and
then <code>C-c</code>'ed form by form.
</p>
<p>First, we should create a package for our example.</p>
<code>
<pre>
(in-package :asdf)
(defpackage :submarine-example
(:use :cl :submarine))
(in-package :submarine-example)
</pre>
</code>
<p>
DEFDAO is a wrapper macro, that creates a class inheriting from DAO
and with the metaclass set to db-class.
</p>
<code>
<pre>
(defdao affiliation ()
((name :type string :initarg :name :accessor affiliation-name))
(:connection-spec "submarine-test" "richard" "dupa" "localhost"))
</pre>
</code>
<p>The aguments in the :connection-spec are the following: name of the
database, name of the user, password, host. The user should be able
to create tables in the given database.</p>
<p>Let's macroexpand last form:</p>
<code>
<pre>
;; (DEFCLASS AFFILIATION (DAO)
;; ((NAME :TYPE STRING :INITARG :NAME :ACCESSOR AFFILIATION-NAME))
;; (:CONNECTION-SPEC "submarine-test" "richard" "dupa" "localhost")
;; (:METACLASS DB-CLASS))
</pre>
</code>
Every slot that is not transient must have a TYPE---it's needed by
PostgreSQL. A person may not have an affiliation, but he or she
must have a name. (Not-null is by default set to NIL.)
<code>
<pre>
(defdao person ()
((name :initarg :name :accessor person-name :type string :not-null t)
(affiliation :accessor person-affiliation :type affiliation :foreign t :initform nil
:initarg :affiliation))
(:connection-spec "submarine-test" "richard" "dupa" "localhost"))
(defdao club ()
((name :initarg :name :accessor club-name :type string))
(:connection-spec "submarine-test" "richard" "dupa" "localhost"))
</pre>
</code>
<p>
There's a many-to-many relationships between persons and clubs: a
club can have several members, one person may be the member of a
number of clubs.
</p>
<code>
<pre>
(def-many-to-many person club :connection-spec ("submarine-test" "richard" "dupa" "localhost"))
</pre>
</code>
<p>
This the macroexpansion of the last form. We can see it defines two
methods, for retrieving all objects related to its argument.
</p>
<code>
<pre>
;; (progn
;; (defclass person-club (dao)
;; ((person :type person :accessor person)
;; (club :type club :accessor club))
;; (:connection-spec "submarine-test" "richard" "dupa" "localhost")
;; (:metaclass db-class))
;; (defmethod club ((submarine::object person))
;; (mapcar #'club
;; (submarine::select-dao-fun 'person-club
;; (list := 'person
;; (get-id
;; submarine::object)))))
;; (defmethod person ((submarine::object club))
;; (mapcar #'person
;; (submarine::select-dao-fun 'person-club
;; (list := 'club
;; (get-id
;; submarine::object))))))
</pre>
</code>
<p>Now, we can populate our database by creating some objects:</p>
<code>
<pre>
(let* ((lions (save-dao (make-instance 'club :name "Lion's")))
(rotary (save-dao (make-instance 'club :name "Rotary")))
(lodge (save-dao (make-instance 'club :name "The Lodge")))
(democ (save-dao (make-instance 'affiliation :name "Democrats")))
(repub (save-dao (make-instance 'affiliation :name "Republicans")))
(john (make-instance 'person :name "John"))
(roger (make-instance 'person :name "Roger" :affiliation democ))
(henry (make-instance 'person :name "Henry" :affiliation repub)))
</pre>
</code>
<p>
As save-dao returns its argument
after saving it, we can do it at the same time as initializing
the variables.
</p>
<p>
Let's add the last names.
</p>
<code>
<pre>
(setf (person-name henry) "Henry Johnson")
(setf (person-name roger) "Roger Monroe")
</pre>
</code>
<p> Little Johny declares himself as a Democrat:</p>
<code>
<pre>
(setf (person-affiliation john) democ)
(dolist (person (list john roger henry))
(save-dao person))
</pre>
</code>
<p> Apparently, they are all masons!</p>
<code>
<pre>
(dolist (person (list john roger henry))
(relate person lodge))
</pre>
</code>
<p>But they also belong to other clubs:</p>
<code>
<pre>
(relate john lions)
(relate roger rotary)
(relate henry lions)
(relate henry rotary)
</pre>
</code>
<p> Now, let's see what do we know about our small world.</p>
<code><pre>
(format t "All persons: ~%~{ * ~A~%~}~%~%" (mapcar 'person-name (select-dao 'person)))
(format t "All democrats: ~%~{ * ~A~%~}~%" (mapcar 'person-name (get-all 'person democ)))
(format t "All masons: ~%~{ * ~A~%~}~%" (mapcar 'person-name (person lodge)))
(format t "All the clubs of Henry: ~%~{ * ~A~%~}~%" (mapcar 'club-name (club henry))))
</pre></code>
<p>The effect of executing the last form:</p>
<pre>
; All persons:
; * John
; * Roger Monroe
; * Henry Johnson
; All democrats:
; * John
; * Roger Monroe
; All masons:
; * John
; * Roger Monroe
; * Henry Johnson
; All the clubs of Henry:
; * The Lodge
; * Lion's
; * Rotary
</pre>
<p>We can also retrieve dao's from the database if we have their
ID:</p>
<code><pre>
(let ((john (make-instance 'person :id 1)))
(format t "This is number 1: ~A" (person-name john)))
</pre>
</code>
<p>After executing the last form:<p>
<pre>
; This is number 1: John
</pre>
<p>(Well, I knew it.)</p>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment