Second Summer 2009


Keeping state with CGI (initial thoughts).
Today we will start by discussing this program (written in Python):
#!/usr/bin/python

name = raw_input("What's your name? ")
age = raw_input("Well, hello " + name + "! How old are you? ")
try:
  output = name +", next year you will be " + str(int(age) + 1) + " years old!"
except:
  output = "Wow, something wrong happened! Did you provide a valid age?"
print output
Based on yesterday's demonstration we want to see what it takes to make this program available over the web.

Here's a series of steps that builds the program in Perl/CGI.

1. Same program in Perl:

#!/usr/bin/perl

print "What is your name? \nType here: ";
$name = <STDIN>;
chop($name);
print "Well $name how old are you?\nType your age here: ";
$age = <STDIN>;
print "OK $name, next year you will be ", ($age + 1), " years old.\n";
2. Next we try to implement this last program as a web script.

We have the following stages:

a) first we create an HTML form (nine.html) in htdocs:

<html>
  <head><title>Age Calculation Form</title></head>
  <body>

    <form action="http://silo.cs.indiana.edu:44xxx/cgi-bin/nine">
      <table>
        <tr> <td> Name: <td> <input type="text" name="who">
        <tr> <td> Age:  <td> <input type="text" name="age">
      </table>
      Push <input type="submit" value="Proceed"> to move on.
    </form>


  </body>
</html>
b) then we implement this script (~/apache/cgi-bin/nine):
#!/usr/bin/perl

use CGI;

$q = new CGI;

print $q->header, $q->start_html;

print $q->Dump;

print $q->end_html;
We discussed:

Our next script (version of nine) looks like this:

#!/usr/bin/perl

use CGI;

$q = new CGI;

print $q->header, $q->start_html;

$name = $q->param('who');
$age = $q->param('age');

print "Well $name you are going to be ", ($age + 1), " next year.";

print $q->end_html;
We make two comments:

So we merge the interface with the script as follows:

#!/usr/bin/perl

use CGI;

$q = new CGI;

print $q->header, $q->start_html;

$name = $q->param('who');
$age = $q->param('age');

if ($name) {
  print "Well $name you are going to be ", ($age + 1), " next year.";
} else {
  print qq{
    <form action="http://silo.cs.indiana.edu:44xxx/cgi-bin/nine">
      <table>
        <tr> <td> Name: <td> <input type=text name=who>
        <tr> <td> Age:  <td> <input type=text name=age>
      </table>
      Press <input type=submit value=Proceed> to move on.
    </form>
  };
}
print $q->end_html;
The action attribute is no longer needed.

When it's missing the browser sends the data to the address the form came from.

#!/usr/bin/perl
use CGI;
$q = new CGI;
print $q->header, $q->start_html;

$name = $q->param('who');
$age  = $q->param('age');

if ($name) {
  $age = $age + 1;
  print qq{Well $name you will be $age next year.};
} else { # no data, print the form
  print qq{
<form>
  Name: <input type="text" name="who"> <br>
  Age: <input type="text" name="age" size=4> <br>
  Press <input type="submit" value="Proceed"> to move on.
</form>
  };
}

print $q->end_html;
So now the host name, the port number and program name are all implicit.

This was great but it wasn't able to collect the data in stages, the way our third Perl script did.

So we developed the following program:

#!/usr/bin/perl

use CGI; 
$q = new CGI; 

print $q->header, $q->start_html; 

$name = $q->param('who'); 
$age  = $q->param('age');

if ($name) {
  if ($age) {
    $age = $age + 1;
    print qq{ Well $name it looks like you will be $age next year.};
  } else {
    print qq{
      <form>
        Well $name <input type=hidden name=who value=$name> what is your age:
        <input type="text" name="age">
        <input type="submit" value="Send it!">
      </form>
    };
  }
} else {
  print qq{
    <form>
      Name: <input type="text" name="who"> <input type="submit" value="Send it!">
    </form>
  };
}

print $q->end_html;
This was a big accomplishment for us, if we realized it or not.

For the first time we managed to keep state.

In the basic layout:

if ($name) {
  if ($age) {
    # final report (age is input, name is remembered, as state) 
  } else {
    # form to ask for age (prints name in question)
    # (age is collected as input here, name turns into state)
  }
} else {
  # form to ask for name 
  # (name is collected as input here)
}
... we identified the following:

  1. when the name is missing we ask for it, and read it
  2. initially name is input: we just learn it, and use it to ask for age
  3. name must turn into state when we collect age as input
  4. age is processed and reported as input, name is remembered as state in final report

Recall how the form changed gradually.

1. First we had a form focused on getting the input:

<form>
  Well $name what is your age:
  <input type="text" name="age">
  <input type="submit" value="Send it!">
</form>
2. Second we had a form that was able to send the name back to us:
<form>
  Well <input type=text name=who value=$name> what is your age:
  <input type="text" name="age">
  <input type="submit" value="Send it!">
</form>
Basically at this stage we had the problem solved.

But we wanted to eliminate the possibility that the user may retype her/his name.

So we learned about hidden fields, and turned the text field into a hidden field.

3. This stage stores the name as hidden state, but leaves the user in the fog:

<form>
  Well <input type=hidden name=who value=$name> what is your age:
  <input type="text" name="age">
  <input type="submit" value="Send it!">
</form>
4. So we made one small change and obtained the final version:
<form>
  Well $name <input type=hidden name=who value=$name> what is your age:
  <input type="text" name="age">
  <input type="submit" value="Send it!">
</form>
As an exercise we will ask to use information in this tutorial to implement the same in Python.

We emphasized this structure:

In lab we will apply this structure to implement a series of simple examples.


Updated by Adrian German for A202/A598