Creating a Multiple Choice Quiz System, Part 2

Designing our CGI quiz to be more robust and to include error checking.
Creating the Quiz File

So far, we have dealt with ways in which the QuizQuestions object can handle syntax errors within the quiz file. But many syntax errors are created simply by mistake or by users unfamiliar with the defined file format.

One solution is to provide users with tools for creating quiz files with fewer errors. Given the amount of time we spend writing CGI programs and HTML forms, it makes sense to create a short program that takes the contents of an HTML form and saves it to disk.

An example of one such form is shown in Listing 4. Upon submission, the form's contents are handed to, which then creates a properly formatted quiz file.

In order to implement this feature, we need to add two new methods to QuizQuestions. One, addQuestion, takes a six-element list and adds it to questions, the instance variable containing fields from the quiz file. The second method, saveFile, does the opposite of loadFile, taking the current questions and saving them.

Here is one possible implementation of addQuestion:

sub addQuestion
    # Get ourselves
    my $self = shift;
    # Get our arguments
    my ($question, $a1, $a2, $a3, $a4,
                    $correct) = @_;
    # Turn our arguments into a string
    my $new_question = join("      ", @_);
    # Get our instance variable
    my @questions = @{$self->{"questions"}};
    # Add the new question
    push (@questions, $new_question);
    # Reset the instance variable
    $self->{"questions"} = \@questions;
    # Return successfully (= 0)
    return 0;

This version of addQuestion is fairly simple, if not very robust. For instance, it doesn't check to make sure the correct answer is one of A, B, C or D. But it does let us add new questions to the QuizQuestions object. Notice that addQuestion both retrieves and sets values for the instance variable questions.

If we were interested in extending our quiz on Emacs, we could use addQuestion in the following way:

my $error = $questions->loadFile;
&log_and_die($error) if $error;
"What term describes the cursor's current location?",
  "mark", "point", "cursor", "mouse", "B");

Immediately after executing this code, $questions contains one more question. However, this question is lost upon the program's exit, because we have not yet saved the new question to the quiz file. In order to save the questions to a quiz file, define saveFile like this:

sub saveFile
    # Get ourselves
    my $self = shift;
    # Open the questions file for writing
    open (QUESTIONS, ">$questionDir" .
                    $self->{"quizname"}) ||
        return "Could not open " .
                    $self->{"quizname"} . " for writing";
    # Loop through the questions
    my @questions = @{$self->{"questions"}};
    my $question;
    for each $question (@questions)
        print QUESTIONS $question, "\n";
    return 0;
This code iterates through the questions, and writes them to the quiz file. Since we are writing all of the questions to disk rather than appending them, we use the > when opening the file, thereby overwriting any data that existed previously.

Since saveFile saves only the contents of the questions instance variable, it effectively obliterates comments and white space in the file. Of course, anyone creating the quiz file using a program is unlikely to look at the comments. Nonetheless, a more refined version of saveFile and the QuizQuestions object might let users add comments and white space to the file, as well as questions. (Obviously, the HTML form would also have to allow for this.)

Our version of saveFile uses the same system for reporting errors as loadFile--by returning a string, while the lack of an error is indicated by returning 0. This lets us use the following code:

$error = $questions->saveFile;
&log_and_die($error) if $error;

Now that you have seen the skeleton for, you should have a good understanding of the program shown in Listing 5. This version of is fairly straightforward. It checks to see if the user entered a question; if there is text for a question, it takes the remaining parameters from the HTML form submitted.

Now is a good time to remember that CGI programs that write user-defined strings to your file system are potentially dangerous, and thus must be placed in locations that are restricted to authorized users, either by using your HTTP server's built-in protection or by placing such programs behind a firewall. No matter how unlikely this may seem, a user may eventually discover that you have a program named, and create quizzes on your system, possibly overwriting your creations.

This month, we made our quiz engine friendlier for non-programmers by checking the integrity of the quiz file and by allowing users to create quiz files using HTML forms. What happens when users want to edit quiz files? For now, they are stuck modifying the file on disk, which again opens Pandora's box of potential syntax problems. While we can discover these problems with our simple error-checking code, it might be a good idea to create a program that can edit quiz files as well as create them. Next month, we will modify to do just that, making our quiz system easier for everyone to handle.

Reuven M. Lerner is an Internet and Web consultant living in Haifa, Israel, who has been using the Web since early 1993. In his spare time, he cooks, reads and volunteers with educational projects in his community. He can be reached via e-mail at