1 #LyX 1.3 created this file. For more info see http://www.lyx.org/
11 \paperpackage widemarginsa4
15 \use_numerical_citations 0
16 \paperorientation portrait
19 \paragraph_separation indent
21 \quotes_language english
25 \paperpagestyle default
41 \begin_inset Quotes eld
45 \begin_inset Quotes erd
55 is a very useful module that works on top of the
59 framework to do rapid developing of complex data-driven web appilcations
74 was founded and developed and continues to be developed and mantained by
85 The conventions used in this manual are:
88 Code is shown in a monospace font.
89 When it is expected that the user is working in an interactive environment
90 what the user should type appears as bold, while the computer result appears
91 non-bold, for example:
104 Names of people or products are show as small caps, like
119 require further revision.
124 ToDo: Add more conventions as they are needed, possible classes of text:
125 names of concepts, name of programming entities, like variables, functions,
126 etc (which are embedded in text, should they be shown monospaced ?).
134 Protocol A Protocol for introspection on relational objects.
138 Presentations A Mewa-like
144 http://www.adrian-lienhard.ch/files/mewa.pdf
147 layer for UncommonWeb
153 http://common-lisp.net/project/ucw/
162 First we start with the data model.
163 The Meta Model Protocol (MMP) is used to provide information on the data
164 objects and how they relate to one another.
165 Its is currently implemented as a layer over CLSQL
174 , although support is planned for other backends (
185 The MMP shares its definition syntax with
191 's Object Oriented Data Definition Language (OODDL)
197 http://clsql.b9.com/manual/ref-ooddl.html
203 Shouldn't this footnote be a bibliographical entry ? or something like that
211 The macro to define view-classes is named DEF-VIEW-CLASS/META, and takes
212 the same arguments as DEF-VIEW-CLASS from CLSQL.
213 For the purposes of this simple example, we will only need two functions
214 from the MMP beyond what CLSQL provides : LIST-SLOTS and LIST-SLOT-TYPES[5].
217 We'll define a simple class to hold a user.
222 (def-view-class/meta user ()
227 ((userid :initarg :userid :accessor userid :type integer :db-kind :key)
232 (username :initarg :username :accessor username :type string :db-kind
238 (password :initarg :password :accessor password :type string :db-kind
242 and now we create a user:
247 (defparameter user (make-instance 'user :userid 1
257 :password "p@ssw0rd"))
260 We can see the slots of users running:
265 (lisp-on-lines::list-slots user)
268 (USERID USERNAME PASSWORD)
276 (lisp-on-lines::list-slot-types user)
279 ((USERID INTEGER) (USERNAME STRING) (PASSWORD STRING))
282 To see the default attributes of a class
283 \begin_inset Marginal
288 Is this correct ? Drew, please, check.
296 (lisp-on-lines::default-attributes user)
299 ((USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
302 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
305 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD))
308 To set the attributes of a class to the default values we use:
313 (lisp-on-lines::set-default-attributes user)
316 ((USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
319 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
322 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD))
325 which takes an object of the class we are working with.
326 This is going to be change so we can do this action directly on the class.
327 It is on the TODO file.
335 (lisp-on-lines::find-class-attributes user)
338 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
341 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
344 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
350 note that the mewa functions (find-attribute, set-attribute etc) can take
351 either an instance, or a class-name as a symbol:
356 (lisp-on-lines::find-class-attributes 'user)
359 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
362 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
365 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
373 (lisp-on-lines::find-class-attributes (make-instance 'user))
376 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
379 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
382 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
388 Using that information, we have enough to create an interface to the object.
393 includes a powerful presentation system
399 To see this system in action, we strongly recomend to study the presentations
400 example which comes with
405 Reading components/presentations.lisp can help understand a lot about how
406 presentations are built.
409 , but it is not dynamic enough for some of the most advanced applications.
410 Mewa defines an approach to presentations that solves that problem, but
411 the paper is written from a
416 A mixture of the two , Mewa Presentations(MP), is described here.
428 An attribute is essentially a named version of the DEFPRESENTATION slot-like
429 arguments, for example in :
434 (defpresentation person-editor (object-presentation)
439 ((string :label "First Name" :slot-name 'first-name :max-length 30)))
442 the (string :label "First Name" ...) form is an attribute definiton.
443 Attributes are accessed through FIND-ATTIRIBUTES, and are composed at run
448 's presentation system is done at compile time) to display the object.
449 This allows a very flexible system of displaying objects which is reminiscent
464 discovered this, rather than invent or design it, so there are some rough
465 edges, but its a good start.
466 Exploration baby yeah!
472 Its much easier to show this than to tell.
473 Lets present our user class.
478 , you'd define a presentation as such :
483 (defpresentation user-presentation (object-presentation)
486 ((INTEGER :LABEL "USERID" :SLOT-NAME USERID)
489 (STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
492 (STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)))
495 which could be presented using PRESENT-OBJECT :
500 (present-object user :using 'user-presentation)
503 The equivalent approach using mewa presentations is actually longer and
504 more verbose(!) but it serves to demonstrate how the MP system works.
507 Mewa Presentations adds a set of attributes to a class, keyed off the class
509 Attributes are inherited, so if you define an attribute on T, you can use
513 MP stores named attributes keyed on a class name.
514 To achieve the same functionality as the above using mp would look like
520 (setf (lisp-on-lines::find-attribute 'user :viewer)
521 \begin_inset Marginal
526 Isn't this too imperative (in contrast to functional, lispy).
534 '(lisp-on-lines::mewa-object-presentation
539 :attributes (userid username password)
544 :global-properties (:editablep nil)))
547 (:VIEWER MEWA-OBJECT-PRESENTATION
553 (USERID USERNAME PASSWORD)
564 (setf (lisp-on-lines::find-attribute 'user 'userid)
565 \begin_inset Marginal
570 Are this setfs to 'userid, 'username and 'password needed ? I (Pupeno) inspected
571 they contents at of this moment and they seem to already contain what they
580 '(integer :label "userid" :slot-name userid))
583 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
588 (setf (lisp-on-lines::find-attribute 'user 'username)
593 '(STRING :LABEL "USERNAME" :SLOT-NAME USERNAME))
596 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
601 (setf (lisp-on-lines::find-attribute 'user 'password)
606 '(STRING :LABEL "USERNAME" :SLOT-NAME PASSWORD))
609 (PASSWORD STRING :LABEL "USERNAME" :SLOT-NAME PASSWORD)
614 (lisp-on-lines::find-class-attributes 'user)
620 (:VIEWER MEWA-OBJECT-PRESENTATION
626 (USERID USERNAME PASSWORD)
635 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
638 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
641 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
647 this is all turned into a
651 presentation at runtime using MAKE-PRESENTATION, for example, the following
652 code should be enough to show what's built so far attached to the examples
658 (defcomponent lol-example (window-component)
668 (defmethod render-on ((res response) (lol-example lol-example))
678 (<ucw:render-component :component (lisp-on-lines::make-presentation
679 user :type :viewer)))
684 (defentry-point "lol.ucw" (:application *example-application*) ()
692 As you'll see, nothing is exported from the LISP-ON-LINES package.
693 If you wish to use LOL in your own package (or in UCW-USER or whatever),
694 you simply need to use the MEWA and META-MODEL packages.
697 SET-ATTRIBUTE can be used in place of (setf (find-attribute ...)) when you
698 want to "inherit" the properties of an existing attribute definition :
703 (set-attribute 'user 'password '(string :label "password: (must be at least
713 "password: (must be at leat 8 chars)"
722 Now we want to create a presentation with which to edit the username.
723 we will use the existing attributes on a subclass of mewa-object-presetation
729 (defcomponent user-editor (mewa-object-presentation)
744 :attributes '((username :label "Enter your New Username") password)
749 :global-properties '(:editablep t)))
757 (setf (find-attribute 'user :editor) '(user-editor))
760 (:EDITOR USER-EDITOR)
763 which we then can display below our earlier example :
768 (defmethod render-on ((res response) (e presentations-index))
778 As you'll see, nothing is exported from the LISP-ON-LINES package.
784 if you wish to use LOL in your own package (or in UCW-USER or whatever),
789 you simply need to use the MEWA and META-MODEL packages"
794 (<ucw:render-component :component (lisp-on-lines::make-presentation lisp-on-line
795 s::user :type :viewer))
800 (<ucw:render-component :component (lisp-on-lines::make-presentation lisp-on-line
801 s::user :type :editor)))
804 that should give you some idea on how it works ..
805 ask me when you get confused :)
811 This is Pupeno's view of how to do rapid developing of a database-driven
813 It currently is going to assume a very specific case but latter it may
817 We first start with a
821 connection of CLSQL which is set up with one line:
826 (clsql:connect '("localhost" "geo" "geo" "geogeo"))
829 which connect us to the server on
838 \begin_inset Quotes eld
842 \begin_inset Quotes erd
846 \begin_inset Quotes eld
850 \begin_inset Quotes erd
853 (this is not a smart way to generate password, don't do this).
854 To have a nice SQL environment, we also want:
859 (clsql:locally-enable-sql-reader-syntax)
864 (setf clsql:*default-caching* nil)
867 Actually, it is more than a nice environmnet, without those lines the rest
868 of the code won't work.
875 database, there's a table called
879 which has the following structure:
884 CREATE TABLE product (
909 cost double precision,
914 CONSTRAINT product_cost_check CHECK ((cost > (0)::double precision))
924 ALTER TABLE ONLY product ADD CONSTRAINT product_name_key UNIQUE (name);
929 ALTER TABLE ONLY product ADD CONSTRAINT product_pkey PRIMARY KEY (id);
934 ToDo: express the table structure in a better way.
937 Now we'll create the class that represents a product, mirroring the database
943 (lisp-on-lines::def-view-class/table "product")
946 and then we generate the default attributes (from
950 's slots) and assign it to
959 (lisp-on-lines::set-default-attributes (make-instance 'product))-
960 \begin_inset Marginal
965 set-default-attributes is marked to be renamed to set-generated-attributes.
971 As you can see, we instantiate
977 set-default-attributes
979 , because it expects an object instead of a class.
980 We don't need the object anymore, so we don't save any reference to it.
981 In the future we might have a
983 set-default-attributes
985 that can use a class directly.
986 Now we set a the attribute
992 mewa-object-presentation
994 exposing the attributes we like the user to work with:
999 (setf (lisp-on-lines::find-attribute (make-instance 'product) :viewer)
1004 '(lisp-on-lines::mewa-object-presentation
1009 :attributes (name details description cost)
1014 :global-properties (:editablep nil)))
1017 The last parameter is a list of properties that will be applied to each
1021 Yet Another Example .
1024 Drew Crampsie Posted the following to comp.lang.lisp ..
1025 it just might help until he writes some real documentation.
1029 I've written a system that generates presentations for database objects
1030 based on the type and relation information in the system catalog.
1037 Mewa : Meta-level Architecture for Generic Web-Application Construction
1040 http://map1.squeakfoundation.org/sm/package/32c5401f-fa30-4a2b-80c8-1006dd462859
1043 clsql + postgres and the UCW presentation components.
1046 This is the code to add a new contact to the system.
1047 (screenshot pr0n follows).
1050 In the RENDER-ON method of my front-page i have :
1053 (let ((p (make-instance 'person :person-type-code nil)))
1056 (<:as-html "Add Person :")
1059 (<ucw:render-component
1062 :component (make-presentation
1071 :initargs '(:attributes
1074 ((person-type-code :editablep t)))))
1077 (<ucw:submit :action (new-person self p) :value "add"))
1082 This creates a drop-down list of person-types and an "add" button which
1086 (defaction new-person ((self component) person)
1092 Take a PERSON with a user-defined PERSON-TYPE-CODE,
1095 * Prompt the user for a FIRST-NAME, LAST-NAME and/or COMPANY-NAME
1098 * Search for similar PERSONs in the database.
1101 * If they exist, ask the user to select one or continue
1104 * otherwise, just continue editing the person"
1110 (call-component self (make-presentation
1119 :initargs '(:global-properties
1122 (:size 25 :editablep t))))))
1128 (call-component self (make-presentation
1131 (find-or-return-named-person self named-person)
1139 (defaction find-or-return-named-person ((self component) person)
1145 If any similiar contacts exist in the database,
1148 select one or continue with the current person
1151 PERSON must have FIRST-NAME, LAST-NAME and COMPANY-NAME bound."
1154 (let ((instances (sql-word-search person 'first-name 'last-name 'company-name)
1161 (call-component self (make-presentation
1167 :type 'person-chooser
1173 `(:instances ,instances)))
1181 You can hardly tell it's a web application ...
1182 there is no checking of CGI params etc...
1183 just nice code in the order i wanted to write it.
1189 http://tech.coop/img/screenshots/select-person-type.jpg
1192 http://tech.coop/img/screenshots/enter-person-name.jpg
1195 http://tech.coop/img/screenshots/select-similar-contacts.jpg
1198 http://tech.coop/img/screenshots/edit-person-details.jpg
1201 http://tech.coop/img/screenshots/view-recent-changes.jpg
1204 All of the code used to create the presentations for this is below my sig.
1205 I do eventually plan to release the presentation system as Free Software,
1206 it just needs a little cleaning up.
1207 E-mail me for a sneak peak.
1216 drewc at tech dot coop
1219 "Never mind the bollocks -- here's the sexp's tools."
1223 Krueger on comp.lang.lisp
1228 (def-view-class/table "person")
1233 (set-default-attributes (make-instance 'person)
1238 (defcomponent person-display (mewa::two-column-presentation)
1246 (defcomponent one-line-person (mewa::mewa-one-line-presentation)
1252 (:default-initargs :attributes '(first-name last-name company-name)))
1257 (setf (find-attribute 'person :one-line) '(one-line-person))
1262 (set-attribute 'person 'person-type-code '(code-select :category 1))
1267 (set-attribute 'person 'province-state-code '(code-select :category 2))
1272 (setf (find-attribute 'person :viewer) '(person-display :global-properties
1278 (set-attribute 'person :editor '(person-display :global-properties (:editablep
1284 (setf (find-attribute 'person 'claim->adjuster-id) '(ucw::has-very-many
1285 :label "Claims as Adjuster" :slot-name claim->adjuster-id ) )
1290 (set-attribute 'person 'policy->agent-id '(ucw::has-very-many :label "Policies
1296 (defcomponent new-person (person-display)
1305 :attributes '(first-name last-name company-name)))
1310 (defcomponent person-chooser (mewa::mewa-list-presentation)
1319 :attributes '(first-name
1337 :global-properties '(:editablep nil)
1348 (defmethod render-on :wrapping ((res response) (self person-chooser))
1351 (<:p (<:as-html "Similar contact(s) in database.
1358 (<:li (<:as-html "Select one of the contacts below"))
1361 (<:li (<ucw:a :action (answer (instance self))
1364 (<:as-html "Continue, adding a new contact")))))
1372 (defaction ok ((self new-person) &optional arg)
1375 (declare (ignore arg))
1378 (answer (instance self)))
1383 (defmethod sql-word-search ((instance standard-db-object) &rest slots)
1389 (loop for slot in slots
1392 nconc (split-sequence #
1394 Space (slot-value instance slot)))))
1397 (select (class-name (class-of instance))
1400 :where (sql-or (mapcar #'(lambda (x)
1403 (when (< 0 (length x))
1409 (mapcar #'(lambda (y)
1415 (sql-slot-value 'person y)