| CGI-MxScreen documentation | view source | Contained in the CGI-MxScreen distribution. |
CGI::MxScreen - a multi-screen stateful CGI framework
require CGI::MxScreen;
my $manager = CGI::MxScreen->make(
-bgcolor => "#dedeef",
-screens =>
{
"state_1" =>
[-class => "STATE_1", -title => "Hello"],
"state_2" =>
[-class => "STATE_2", -title => "Hello #2"],
},
-initial => "state_1",
-version => "1.0",
);
$manager->play();
CGI::MxScreen is a framework for building multi-screen stateful CGI
programs. It is rather object-oriented, with some peculiarities brought
by persistency constraints: all objects must be handled by Storable.
CGI::MxScreen is based on the CGI module, and co-operates with it,
meaning you are able to use most CGI calls normally. The few places
where you should not is where CGI::MxScreen supersedes the CGI
functionalities: for instance, there's no need to propagate hidden values
when you use CGI::MxScreen.
CGI::MxScreen is architected around the concept of screens.
Among the set of defined screens within the same script, only one is visible
at a time. One moves around the various screens by pressing buttons,
which submit data to the server and possibly move you to a different screen.
The state machine is handled by CGI::MxScreen, the user only defines
which state (screen) a button shall move the application to
CGI::MxScreen is stateful in the sense that many of the runtime objects
created to operate (and screens are among those) are made persistent.
This is a very interesting property, because you do not have to worry
too much about the underlying stateless nature of the CGI protocol. The
CGI module brought the statefulness to the level of form controls, but
CGI::MxScreen raises it to the level of the application itself.
CGI::MxScreen is not meant to be used for so-called quick and dirty
scripts, or for scripts which do not require some fair amount of round trips
between the browser and the server. You'll be better off with using
the good old CGI module. However, for more complex web applications,
where there is a fair amount of processing required on the server side, and
where each script involves several states, CGI::MxScreen is for you.
OK, enough talking.
This section describes the CGI::MxScreen framework. If you wish to
read about the interface of the CGI::MxScreen managing object, please
skip down to "INTERFACE".
Here are the main features of CGI::MxScreen:
CGI module. You can continue to use CGI
routines wherever you like. Storable is
natively supported.
See CGI::MxScreen::Session::Medium for the medium interface and
CGI::MxScreen::Serializer for the serialization interface. CGI::MxScreen::Screen and redefining the display routine, at least.
There are also enter and leave hooks for each screen.
Each created screen object is made persistent accross the whole session.
See CGI::MxScreen::Screen for the full interface. display routine is called
will be trapped and discarded (with logging showing the place where such a
violation occurs). This architecturally enforces proper application behaviour.
Furthermore, by default, the whole output is buffered until it is
time to save the context, thereby protecting against further submits
with a partially received form on the browser side, and also strengthening
the protection when the application uses bounce exceptions to jump into
another state. Log::Agent, and application logging is
done via Log::Agent::Logger, which ensures the maximum flexibility.
Logfile rotation is also supported via Log::Agent::Rotate.
Configuration of the various logging parameters is done via the
CGI::MxScreen::Config interface. CGI::MxScreen uses Carp::Datum internally. If you have chosen to
install a non-stripped version, you may trace parts of the module to better
understand what is going on with the various callbacks you register.Here is a high-level description of the processing flow when issuing requests
to a CGI::MxScreen script:
display routine.
Any processing action attached to the button is also run at that point. leave routine is called on the
old screen and enter is called on the new one. display routine
is called to actually generate the form's content. Before they output
anything, screens are allowed to request the bouncing to some other state,
based on some local information (but if output buffering is configured, any
spurious output from the old screen will be cleanly discarded).
Any other exception that can occur during display is trapped and cleanly
logged, before displaying an internal error message. The following example demonstrates the various common operations that need
to be performed with CGI::MxScreen.
An important comment first: if we forget about the fact that you need
an object per screen (which has some code overhead compared to using
plain CGI), you will need to write more declarative code with
CGI::MxScreen than you would with CGI, but this buys you more
persistent state for fields, and lets you define the state transitions and
associated processing for buttons.
Moreover, please note that this example could be written in less code
by using the CGI module only. But CGI::MxScreen is not aimed at
simple scripts.
Our example defines a two-state script, where one choose a color in the first screen, and then a week day in the second screen. The script reminds you about the choice made in the other screen, if any. It is possible to "redraw" the first screen to prove that the selection made is sticky. First, the whole script:
1 #!/usr/local/bin/perl -T
2
3 package Color; use base qw(CGI::MxScreen::Screen);
4
5 use CGI qw/:standard/;
6
7 sub init {
8 my $self = shift;
9 $self->vars->{color} = "";
10 }
11
12 sub display {
13 my $self = shift;
14 print h1($self->screen_title);
15
16 my $color = $self->record_field(
17 -name => "color",
18 -storage => "color",
19 -default => $self->vars->{color} || "Green",
20 -override => 1,
21 -values => [qw(Red Green Blue White Black Yellow Orange Cyan)],
22 );
23
24 print p("You told me your favorite weekday was", $self->vars->{weekday})
25 if exists $self->vars->{weekday};
26
27 print p("Your favorite color is", popup_menu($color->properties));
28
29 my $ok = $self->record_button(
30 -name => "Next",
31 -target => "Weekday");
32
33 my $redraw = $self->record_button(
34 -name => "Redraw",
35 -target => $self->current_screen);
36
37 print submit($ok->properties), submit($redraw->properties);
38 }
39
40 package Weekday; use base qw(CGI::MxScreen::Screen);
41
42 use CGI qw/:standard/;
43
44 sub init {
45 my $self = shift;
46 $self->vars->{weekday} = "";
47 }
48
49 sub display {
50 my $self = shift;
51 print h1($self->screen_title);
52
53 print p("You told me your favorite color was", $self->vars->{color});
54
55 my $weekday = $self->record_field(
56 -name => "day",
57 -storage => "weekday",
58 -default => $self->vars->{weekday} || "Mon",
59 -override => 1,
60 -values => [qw(Mon Tue Wed Thu Fri Sat Sun)],
61 );
62
63 print p("Your favorite weekday is", popup_menu($weekday->properties));
64
65 my $back = $self->record_button(
66 -name => "Back",
67 -target => $self->spring_screen,
68 );
69
70 print submit($back->properties);
71 }
72
73 package main;
74
75 require CGI::MxScreen;
76
77 my $manager = CGI::MxScreen->make(
78 -screens =>
79 {
80 'Color' => [-class => 'Color', -title => "Choose Color" ],
81 'Weekday' => [-class => 'Weekday', -title => "Choose Day" ],
82 },
83 -initial => ['Color'],
84 );
85
86 $manager->play();
87
Let's study this a piece at a time:
1 #!/usr/local/bin/perl -T 2
The classical declaration for a CGI script, in taint mode.
3 package Color; use base qw(CGI::MxScreen::Screen); 4
This defines the first state, Color. It inherits from
CGI::MxScreen::Screen, as it should.
5 use CGI qw/:standard/; 6
We're going to use CGI routines. We could do with less than what is
exported by the :standard tag, but I did not bothered.
7 sub init {
8 my $self = shift;
9 $self->vars->{color} = "";
10 }
11
The init() routine is called on the screen the first time it is created.
Upon further invocations, the same screen object will be used and re-used
each time we need to access the Color state.
To differentiate from a plain CGI script which would use hidden parameters
to propagate the information, we store the application variable in the
persistent hash table, which every screen can access through $self->vars.
Here, we initialize the "color" key, because any access to an unknown
key is an error at runtime (to avoid malicious typos).
12 sub display {
13 my $self = shift;
The display() routine is invoked by the state manager on the screen
selected for displaying.
14 print h1($self->screen_title); 15
Prints screen title. This refers to the defined title in the manager, which are declared for each known screen further down on lines 78-82.
16 my $color = $self->record_field(
17 -name => "color",
18 -storage => "color",
19 -default => $self->vars->{color} || "Green",
20 -override => 1,
21 -values => [qw(Red Green Blue White Black Yellow Orange Cyan)],
22 );
23
This declaration is very important. It tells CGI::MxScreen that the
screen makes use of a field named "color", and whose value should be
stored in the global persistent hash under the key "color" (as per
the -storage indication).
The remaining attributes are simply collected to be passed to the
popup_menu() routine via $color-properties> below. They could be
omitted, and added inline when popup_menu() is called, but it's best
to regroup common things together.
The underlying object created by record_field() will be serialized
and included in the CGI::MxScreen context (only the relevant attributes
are serialized, i.e. CGI parameters such as -values are not).
This will allow the processing engine to honour some meaningful actions,
such as validation, storage, or on-the-fly patching.
Another important property of those objects is that CGI::MxScreen will
update the value attribute, which would be noticeable if there was no
-default line: you could query $color-value> to get the current
CGI parameter value, as submitted.
24 print p("You told me your favorite weekday was", $self->vars->{weekday})
25 if exists $self->vars->{weekday};
26
If we have been in the Weekday screen, then the key "weekday" will
be existing in the global hash $self->vars, because it is created by
the init() routine of that object, at line 46. If we tried to access
the key without protecting by the exists test on line 25, we'd get
a fatal error saying:
access to unknown key 'weekday'
This protection can be disabled if you want it so, but it is on by default. It will probably save you one day, but unfortunately this is a runtime check.
27 print p("Your favorite color is", popup_menu($color->properties));
28
The above is generating the sole input of this screen, i.e. a popup
menu so that you can select your favorite color. Note that we're passing
popup_menu(), which is a routine from the CGI module, a list of
arguments derived from the recorded field $color, created at line 16.
29 my $ok = $self->record_button( 30 -name => "Next", 31 -target => "Weekday"); 32
This declaration is also very important. We're using record_button()
to declare a state transition: we wish to move to the Weekday screen
when the button Next is pressed.
33 my $redraw = $self->record_button( 34 -name => "Redraw", 35 -target => $self->current_screen); 36
The Redraw button simply redisplays the current screen, i.e. there is no transition to another screen (state). The current_screen routine returns the name of the current screen we're in, along with all the parameters we were called with, so that the transition is indeed towards the exact same state.
37 print submit($ok->properties), submit($redraw->properties); 38 } 39
We're finishing the display routine by calling the submit() routine
from the CGI module to generate the submit buttons. Here again, we're
calling properties() on each button object to expand the CGI parameters,
just like we did for the field on line 27.
40 package Weekday; use base qw(CGI::MxScreen::Screen); 41 42 use CGI qw/:standard/; 43
This defines the second state, Weekday. It inherits from
CGI::MxScreen::Screen, as it should. We also import the CGI
functions in that new package.
Note that the name of the class need not be the name of the state. The association between state name and classes is done during the creation of the manager object (see lines 78-82).
44 sub init {
45 my $self = shift;
46 $self->vars->{weekday} = "";
47 }
48
Recall that init() is called when the screen is created. Since screen
objects are made persistent for the duration of the whole session (i.e.
while the user is interacting with the script's forms), that means the
routine is called once for every screen that gets created.
Here, we initialize the "weekday" key, which is necessary because we're
going to use it line 58 below...
49 sub display {
50 my $self = shift;
51 print h1($self->screen_title);
52
This is the display() routine for the screen Weekday. It will be
called by the CGI::MxScreen manager when the selected state is "Weekday"
(name determined line 81 below).
53 print p("You told me your favorite color was", $self->vars->{color});
54
We remind them about the color they have chosen in the previous screen. Note that we don't rely on a hidden parameter to propagate that value: because it is held in the global persistent hash, it gets part of the session context and is there for the duration of the session.
55 my $weekday = $self->record_field(
56 -name => "day",
57 -storage => "weekday",
58 -default => $self->vars->{weekday} || "Mon",
59 -override => 1,
60 -values => [qw(Mon Tue Wed Thu Fri Sat Sun)],
61 );
62
The declaration of the field used to ask them about their preferred week day.
It looks a lot like the one we did for the color, on lines 16-22, with the
exception that the field name is "day" but the storage in the context
is "weekday" (we used the same string "color" previously).
63 print p("Your favorite weekday is", popup_menu($weekday->properties));
64
The above line generates the popup. This will create a selection list
whose CGI name is "day". However, upon reception of that parameter,
CGI::MxScreen will immediately save the value to the location identified
by the -storage line, thereby making the value available to the application
via the $self->vars hash.
65 my $back = $self->record_button( 66 -name => "Back", 67 -target => $self->spring_screen, 68 ); 69
We declare a button named Back, which will bring us back to the screen
we were when we sprang into the current screen. That's what spring_screen
is about: it refers to the previous stable screen. Here, since there is
no possibility to remain in the current screen, it will be the previous screen.
But if we had a redraw button like we had in the Color screen, which
would make a transition to the same state, then spring_screen will still
correctly point to Color, whereas previous_screen would be Weekday
in that case.
70 print submit($back->properties); 71 } 72
This closes the display() routine by generating the sole submit button
for that screen.
73 package main; 74
We now leave the screen definition and enter the main part, where the
CGI::MxScreen manager gets created and invoked. In real life, the code
for screens would not be inlined but stored in a dedicated file, one file
for each class, and the CGI script would only contain the following code,
plus some additional configuration.
75 require CGI::MxScreen; 76
We're not "using" it, only "requiring" since we're creating an object, not using any exported routine.
77 my $manager = CGI::MxScreen->make(
78 -screens =>
79 {
80 'Color' => [-class => 'Color', -title => "Choose Color" ],
81 'Weekday' => [-class => 'Weekday', -title => "Choose Day" ],
82 },
83 -initial => ['Color'],
84 );
85
The states of our state machine are described above. The keys of the
-screens argument are the valid state names, and each state name is
associated with a class, and a screen title. This screen title will be
available to each screen with $self->title, but there's no
obligation for screens to display that information. However, the manager
needs to know because when the display() routine for the script is called,
the HTML header has already been generated, and that includes the title.
The act of creating the manager object raises some underlying processing: the session context is retrieved, incoming parameters are processed and silently validated.
86 $manager->play(); 87
This finally launches the state machine: the next state is computed, action callbacks are fired, and the target screen is displayed.
To learn about the interface of the CGI::MxScreen manager object,
see "INTERFACE" below.
To learn about the screen interface, i.e. what you must implement when you derive your own objects, what you can redefine, what you should not override (the other features that you cannot redefine, so to speak), please read CGI::MxScreen::Screen.
To learn more about the configuration options, see CGI::MxScreen::Config.
For information on the processing done on recorded fields, read CGI::MxScreen::Form::Field and CGI::MxScreen::Form::Utils.
For information on the state transitions that can be recorded, and the associated actions, see CGI::MxScreen::Form::Button.
The various session management schemes offered are described in CGI::MxScreen::Session::Medium.
The layering hooks allowing you to control where the generated HTML for the current screen goes in your grand formatting scheme are described in CGI::MxScreen::Layout.
Finally, the extra HTML-generating routines that are not implemented by
the CGI module are presented in CGI::MxScreen::HMTL.
This sections documents in a central place the state and callback
representations that can be used throughout the CGI::MxScreen framework.
Those specifications must be serializable, therefore all callbacks are expressed in various symbolic forms, avoiding code references.
Do not forget that all the arguments you specify in callbacks and screens
get serialized into the context. Therefore, you must make sure your
objects are indeed serializable by the serializer (which is Storable
by default, well, actually CGI::MxScreen::Serializer::Storable, which is
wrapping the Storable interface to something CGI::MxScreen understands).
See CGI::MxScreen::Config to learn how to change the
serializer, and CGI::MxScreen::Serializer for the interface it must
follow.
A state is a screen name plus all the arguments that are given to its
display() routine. However, the language used throughout this
documentation is not too strict, and we tend to blurr the distinction between
a state and a screen by forgetting about the parameters. That is because,
in practice, the parameters are simply there to offer a slight variation
of the overall screen dispay, but it is fundamentally the same screen.
Anyway, a state can be either given as:
-screens (see Creation Routine below), and the
screen's display() routine will be called without any parameter. display(). For instance:
["Color", "blue"]
display("blue") on the
screen object known as Color.When an argument expects a callback, you may provide it under the foloowing forms.
'validate'.
CGI::MxScreen::Form::Button, it specifies a
routine to call on the screen object, without any user parameter. However,
within a CGI::MxScreen::Form::Field, it could be a routine to lookup
within the utility namespaces. More on the latter in Utility Path. ['routine', @args]
routine(@args) should be called on the screen object. [$obj, 'routine', @args]
CGI::MxScreen::Form::Button objects only.The public interface with the manager object is quite limited.
The main entry points are the creation routine, which configures the
overall operating mode, and the play() routine, which launches the
state machine resolution.
As usual, the creation routine is called make(). It takes a list of
named arguments, some of which are optional:
-bgcolor => colorOptional, sets the default background color to be used for all screens.
If unspecified, the value is gray75, aka "#bfbfbf", which is the
default background in Netscape on Unix. The value you supply will be
used in the BGCOLOR HTML tag, so any legal value there can be used.
For instance:
-bgcolor => "beige"
You may override the default background on a screen basis, as explained in "Creation Routine" in CGI::MxScreen::Screen.
-initial => scalar | array_refMandatory, defines the initial state. See States above for the actual format details.
The following two forms have identical effects:
-initial => ["Color"]
-initial => "Color"
and both define a state Color whose display() routine is called
without arguments.
-layout => layout_objectOptional, provides a CGI::MxScreen::Layout object to be used for laying
out the screen's HTML generated by display(). See CGI::MxScreen::Layout
for details.
-screens => hash_refMandatory, defines the list of valid states, whose class will handle it, and
what the title of the page should be in that state. Usually, there is
identity between a screen and a state, but via the display() parameters,
you can have the same screen object used in two different states, with a
slightly different mode of operation.
The hash reference given here is indexed by state names. The values must
be array references, and their content is the list of arguments to supply
to the screen's creation routine, plus a -class argument defining the
class to use. See "Creation Routine" in CGI::MxScreen::Screen.
Example of hash_ref:
{
'Color' => [-class => 'Color', -title => "Choose Color" ],
'Weekday' => [-class => 'Weekday', -title => "Choose Day" ],
}
The above sequence defines two states, each implemented by its own class.
-timeout => secondsOptional, defines a session timeout, which will be enforced by CGI::MxScreen
when retrieving the session context. It must be smaller than the session
cleaning timout, if sessions are not stored within the browser.
When the session is expired, there is an error message stating so and the user is invited to restart a new session.
-version => stringDefines the script's version. This is your versioning scheme, which has
nothing to do with the one used by CGI::MxScreen.
You should use this to track changes in the screen objects that would make deserialization of previous ones (from an old session) improper. For instance, if you add attributes to your screen objects and depend on them being set up, an old screen will not bear them, and your application will fail in mysterious ways.
By upgrading -version each time such an incompatibility is introduced,
you let CGI::MxScreen trap the error and produce an error message.
internal_error stringImmediately abort current processing and emit the error message string. If a layout is defined, it is honoured during the generation of the error message.
If you buffer STDOUT (which is the case by default), then all the output currently generated will be discarded cleanly. Otherwise, users might have to scroll down to see the error message.
logGives you access to the Log::Agent::Logger logging object. There is
always an object, whether or not you enabled logging, if only to redirect
all the logs to /dev/null. This is the same object used by
CGI::MxScreen to do its hardwired logging.
See Log::Agent::Logger to learn what can be done with such objects.
playThe entry point that dispatches the state machine handling. Upon return, the whole HTML has been generated and sent back to the browser.
The concept of utility path stems from the need to keep all callback
specification serializable. Since Storable cannot handle CODE references,
CGI::MxScreen uses function names. In some cases, we have a default
object to call the method on (e.g. during action callbacks), or one can
specify an object. In some other case, a plain name must be used, and you
must tell CGI::MxScreen in which packages it should look to find that name.
This is analogous to the PATH search done by the shell. Unless you specify an absolute path, the shell looks throughout your defined PATH directories, stopping at the first match.
Here, we're looking through package namespaces. For instance, given the
name "is_num", we could check main::is_num, then Your::Module::is_num,
etc... That's what the utility path is.
The routine CGI::MxScreen::add_utils_path must be used before the
creation of the CGI::MxScreen manager, and takes a list of strings,
which define the package namespaces to look through for field validation
callbacks and patching routines. The reason it must be done before
is that incoming CGI parameters are currently processed during the
manager's creation routine.
During its operation, CGI::MxScreen can emit application logs. The
amount emitted depends on the configuration, as described in
CGI::MxScreen::Config.
Logs are emitted with the session number prefixed, for instance:
(192.168.0.3-29592) t=0.13s usr=0.12s sys=0.01s [screen computation]
The logged session number is the IP address of the remote machine, and the PID of the script when the session started. It remains constant throughout all the session.
There is also some timestamping and process pre-fixing done by the
underlying logging channel. See Log::Agent::Stamping for details.
The so-called "own" date stamping format is used by CGI::MxScreen,
and it looks like this:
01/04/18 12:08:22 script:
showing the date in yy/mm/dd format, and the time in HH::MM::SS format.
The script: part is the process name, here the name of your CGI script.
At the "debug" logging level, you'll get this whole list of logs for every intial script invocation:
[main/0] t=0s u="ram" q="id=4"
using "Mozilla/4.75 [en] (X11; U; Linux 2.4.3-ac4 i686)"
t=0.20s usr=0.17s sys=0.01s [context restore + log init]
t=1.15s usr=0.86s sys=0.05s [parameter init]
t=1.71s usr=0.61s sys=0.07s [outside CGI::MxScreen]
main()
t=0.13s usr=0.12s sys=0.01s [screen computation]
t=46.46s usr=43.42s sys=1.67s ["main" display]
t=0.30s usr=0.29s sys=0.02s [context save]
t=50.01s usr=45.53s sys=1.83s [total time] T=52.45s
The t=0s indicates the start of a new session, and u="ram" signals
that the request is made for an HTTP-authenticated user named ram.
The [main/0] indicates that we're in the state called main, and 0
is the interaction counter (incremented at each roundtrip).
The q="id=4" traces the query string.
The next line traces the user agent, and is only emitted at the start of a new session. May be useful if something goes wrong later on, so that you can suspect the user's browser.
Then follows a bunch of timing lines, each indicating what was timed
in trailing square brackets. The final total summs up all the other lines,
and also provides a precious T=52.45s priece of statistics, measuring the
total wallclock time since the script startup. This helps you evaluate
the overhead of loading the various modules.
The single main() line traces the state information. Here, since this
is the start of a new session, we enter the initial state and there's no
state transition.
Note the very large time spent by the display() routine for that
screen. This is because Carp::Datum was on, and there was a lot of
activity to trace.
Compare this to the following log, where the user pressed a button
called refresh, which simply re-displays the same screen, and where
Carp::Datum was turned off:
[main/1] t=1m11s d=19s u="ram"
t=0.90s usr=0.83s sys=0.08s [context restore + log init]
t=0.01s usr=0.00s sys=0.00s [parameter init]
t=0.02s usr=0.02s sys=0.00s [outside CGI::MxScreen]
main() -> main() on "refresh" pressed
t=0.02s usr=0.01s sys=0.00s [screen computation]
t=0.56s usr=0.58s sys=0.00s ["main" display]
t=0.05s usr=0.05s sys=0.00s [context save]
t=1.56s usr=1.50s sys=0.08s [total time] T=3.24s
The new d=19s item on the first line indicates the elapsed time since
the end of the first invocation of the script, and this new one. It is
the time the user contemplated the screen before pressing a button.
Note that there is no q="id=4" shown: CGI::MxScreen uses POST requests
between its invocations, and does not propagate the initial query string.
It is up to you to save any relevant information into the context.
The following table indicates the logging level used to emit each of the logging lines outlined above:
Level Logging Line Exerpt ------- -------------------------------- warning [main/1] ... info using "Mozilla/4.75... debug ... [context restore + log init] debug ... [parameter init] debug ... [outside CGI::MxScreen] notice main() -> main() on "refresh"... debug ... [screen computation] debug ... ["main" display] debug ... [context save] info ... [total time] T=3.24s
All timing logs but the last one summarizing the total time are made at the debug level. All state transitions (button press, or even bounce exceptions) are logged at the notice level. Invocations are logged at the warning level, in order to trace them more systematically.
There are still some rough edges. Time will certainly help polishing them.
If you find any bug, please contact both authors with the same message.
CGI::MxScreen began when Raphael Manfredi, who knew next to nothing about
CGI programming, stumbled on the wonderful MxScreen program, by
Tom Christiansen, circa 1998. It was a graphical query compiler for his
Magic: The Gathering database. I confess I learned eveything there was to
learn about by studying this program. I owed so much to that MxScreen
script that I decided to keep the name in the module.
However, MxScreen was a single application, very well written, but not
reusable without doing massive cut-and-paste, and rather monolithic.
The first CGI::MxScreen version was written by Raphael Manfredi to
modularize the various concepts in late 1998 and early 1999. It was
never published, and was too procedural.
In late 1999, I introduced my CGI::MxScreen to Christophe Dehaudt.
After studying it for a while, he bought the overall concept, but
proposed to drop the procedural approach and switch to a pure object-oriented
design, to make the framework easier to work with. I agreed.
The current version of CGI::MxScreen is the result of a joint work
between us. Christophe did the initial experimenting with the new ideas,
and Raphael consolidated the work, then wrote the whole documentation
and regression test suite. We discussed the various implementation
decisions together, and although the result is necessarily a compromise,
I (Raphael) believe it is a good compromise.
We managed to use CGI::MxScreen in the industrial development of a
web-based project time tracking system. The source was well over 20000
lines of pure Perl code (comments and blank lines stripped), and we reused
more than 50000 lines of CPAN code. I don't think we would have succeeded
without CGI::MxScreen, and without CPAN.
The public release of CGI::MxScreen was delayed more than a year because
the dependencies of the module needed to be released first, and also
we were lacking CGI::Test which was developped only recently. Without
it, writing the regression test suite of CGI::MxScreen would have been
a real pain, due to its context-sensitive nature. See CGI::Test if
you're curious.
The original authors are Raphael Manfredi <Raphael_Manfredi@pobox.com> and Christophe Dehaudt <Christophe.Dehaudt@teamlog.fr>.
Send bug reports, suggestions, problems or questions to Jason Purdy <Jason@Purdy.INFO>
CGI::MxScreen::Config(3), CGI::MxScreen::Screen(3), CGI::MxScreen::Layout(3).
| CGI-MxScreen documentation | view source | Contained in the CGI-MxScreen distribution. |