Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
S
submarine.common-lisp.dev
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
submarine
submarine.common-lisp.dev
Commits
bb415a59
Commit
bb415a59
authored
17 years ago
by
Ryszard Szopa
Browse files
Options
Downloads
Patches
Plain Diff
quick-start
darcs-hash:76be20e849f97c3c4832616734b9e172a1e8c93c
parent
e886a442
No related branches found
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
index.html
+192
-2
192 additions, 2 deletions
index.html
with
192 additions
and
2 deletions
index.html
+
192
−
2
View file @
bb415a59
...
...
@@ -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>
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment