Commit 8d5ca8d5 authored by Dave Cooper's avatar Dave Cooper
Browse files

Merge pull request #1 from teodor-gelu/master

Hi Dave 
parents 1f4c642a e61340a2
;; Copyright 2002-2011 Genworks International and Genworks BV
;; This file is part of the General-purpose Declarative
GDL contains a built-in web server and supports the creation of
generative @i{web-based} user interfaces@footnote{GDL does not contain
support for native desktop GUI applications. Although the host Common
Lisp environment (e.g. Allegro CL or LispWorks) may contain a GUI
builder and Integrated Development Environment, and you are free to
use these, Genworks cannot provide support for them.}. Using the same
@b{define-object} syntax which you have already learned, you can
define web pages, sections of web pages, and form control elements
such as type-in fields, checkboxes, and choice lists. Using this
capability does require a basic working knowledge of the HTML
language@footnote{We will not cover HTML in this manual, but plentiful
resources are available online and in print}.
Any web extensions such as custom JavaScript and JavaScript libraries
can also be used, as with any standard web application.
With the primitive objects and functions in its ``GWL'' package, GDL
supports both the traditional ``Web 1.0'' interfaces (with fillout
forms, page submittal, and complete page refresh) as well as so-called
``Web 2.0'' interaction with AJAX.
@section Package and Environment for Web Development
Similarly to @b{gdl:define-package}, you can use
@b{gwl:define-package} in order to create a working package which has
access to the symbols you will need for building a web application (in
addition to all the other GDL symbols).
The @b{:gwl-user} package is pre-defined and may be used for practice
work. For real projects, you should define your own package using
The acronym ``GWL'' stands for Generative Web Language, which is not
actually a separate language from GDL itself, but rather a set of
primitive objects and functions available with GDL for building web
applications. The YADD reference documentation for package
``Generative Web Language'' provides detailed specifications for all
the primitive objects and functions.
@section Traditional Web Pages and Applications
To make a GDL object presentable as a web page, the following two
steps are needed:
@item Mix @b{base-html-sheet} into the object definition.
@item define a @b{:function} called @b{main-sheet} within the object definition.
@end enumerate
The @b{main-sheet} function should return valid HTML for the page. The
easiest way to produce HTML is with the use of an HTML generating
library, such as @i{cl-who}@footnote{} or
both of which are built into GDL.
For our examples we will use cl-who, which is currently the standard
default HTML generating library used internally by Genworks. Here we
will make note of the major features of cl-who while introducing the
examples; for complete documentation on cl-who, please visit the page
at Edi Weitz' website listed in the footnote.
@subsection A Simple Static Page Example
In the following example, the GWL convenience macro @b{with-cl-who} is
used; this sets up a standard default environment for outputting HTML
within a GWL application.
@include examples/gwl-1.lisp
@end cartouche
@end smallexample
@sp 2
Produces the following HTML output:
@sp 2
@include examples/gwl-1.html
@end cartouche
@end smallexample
@sp 2
Which looks similar to the following in a web browser:
@sp 1
@center @image{images/gwl-1,,3.2in}
@sp 1
Several important concepts are packed into the above example. Note the following:
@item Our convenience macro @b{with-cl-who} is used to wrap the native
@b{with-html-output} macro which comes with the cl-who library.
@item We use the keyword argument @b{:indent t} in order to pretty-print
the generated HTML. This does not affect the browser display but can
make the generated HTML easier to read and debug. This option should
be left as nil (the default) for production deployments.
@item The @b{fmt} symbol has special meaning within the cl-who environment
and works the same as a Common Lisp @b{format nil} in order to evaluate
a format string together with matching arguments, and produce a
string at runtime.
@item The @b{str} symbol has special meaning within the cl-who environment
and works by evaluating an expression at runtime to return a string
or other printable object, which is then included at that point in
the HTML output.
@item Expressions within the @b{body} of an HTML tag have to be evaluated,
usually by use of the @b{fmt} or @b{str} in cl-who.
There are three examples of this in the above sample: one @b{fmt}
and two @b{str}.
@item Expressions within a @i{tag attribute} are always evaluated automatically,
and so do @b{not} require a @b{str} or other special symbol to force
evaluation at runtime. Tag attributes in HTML (or XML) are
represented as a plist spliced in after a tag name, wrapped in extra
parentheses around the tag name. In the sample above, the
@b{:border (the table-border)} is an example of a tag attribute
on the @b{:table} tag. Notice that the expression @b{(the table-border)}
does not need @b{str} to be evaluated - it gets evaluated automatically.
@item In cl-who, if a tag attribute evaluates to nil, then that tag attribute
will be left out of the output completely. For example if @b{the table-border}
evaluates to nil, then the @b{:table} tag will be outputted without any
attributes at all. This is a convenient way to conditionalize tag
@item The URL @b{http://localhost:9000/make?object=gwl-user::president} is published
automatically based on the package and name of the object definition. When
you visit this URL, the response is redirected to a unique URL identified by
a @i{session ID}. This ensures that each user to your application site will
see their own specific instance of the page object. The session ID is
constructed from a combination of the current date and time, along with
a pseudo-random number.
@end itemize
@subsection A Simple Dynamic Page which Mixes HTML and Common Lisp/GDL
Within the cl-who environment, it is possible to include any standard
Common Lisp structures such as @b{let}, @b{dolist}, @b{dotimes}, etc,
which accept a @i{body} of code. The requirement is that any internal
code body must be wrapped in a list beginning with the special symbol
@b{htm}, which has meaning to cl-who.
The following example uses this technique to output an HTML table row
for each ``row'' of data in a list of lists:
@include examples/gwl-2.lisp
@end cartouche
@end smallexample
@sp 2
The output looks similar to the following in a web browser:
@sp 2
@center @image{images/gwl-2,,3.2in}
@sp 2
Note the following from this example:
@item @b{title} is a @b{let} variable, so we use @b{(str title)} to evaluate it
as a string. We do not use @b{(str (the title))} because @b{title} is a
local variable and not a message (i.e. slot) in the object.
@item Inside the @b{dolist}, we ``drop back into'' HTML mode using the @b{htm}
@end itemize
@subsection Linking to Multiple Pages
The base-html-sheet mixin provides a @b{self-link} message for the purpose of
generating a hyperlink to that page. Typically you will have a ``parent'' page
object which links to its ``child'' pages, but GDL pages can link to other pages
anywhere in the GDL tree@footnote{In order for dependency-tracking to work properly,
the pages must all belong to the same tree, i.e. they must share a common root object}.
In the example below, we provide links from a parent page into a child page
with detailed information on each president:
@include examples/gwl-3.lisp
@end cartouche
@end smallexample
@sp 2
@include examples/gwl-3a.lisp
@end cartouche
@end smallexample
The output looks similar to the following in a web browser:
@sp 2
@center @image{images/gwl-3,,3.6in}
@sp 2
Note the following from this example:
@item The @b{write-self-link} message is a function which can take a keyword
argument of @b{:display-string}. This string is used for the actual hyperlink
@item There is a @b{write-back-link} message which similarly can take a keyword
argument of @b{:display-string}. This generates a link back to @b(the return-object)
which, by default in base-html-sheet, is @b(the parent).
@end itemize
@subsection Form Controls and Fillout-Forms
@subsubsection Form Controls
GDL provides a set of primitives useful for generating the standard HTML
such as text, checkbox, radio, submit, menu, etc. These should be
instantiated as child objects in the page, then included in the HTML
for the page using @b{str} within an HTML @b{form} tag (see next section).
@subsubsection Fillout Forms
A traditional web application must enclose form controls inside a @b{form}
tag and specify an @b{action} (a web URL) to receive and respond to the
form submission. The response will cause the entire page to refresh with
a new page. In GDL, such a form can be generated by wrapping the layout
of the form controls within the @b{with-html-form} macro.
Here is an example which allows the user to enter a year, and the
application will respond with the revenue amount for that year. Additional
form controls are also provided to adjust the table border and cell padding.
This example when instantiated in a web browser, might look like this:
@sp 2
@center @image{images/gwl-3b,,3.6in}
@sp 2
@sp 2
@include examples/gwl-3b.lisp
@end cartouche
@end smallexample
@section Partial Page Updates with gdlAjax
AJAX stands for Asynchronous JavaScript and
XML@footnote{}, and
allows for more interactive web applications which respond to user
events by updating only part of the web page. The ``Asynchronous'' in
Ajax refers to the ability for a web page to continue interacting
while one part of the page is being updated by a server
request. Requests need not be Asynchronous, they can also be
Synchronous (``SJAX''), which would cause the web browser to block
execution of any other tasks while the request is being carried
out. The ``XML'' refers to the format of the data that is typically
returned from an AJAX request.
GDL contains a simple framework refered to as @i{gdlAjax} which
supports a uniquely convenient and generative approach to AJAX (and
SJAX). With gdlAjax, you use standard GDL object definitions and child
objects in order to model the web page and the sections of the page,
and the dependency tracking engine which is built in to GDL
automatically keeps track of which sections of the page need to be
updated after a request.
Moreover, the state of the internal GDL model which represents the
page and the page sections is kept identical to the displayed state of
the page. This means that if the user hits the ``Refresh'' button in
the browser, the state of the page will remain unchanged. This is not
true of some other Ajax frameworks.
@subsection Steps to create a gdlAjax application
First, it is important to understand that the fundamentals from the
previous section on Standard Web Applications still apply for gdlAjax
applications --- that is, HTML generation, page linking, etc. These
techniques will all still work in a gdlAjax application.
To produce a gdlAjax application involves three main differences from
a standard web application:
@item You mix in @b{base-ajax-sheet} instead of @b{base-html-sheet}.
@b{base-ajax-sheet} mixes in @b{base-html-sheet}, so it will still
provide all the functionality of that mixin. In fact, you can use
@b{base-ajax-sheet} in standard web applications and you won't notice
any difference if you do everything else the same.
@item Instead of a @b{write-html-sheet} message, you specify a @b{main-sheet-body}
message. The @b{main-sheet} can be a computed-slot or function,
and unlike the @b{write-html-sheet} message, it should simply return a
string, not send output to a stream. Also, it only fills in the body
of the page --- everything between the <body> and </body> tags. The
head tag of the page is filled in automatically and can be customized
in various ways.
@item Any sections of the page which you want to be able to change state
in response to an Ajax call must be made into separate page
sections, or ``sheet sections,'' and the HTML for their @b{main-div}
included in the main page's @b{main-sheet-body} by use of cl-who's
@b{str} directive.
@end enumerate
@sp 2
@include examples/gwl-4.lisp
@end cartouche
@end smallexample
Note the following from this example:
@item We mix in @b{base-ajax-sheet} and specify a @b{main-sheet-body} slot, which
uses @b{with-cl-who-string} to compute a string of HTML. This approach is also
easier to debug, since the @b{main-sheet-body} string can be evaluated in the
tasty inspector or at the command-line.
@item We use @b{str} to include the string for the main page
section (called @b{main-section} in this example) into the @b{main-sheet-body}.
@item In the @b{main-section}, we also use @b{str} to include the
html-string for each of three form-controls. We have provided a form control
for the table border, the table padding, and the revenue year to look up.
@item The only page section in this example is @b{(the main-section)}. This is
defined as a child object, and has its @b{main-view} computed in the parent and
passed in as an input. The @b{sheet-section} will automatically compute a
@b{main-div} message based on the @b{main-view} that we are passing in. The
@b{main-div} is simply the @b{main-view}, wrapped with an HTML DIV (``division'')
tag which contains a unique identifier for this section, derived from the
root-path to the GDL object in the in tree which represents the sheet section.
@item We introduce the CL function @b{publish-gwl-app}, which makes available
a simplified URL for visiting an instance of this object in the web browser. In
this case, we can access the instance using @b{http://localhost:9000/revenue-lookup}
@end itemize
@subsection Including Graphics
The fundamental mixin or child type to make a graphics viewport is
@b{base-ajax-graphics-sheet}. This object definition takes several
optional input-slots, but the most essential is the
@b{:display-list-objects} and the @b{:display-list-object-roots}. As
indicated by their names, you specify a list of nodes to include in
the graphics output with the @b{:display-list-objects}, and a list of
nodes whose @b{leaves} you want to display in the graphics output with
the @b{:display-list-object-roots}. View controls, rendering format,
action to take when clicking on objects, etc, can be controlled with
other optional input-slots.
The following example contains a simple box with two graphics viewports
and ability to modify the length, height, and and with of the box:
@sp 2
@include examples/gwl-5.lisp
@end cartouche
@end smallexample
This will produce a web browser output similar to the following:
@sp 2
@center @image{images/gwl-5,,4.2in}
@sp 2
Note the following from this example:
@item The @b{(:use-raphael? t)} enables raphael for SVG or VML output.
@item The @b{:raphael} image-format generates SVG or VML, depending on the browser.
@item We conditionally include development-links for full Update and SetSelf! actions.
@item We include two viewports in the main-sheet-bodyd, elements from a sequence of size 2.
@item In the inputs-section, we use the @b{html-string} message from each form-control
to display the default decoration (prompt, etc).
@end itemize
@section Development of Projects using Files and Packages
While in the previous chapter most of the coding was done at the
prompt level, in this chapter the coding is done by using a project
management approach, in separate files. Each time you start a new GDL
project, it is recommended to structure your applications in a modular
fashion. This allows you to incrementally compile and load your
applications. To illustrate the best practice in creating a new GDL
project, we will describe in detail a small application. This
application is created and loaded following the next three steps:
@i{Note: To avoid confusion, at the beginning of each sample code the file name will}
@i{be mentioned with the following notation:} @b{;;--file-name.lisp--}
@i{and in some cases, comments will be added.}
@b{Step 1.} Create a new folder in your HOME folder, named
@b{shock-absorber}. Within this folder create a new folder named
@b{Step 2.} In the @b{source} directory create the next three lisp
files (the file names used below follow the normal convention, but
your file names can potentially be different):
@b{The first file} will be named @b{package.lisp} and will contain
your package definition as mentioned below:
@sp 1
(in-package :gdl-user)
(define-package :shock-absorber ;;the name of your new package as key word
(:nicknames :shock :picasso ) ;;the nicknames of your new package
(:use ) ;;the packages used by your new package
(:export #:assembly
;; Exported symbols
@end smallexample
@end cartouche
@sp 1
It is also possible to skip the package specification @b{(in-package :gdl-user)}
as presented in this example code, if you refer to the package definition macro
as @b{gdl:define-package}.
Note that you can work directly in the @b{:gdl-user} package, which is
an empty pre-defined package for your use, if you do not wish to make
a new package just for scratch work.
For real projects, however, it is recommended that you make and work
in your own GDL package.
In GDL, the macro @b{define-package} is used to set up a new working package.
Packages defined with @b{gdl:define-package} will implicitly @b{:use}
the GDL package and the Common-Lisp package, so you will have access
to all exported symbols in these packages without prefixing them with
their package name. You may extend this behavior by calling
@b{gdl:define-package} and adding additional packages to use with
@b{(:use ...)}. For example, if you want to work in a package with
access to GDL exported symbols, Common Lisp exported symbols, and
symbols which are defined in other packages like @b{:my-utilities}, you
could set it up as follows:
@sp 1
(gdl:define-package :shock-absorber
(:nicknames :shock :picasso )
(:use :my-utilities)
(:export #:assembly
;; Exported symbols
@end smallexample
@end cartouche
@sp 1
@i{Note: The ``Exported symbols'' option will be addressed in the next
chapter on a real case. However, the fundamental concept is that
symbols are contained in packages, and can be marked as ``internal''
or ``external,'' which is roughly analogous to ``private'' and
``public'' in languages like C++ and Java --- that is, internal
symbols should only be accessed from inside the same package where
they are defined. In the above example, the symbol @b{assembly} is
exported from the @b{:shock-absorber} package. Note that symbols named
in the export list should be prepended with ``#:'' --- this is for
``package hygiene'' reasons which are not important to understand at
this point.}
@b{The second file} will be named @b{assembly.lisp} and will contain
the definition for the toplevel ``assembly'' (i.e. object) of your
application. As you have already seen, this file will contain a GDL
object definition so it will have the following structure:
As stated in previous chapters, @b{define-object} is the basic macro for defining objects
(i.e. creating classes) in GDL. A GDL object definition in which:
@itemize @bullet
@item @b{class-name} is any non-keyword symbol [i.e. a symbol not preceded by a colon (``:'')]. A GDL Class will be generated for this symbol, so that any name you use will override a previous class if one is already defined with the same name.
@item @b{mixin-list} is a list of other class-names (i.e. object definition names) from which this object will inherit. Note that the standard mixin @b{gdl:vanilla-mixin} gets mixed in automatically with any GDL object and carries some of the basic GDL functionalities (messages).
@item @b{specification-plist} is a plist made up of pairs made from special keywords and expression lists.
@end itemize
For the moment we will exemplify the use of the specification-plist by
defining a cylinder child object named @b{my-cylinder} contained by
the @b{new-object} as mentioned below:
@sp 1
;;-- assembly.lisp--
(in-package :shock-absorber)
(define-object assembly (base-object)
:input-slots ()
:trickle-down-slots ()
:computed-slots ()
:objects ((my-cylinder :type 'cylinder
:length 10
:radius 3))
:hidden-objects ()
:functions ()
:methods ())
@end smallexample
@end cartouche
@sp 1
The @b{:objects} and @b{:hidden-objects} are used to specify a list of
instance specifications, where each instance is considered to be a
``child'' object of the current object, @b{:hidden-objects} serves the