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
39 \begin_inset Quotes eld
43 \begin_inset Quotes erd
53 is a very useful module that works on top of the
57 framework to do rapid developing of complex data-driven web appilcations
72 was founded and developed and continues to be developed and mantained by
83 The conventions used in this manual are:
86 Code is shown in a monospace font.
87 When it is expected that the user is working in an interactive environment
88 what the user should type appears as bold, while the computer result appears
89 non-bold, for example:
102 Names of people or products are show as small caps, like
117 require further revision.
122 ToDo: Add more conventions as they are needed, possible classes of text:
123 names of concepts, name of programming entities, like variables, functions,
124 etc (which are embedded in text, should they be shown monospaced ?).
132 Protocol A Protocol for introspection on relational objects.
136 Presentations A Mewa-like
142 http://www.adrian-lienhard.ch/files/mewa.pdf
145 layer for UncommonWeb
151 http://common-lisp.net/project/ucw/
160 First we start with the data model.
161 The Meta Model Protocol (MMP) is used to provide information on the data
162 objects and how they relate to one another.
163 Its is currently implemented as a layer over CLSQL
172 , although support is planned for other backends (
183 The MMP shares its definition syntax with
189 's Object Oriented Data Definition Language (OODDL)
195 http://clsql.b9.com/manual/ref-ooddl.html
201 Shouldn't this footnote be a bibliographical entry ? or something like that
209 The macro to define view-classes is named DEF-VIEW-CLASS/META, and takes
210 the same arguments as DEF-VIEW-CLASS from CLSQL.
211 For the purposes of this simple example, we will only need two functions
212 from the MMP beyond what CLSQL provides : LIST-SLOTS and LIST-SLOT-TYPES[5].
215 We'll define a simple class to hold a user.
220 (def-view-class/meta user ()
225 ((userid :initarg :userid :accessor userid :type integer :db-kind :key)
230 (username :initarg :username :accessor username :type string :db-kind
236 (password :initarg :password :accessor password :type string :db-kind
240 and now we create a user:
245 (defparameter user (make-instance 'user :userid 1
255 :password "p@ssw0rd"))
258 We can see the slots of users running:
263 (lisp-on-lines::list-slots user)
266 (USERID USERNAME PASSWORD)
274 (lisp-on-lines::list-slot-types user)
277 ((USERID INTEGER) (USERNAME STRING) (PASSWORD STRING))
280 To see the default attributes of a class
281 \begin_inset Marginal
286 Is this correct ? Drew, please, check.
294 (lisp-on-lines::default-attributes user)
297 ((USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
300 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
303 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD))
306 To set the attributes of a class to the default values we use:
311 (lisp-on-lines::set-default-attributes user)
314 ((USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
317 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
320 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD))
323 which takes an object of the class we are working with.
324 This is going to be change so we can do this action directly on the class.
325 It is on the TODO file.
333 (lisp-on-lines::find-class-attributes user)
336 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
339 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
342 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
348 note that the mewa functions (find-attribute, set-attribute etc) can take
349 either an instance, or a class-name as a symbol:
354 (lisp-on-lines::find-class-attributes 'user)
357 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
360 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
363 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
371 (lisp-on-lines::find-class-attributes (make-instance 'user))
374 (USER (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
377 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
380 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
386 Using that information, we have enough to create an interface to the object.
391 includes a powerful presentation system
397 To see this system in action, we strongly recomend to study the presentations
398 example which comes with
403 Reading components/presentations.lisp can help understand a lot about how
404 presentations are built.
407 , but it is not dynamic enough for some of the most advanced applications.
408 Mewa defines an approach to presentations that solves that problem, but
409 the paper is written from a
414 A mixture of the two , Mewa Presentations(MP), is described here.
426 An attribute is essentially a named version of the DEFPRESENTATION slot-like
427 arguments, for example in :
432 (defpresentation person-editor (object-presentation)
437 ((string :label "First Name" :slot-name 'first-name :max-length 30)))
440 the (string :label "First Name" ...) form is an attribute definiton.
441 Attributes are accessed through FIND-ATTIRIBUTES, and are composed at run
446 's presentation system is done at compile time) to display the object.
447 This allows a very flexible system of displaying objects which is reminiscent
462 discovered this, rather than invent or design it, so there are some rough
463 edges, but its a good start.
464 Exploration baby yeah!
470 Its much easier to show this than to tell.
471 Lets present our user class.
476 , you'd define a presentation as such :
481 (defpresentation user-presentation (object-presentation)
484 ((INTEGER :LABEL "USERID" :SLOT-NAME USERID)
487 (STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
490 (STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)))
493 which could be presented using PRESENT-OBJECT :
498 (present-object user :using 'user-presentation)
501 The equivalent approach using mewa presentations is actually longer and
502 more verbose(!) but it serves to demonstrate how the MP system works.
505 Mewa Presentations adds a set of attributes to a class, keyed off the class
507 Attributes are inherited, so if you define an attribute on T, you can use
511 MP stores named attributes keyed on a class name.
512 To achieve the same functionality as the above using mp would look like
518 (setf (lisp-on-lines::find-attribute 'user :viewer)
519 \begin_inset Marginal
524 Isn't this too imperative (in contrast to functional, lispy).
532 '(lisp-on-lines::mewa-object-presentation
537 :attributes (userid username password)
542 :global-properties (:editablep nil)))
545 (:VIEWER MEWA-OBJECT-PRESENTATION
551 (USERID USERNAME PASSWORD)
562 (setf (lisp-on-lines::find-attribute 'user 'userid)
563 \begin_inset Marginal
568 Are this setfs to 'userid, 'username and 'password needed ? I (Pupeno) inspected
569 they contents at of this moment and they seem to already contain what they
578 '(integer :label "userid" :slot-name userid))
581 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
586 (setf (lisp-on-lines::find-attribute 'user 'username)
591 '(STRING :LABEL "USERNAME" :SLOT-NAME USERNAME))
594 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
599 (setf (lisp-on-lines::find-attribute 'user 'password)
604 '(STRING :LABEL "USERNAME" :SLOT-NAME PASSWORD))
607 (PASSWORD STRING :LABEL "USERNAME" :SLOT-NAME PASSWORD)
612 (lisp-on-lines::find-class-attributes 'user)
618 (:VIEWER MEWA-OBJECT-PRESENTATION
624 (USERID USERNAME PASSWORD)
633 (PASSWORD STRING :LABEL "PASSWORD" :SLOT-NAME PASSWORD)
636 (USERNAME STRING :LABEL "USERNAME" :SLOT-NAME USERNAME)
639 (USERID INTEGER :LABEL "USERID" :SLOT-NAME USERID)
645 this is all turned into a
649 presentation at runtime using MAKE-PRESENTATION, for example, the following
650 code should be enough to show what's built so far attached to the examples
656 (defcomponent lol-example (window-component)
666 (defmethod render-on ((res response) (lol-example lol-example))
676 (<ucw:render-component :component (lisp-on-lines::make-presentation
677 user :type :viewer)))
682 (defentry-point "lol.ucw" (:application *example-application*) ()
690 As you'll see, nothing is exported from the LISP-ON-LINES package.
691 If you wish to use LOL in your own package (or in UCW-USER or whatever),
692 you simply need to use the MEWA and META-MODEL packages.
695 SET-ATTRIBUTE can be used in place of (setf (find-attribute ...)) when you
696 want to "inherit" the properties of an existing attribute definition :
701 (set-attribute 'user 'password '(string :label "password: (must be at least
711 "password: (must be at leat 8 chars)"
720 Now we want to create a presentation with which to edit the username.
721 we will use the existing attributes on a subclass of mewa-object-presetation
727 (defcomponent user-editor (mewa-object-presentation)
742 :attributes '((username :label "Enter your New Username") password)
747 :global-properties '(:editablep t)))
755 (setf (find-attribute 'user :editor) '(user-editor))
758 (:EDITOR USER-EDITOR)
761 which we then can display below our earlier example :
766 (defmethod render-on ((res response) (e presentations-index))
776 As you'll see, nothing is exported from the LISP-ON-LINES package.
782 if you wish to use LOL in your own package (or in UCW-USER or whatever),
787 you simply need to use the MEWA and META-MODEL packages"
792 (<ucw:render-component :component (lisp-on-lines::make-presentation lisp-on-line
793 s::user :type :viewer))
798 (<ucw:render-component :component (lisp-on-lines::make-presentation lisp-on-line
799 s::user :type :editor)))
802 that should give you some idea on how it works ..
803 ask me when you get confused :)
809 This is Pupeno's view of how to do rapid developing of a database-driven
811 It currently is going to assume a very specific case but latter it may
815 We first start with a
819 connection of CLSQL which is set up with one line:
824 (clsql:connect '("localhost" "geo" "geo" "geogeo"))
827 which connect us to the server on
836 \begin_inset Quotes eld
840 \begin_inset Quotes erd
844 \begin_inset Quotes eld
848 \begin_inset Quotes erd
851 (this is not a smart way to generate password, don't do this).
852 To have a nice SQL environment, we also want:
857 (clsql:locally-enable-sql-reader-syntax)
862 (setf clsql:*default-caching* nil)
865 Actually, it is more than a nice environmnet, without those lines the rest
866 of the code won't work.
873 database, there's a table called
877 which has the following structure:
882 CREATE TABLE product (
907 cost double precision,
912 CONSTRAINT product_cost_check CHECK ((cost > (0)::double precision))
922 ALTER TABLE ONLY product ADD CONSTRAINT product_name_key UNIQUE (name);
927 ALTER TABLE ONLY product ADD CONSTRAINT product_pkey PRIMARY KEY (id);
932 ToDo: express the table structure in a better way.
935 Now we'll create the class that represents a product, mirroring the database
941 (lisp-on-lines::def-view-class/table "product")
944 and then we generate the default attributes (from
948 's slots) and assign it to
957 (lisp-on-lines::set-default-attributes (make-instance 'product))
958 \begin_inset Marginal
963 set-default-attributes is marked to be renamed to set-generated-attributes.
969 As you can see, we instantiate
975 set-default-attributes
977 , because it expects an object instead of a class.
978 We don't need the object anymore, so we don't save any reference to it.
979 In the future we might have a
981 set-default-attributes
983 that can use a class directly.
984 Now we set a the attribute
990 mewa-object-presentation
992 exposing the attributes we like the user to work with:
997 (setf (lisp-on-lines::find-attribute (make-instance 'product) :viewer)
1002 '(lisp-on-lines::mewa-object-presentation
1007 :attributes (name details description cost)
1012 :global-properties (:editablep nil)))
1015 The last parameter is a list of properties that will be applied to each
1019 Yet Another Example .
1022 Drew Crampsie Posted the following to comp.lang.lisp ..
1023 it just might help until he writes some real documentation.
1027 I've written a system that generates presentations for database objects
1028 based on the type and relation information in the system catalog.
1035 Mewa : Meta-level Architecture for Generic Web-Application Construction
1038 http://map1.squeakfoundation.org/sm/package/32c5401f-fa30-4a2b-80c8-1006dd462859
1041 clsql + postgres and the UCW presentation components.
1044 This is the code to add a new contact to the system.
1045 (screenshot pr0n follows).
1048 In the RENDER-ON method of my front-page i have :
1051 (let ((p (make-instance 'person :person-type-code nil)))
1054 (<:as-html "Add Person :")
1057 (<ucw:render-component
1060 :component (make-presentation
1069 :initargs '(:attributes
1072 ((person-type-code :editablep t)))))
1075 (<ucw:submit :action (new-person self p) :value "add"))
1080 This creates a drop-down list of person-types and an "add" button which
1084 (defaction new-person ((self component) person)
1090 Take a PERSON with a user-defined PERSON-TYPE-CODE,
1093 * Prompt the user for a FIRST-NAME, LAST-NAME and/or COMPANY-NAME
1096 * Search for similar PERSONs in the database.
1099 * If they exist, ask the user to select one or continue
1102 * otherwise, just continue editing the person"
1108 (call-component self (make-presentation
1117 :initargs '(:global-properties
1120 (:size 25 :editablep t))))))
1126 (call-component self (make-presentation
1129 (find-or-return-named-person self named-person)
1137 (defaction find-or-return-named-person ((self component) person)
1143 If any similiar contacts exist in the database,
1146 select one or continue with the current person
1149 PERSON must have FIRST-NAME, LAST-NAME and COMPANY-NAME bound."
1152 (let ((instances (sql-word-search person 'first-name 'last-name 'company-name)
1159 (call-component self (make-presentation
1165 :type 'person-chooser
1171 `(:instances ,instances)))
1179 You can hardly tell it's a web application ...
1180 there is no checking of CGI params etc...
1181 just nice code in the order i wanted to write it.
1187 http://tech.coop/img/screenshots/select-person-type.jpg
1190 http://tech.coop/img/screenshots/enter-person-name.jpg
1193 http://tech.coop/img/screenshots/select-similar-contacts.jpg
1196 http://tech.coop/img/screenshots/edit-person-details.jpg
1199 http://tech.coop/img/screenshots/view-recent-changes.jpg
1202 All of the code used to create the presentations for this is below my sig.
1203 I do eventually plan to release the presentation system as Free Software,
1204 it just needs a little cleaning up.
1205 E-mail me for a sneak peak.
1214 drewc at tech dot coop
1217 "Never mind the bollocks -- here's the sexp's tools."
1221 Krueger on comp.lang.lisp
1226 (def-view-class/table "person")
1231 (set-default-attributes (make-instance 'person)
1236 (defcomponent person-display (mewa::two-column-presentation)
1244 (defcomponent one-line-person (mewa::mewa-one-line-presentation)
1250 (:default-initargs :attributes '(first-name last-name company-name)))
1255 (setf (find-attribute 'person :one-line) '(one-line-person))
1260 (set-attribute 'person 'person-type-code '(code-select :category 1))
1265 (set-attribute 'person 'province-state-code '(code-select :category 2))
1270 (setf (find-attribute 'person :viewer) '(person-display :global-properties
1276 (set-attribute 'person :editor '(person-display :global-properties (:editablep
1282 (setf (find-attribute 'person 'claim->adjuster-id) '(ucw::has-very-many
1283 :label "Claims as Adjuster" :slot-name claim->adjuster-id ) )
1288 (set-attribute 'person 'policy->agent-id '(ucw::has-very-many :label "Policies
1294 (defcomponent new-person (person-display)
1303 :attributes '(first-name last-name company-name)))
1308 (defcomponent person-chooser (mewa::mewa-list-presentation)
1317 :attributes '(first-name
1335 :global-properties '(:editablep nil)
1346 (defmethod render-on :wrapping ((res response) (self person-chooser))
1349 (<:p (<:as-html "Similar contact(s) in database.
1356 (<:li (<:as-html "Select one of the contacts below"))
1359 (<:li (<ucw:a :action (answer (instance self))
1362 (<:as-html "Continue, adding a new contact")))))
1370 (defaction ok ((self new-person) &optional arg)
1373 (declare (ignore arg))
1376 (answer (instance self)))
1381 (defmethod sql-word-search ((instance standard-db-object) &rest slots)
1387 (loop for slot in slots
1390 nconc (split-sequence #
1392 Space (slot-value instance slot)))))
1395 (select (class-name (class-of instance))
1398 :where (sql-or (mapcar #'(lambda (x)
1401 (when (< 0 (length x))
1407 (mapcar #'(lambda (y)
1413 (sql-slot-value 'person y)