Show Me the Design!
Design Overview
The
design has several key
goals:
- Organize code into simple logical pieces.
- Eliminate PRIVATE variables in our pages.
- Improve Error Handling
- Make Server Side Session Variables easy to
use.
- Browse large query results.
- Allow Multiple web sites to run using the same
code.
Organize
Code into Simple Logical Pieces
Our previous Web-Connect projects have turned out well, but our code
organization was not ideal.
The first projects that we did had huge prg files - a real kitchen sink approach -
with all the code typically in one or two prg files.
Our next projects split each web hit into; data, presentation, and business prg files. This led to a huge number of prg files.
For this project, I used the page as our organizing principle.
The code for each page is in its own prg and is based on the rbPage class.
Each page has only a few methods, and all the supporting functions are grouped
together with the code that is using them.
A typical rbPage class will have:
- Page() : Show the page to the
user.
- Submit() : User submits page to web server.
- OtherFunctions() : Supporting functions - used by Page()
or Submit()
Goldilocks would find this approach "just right":
- Not too many functions
- Not too many prg files.
Eliminate PRIVATE Variables in Our Pages
Web Connect projects usually send the page back to the user with Response.ExpandTemplate().
ExpandTemplate() merges together FoxPro variables with an HTML template.
The HTML template refers to FoxPro variables by embedding the variable
with delimiters like this:
<%=pcUserGroupHeader%>
ExpandTemplate() does the following:
- Parses the HTML template
- Finds all delimited variables
- Evaluates the variables
- Plugs the evaluation result back into the HTML template
- Sends the result to the user.
ExpandTemplate() does not and cannot "know" how many FoxPro variables you
want to insert into an HTML page,
so sending them in as parameters to ExpandTemplate() can't work. The
typical way to code around this is to declare
your FoxPro variables as PRIVATE so they will "drop" into ExpandTemplate().
A few PRIVATE variables might be ok when used sparingly.
However, we strongly discourage PRIVATE variables everywhere else in our code,
so why allow them here?
Code with PRIVATE variables would fail code review in our company and be sent
back for a re-write.
Variables parachuting in from parts unknown just make for messy code that could blow
up when you try to change it.
Instead of PRIVATE variables, we just add properties to our page class like
this:
DEFINE
CLASS HOME
AS rbPage
UserGroupHeader =
[]
UserGroupFooter =
[]
Since
One Class = One Page, we can refer to THIS in our
HTML template like this:
<%=This.UserGroupHeader%>
Improve Error Handling
Web Connect does a pretty good job with error
handling.
There are a couple of things that I would like to see done better.
1. Stop the avalanche of error messages caused by errors in your code.
If your code gets an error, it sometimes generates an avalanche of error
messages.
The first error hits, gives an error message... and then VFP tries to execute
the next line of code.
Why? Oh my gosh, WHY?
After the first error, the remaining lines of code will almost certainly fail.
Worse than that, since it is executing each line of code, it can take a long time,
and tie up your server resources.
Code that should run in .05 seconds can take 10 to 15 seconds - an
eternity in web server time.
So, when we get an error we should just report
the first error, and go home.
2. Don't use old fashioned ON ERROR error
trapping for merge errors in your HTML.
As we evaluate each merge variable in our HTML template, we have
to deal with evaluation errors.
A variable could be misspelled, out of scope, etc., so we must deal with it.
Microsoft has improved TextMerge in VFP 7, but any
error during the merge still kills the whole page.
Not a good result.
It is better to evaluate as many merge variables in the template as possible, so
that minor errors don't stop your site.
Web Connect's ExpandTemplate() handles merge
errors by setting an ON ERROR handler before the merge, and then resetting it after the
merge.
It works well, but under some circumstances, the ON ERROR is not re-set.
Uh Oh!
How to fix both problems:
Write our own version of ExpandTemplate() that:
1. Uses FoxPro's RETURN TO so that we bail out after the first error.
2. Uses the Error() method of a class to trap errors. This
eliminates the need to use a global ON ERROR trap.
See rbPage::Merge() and rbPage::MergeToString()
Make Server Side Session Variables easy to
use
Web programming is a little strange.
Each web hit is independent.
You don't know what pages the user has been to, what they have done, or even if
the user is ever going to come back.
For each hit the browser:
- Connects
- Sends it's request to the web server
- Gets your response
- Disconnects
Web programming is often called "Stateless."
Remember, your code is executing on the web server, not on the user's machine.
Worse than that, all user code is executing in
the same space on the server.
Same data session.
Same tables open.
Same variables in scope.
A variable in scope for one user is in scope for all users.
Cookies are a partial answer.
Cookies are a set of up to 20 variables that you can store on the user's machine
from the web server.
t
Each