The steps that I am going to take to allow records to be deleted from and added to the defined order of records for a half-inning represent another good illustration of how much functionality can be rapidly added to a viable structure of code. This is not to say that in the development of this system there will not be new wrinkles added, or that modification of the structure will not be a part of that addition, but rather that once major elements of an interface are in place the marginal effort associated with adding functionality declines, largely as a result of the efficiencies resulting from the use of shared code and a shared structure. (Most people associate the word marginal with small, but this is a misconception. The margin is the edge, the place of becoming, the point at which change from one state or condition to another is taking place. If I were to consider, for example, the task of mowing a lawn, the margin is the border between the area already cut and the area yet to be done. If, from that point, the lawn slopes downward, the marginal effort associated with mowing the next little bit has declined, at least until the point at which one turns around and starts coming back uphill <grin>. People tend to associate the word marginal with small because change at the margin is always small by its very nature. In calculus, the marginal rate of change is defined as the rate of change in the smallest possible increment of some dimension, generally time. Even in a very extreme example, that of an object falling off a cliff, the downward velocity of that object at any given instant is only slightly higher than it was at the previous instant, at least until it reaches its terminal velocity (that has more to do with air resistance and is not particularly germane to this example). If, however, it were possible to exert the appropriate amount of force ar the right time, before the object has left the cliff, to negate the marginal rate of change and perhaps flip its sign so the object is going in the other direction, then the object would be prevented from falling. Such is the nature of change.)
My initial step in creating the ability to add and delete records was to implement the capability to somehow identify the records for which the action is desired. The most relevant form element for this type of action is the checkbox. Therefore, I added a table column with the text “select” to the first table row that serves as the table header, then added the following line (bracketed by appropriate formatting statements) to the rec_line() subroutine
$$r->print("<input type='checkbox' name='check'>");
After restarting the server, navigating through the edit process will generate the following table:
Obviously, I must also have some way of directing the server to take the set
of actions that will satisfy the user's desires. The most straightforward
manner
in which to give the user that control is through the use of select buttons, so
within edit_page() I created a borderless table with one row and three columns,
place within it three submit buttons, and locate it beneath that table in which
the records are displayed.
$$r->print("<table width='65%'><tr><td><center><input type='submit' name='submit' value='delete
selected'></center></td><td><center><input type='submit' name='submit' value='insert beneath
selected'></center></td><td><center><input type='submit' name='submit' value='Commit Changes'></center></td>");
Which produces a set of buttons that look like this.
Now this is all well and good, but these three actions are mutually
exclusive, and my general sense is that the buttons themselves would not
adequately direct an inexperienced user. That would not likely be a problem for
very long, but at the same time it is not a big deal to add descriptive
material
to more fully describe the actions represented by the selection of each button.
I even gave some thought to placing these three options as selections on a
radio
widget, which only allows the selection of one item in a group, to reflect the
fact that the selection is mutually-exclusive; but while this would look kind
of
cool it would also require two seperate actions; one to select the processing
mode and a second to click the select button to submit the form. The use of
three select buttons, in each of which is embedded information representing the
mode choice, is in that sense more efficient. Therefore, I decided to better
highlight the characteristics of the selection and dress the buttons up a
little
by making the table in which they are held visible and including a table row
holding more descriptive text than was displayed in the previous version. I
therefore replaced the single line above (broken to better fit onto this page)
with the following four lines (again broken for this page):
$$r->print("<h2>Select Action</h2>");
$$r->print("<table width='75%' border='1' $colors><tr><td><center>Delete Selected Record(s)</center>
</td><td><center>Insert A Record Beneath Selected Record(s)</center></td><td><center>
Commit Changes to Edited Records</center></td>");
$$r->print("<tr><td><center><input type='submit' name='submit' value='delete'></center>
</td><td><center><input type='submit' name='submit' value='insert'>
</center></td><td><center><input type='submit' name='submit' value='Commit Changes'></center></td>");
$$r->print("</tr><table></form>");
which produce something that looks like this
It is more than a little bit ironic that one chapter after having discussed generalizing store(), I have derived a context in which I have to generalize a little bit beyond that. (Actually, we all know that is the way things work, right? Once one begins elaborating in a certain direction, the code that supports that effort is likely to undergo several transformations.) The impetus for what I am about to discuss should be apparent with a little thought. To this point, both contexts in which I had to process pages of multi-valued parameters have been associated with record storage; the generalization of store in the previous chapter allowed store() to handle either of those contexts. Now, however, I have a wider range of contexts in which to process such pages. As always, timtowtdi. I could continue to use store(), or a renamed version thereof, and add unique subroutines to BB_APP_INTERFACE specific to new situations as they arise. In a sense, that would be like dumping everything into one big container; that approach is fine when one is tossing apples and water into a knapsack for an afternoon hike, but on an extended expedition most people prefer keeping things in compartments. I could also process the parameters in a subroutine and handle individual contexts in unique subroutines, but that would be like taking everything out of the knapsack and distributing the items between coat pockets. (Oh, okay, that's a little simplistic. I just like the metaphor, and it does have some merit.) The approach I did adopt was to create a wrapper subroutine that extracts the multi-valued parameters and then dispatches execution in the appropriate manner.
I have called this subroutine page_proc(), which is probably a boring name but is one easily remembered. As before, my first step was to make sure that the application's existing functionality was appropriately implemented in the new structure. At this level, the changes required to implement the new structure are not extensive. Submission of a html form with "store" in the target now leads to the execution of page_proc(), as in the following snippet from the league() subroutine of BB_STACKED.
if ($path=~/store/) {
&page_proc($r,$num_forms,$user_name,$update_record);
}
At this point, the version of page_proc() I used was very simple
sub page_proc {
my ($forms,$num_forms,$user_name,$update_record)=@_;
##manually construct a hash of arrays for the parameter values, accessible in the same manner as CGI.pm's parameters
##this takes two elements at a time from the array returned from the args method, and pushes the second element into an array
##referenced by the value held in the first element
my @args=$$forms->args;
my %params;
while (my($name,$value) = splice @args,0,2) {
push @{$params{$name}}, $value;
}
$params=\%params;
store($params,$forms,$num_forms,$user_name,$update_record);
}
All that I am really doing here is processing the parameters, storing a
reference to the resultant hash of arrays in the scalar $params, and calling
store(). Within store(), any statements that access that structure must be
changed to dereference that reference. For example, the line
@ec=@{$params{'ec'}};
used to populate an array of even code values in previous versions of store()
must now be changed to
@ec=@{$$params{'ec'}};
the second $ sign serving to point to the item held in the reference.
Obviously, this is not a big change, but as I have mentioned before it is the
type of thing I like to get out of the way before I start to address new
issues. Once these changes have been made, the storage routines associated
with data entry and editing function appropriately.
At this point, I was ready to jump into implementing the new functionality. I started with the delete functionality, simply because it was a more straightforward implementation that the addition of records. Once I started implementing the structure and wrote the returned request object to a file for inspection, however, I realized that the checkbox widget has a limitation that virtually precludes its use in this context: i.e., the checkbox widget returns a parameter only when it is selected. Given the structure of my hash of parameter arrays, there would therefore be no way to associate a parameter value resulting from the selection of a record with any given record. As an illustration, assume that a page of fifteen records is submitted with two checked as selected. The parameter-processing code reads a series of two-element chunks from the array returned from the args() method and pushes the second element into an array referenced in a hash keyed to the first element. As the text and select boxes that have previously made up the screens that submit multi-valued parameters always submit values, even if empty, for the element displayed, the elements occupying the same position in each of the relevant arrays belong to the same record. In this example, however, the first value in the parameter array associated with the checkboxes might be associated with the fifth record on the page, and the second with the twelfth. The only ways around this that I can see would involve significantly cluttering up the parameter-processing code. I could, for example, create some sort of structure with the names of parameters holding the elements of an individual record, and use that as a template to apply to sets of parameters as they are read in and placed in the %params hash of arrays. Something like that would be able to tell me when there was not an element present, which would then allow me to store an appropriate value in the array for that element. This is really clunky, I would have to want to use checkboxes quite a bit to both clutter up the parameter-processing code and slow down the application as that logic is executed.
As I went forward, my initial inclination was to avoid the use of radio button widgets because of a limitation created by the nature of the widget. The radio button widget creates a mutually-exclusive selection between buttons of the same name, and as a result I could not simply drop a line printing radio buttons in rec_line() as I do with other line elements. That would produce fifteen lines, each with two radio buttons, and only one from the total of thirty could be selected. While I could see a workaround involving the use of a loop counter to dynamically generate the names for the radio button widgets on each line, thus creating fifteen widgets of two elements each, my initial inclination was to view this as an unnecessary complication.
I regarded that as unnecessary because a straightforward solution was available that allowed the current parameter processing to be used while delivering the functionality I desire; I simply represented the selection as a simple yes/no select box with the following line
$$r->print("<select name='check'> <option>yes</option><option selected>no</option></select");
which generates a table that looks like this
I wrote the above on a Friday, and over the following weekend found myself musing about these matters. While the page above produces a set of parameters that can be processed in a straightforward fashion, there is little visual differentiation between the displayed records and the select widget, and there is also nothing but the words displayed to signal the selected item. While I could set a special font color to print the "Yes" option, for example, with more thought I decided that I also wanted a different kind of action to be associated with the selection of a record for deletion or as a location for record insertion. While a checkbox widget would be ideal from the standpoint of the action involved, I really did not want to implement something like what I described above, messing with the way the parameters are processed. Development of the selection as a series of radio button widgets, however, provides a very similar selection mechanism that is in some ways preferable to a checkbox in that both possible states are explicitly represented in the selection mechanism. Perhaps most importantly, use of radio buttons will not require modification of the manner in which parameters are processed. Therefore, I decided to redo the page using radio widgets. (Probably saw that one coming, huh?)
The most challenging aspect of this modification, from the standpoint of page generation, is defining and generating a consistent but unique widget name for the radio buttons displayed on each line that displays a record.
while (@rec=$ed_recs->fetchrow_array) {
$check="check_".$elements;
$$r->print("<tr>");
rec_line($r,$participants,$result,$events,$role,$check,@rec);
.
$elements++; }
The name itself is generated within the loop that iterates over the result set
holding records to be editied. As the $elements scalar already serves as a
loop counter, I use the $check scalar simply to hold the concantenation of the
string "check_" with the value held in $elements. The result is then passed as
an argument to the rec_line() subroutine. (Recall that in the absence of a
function prototype perl treats passed arguments as one undifferentiated array.
Those elements are only pulled into a defined set of data structures by the
manner in which the argument array is read in the subroutine. As a result, a
set of arguments that includes an array must pass the array as the last item in
the list. Otherwise, the first array in the receiving list will slurp in all
the remaining elements, and the items following that array will have null
entries. I tripped over this one for a bit as I began making this set of
modifications.) Within rec_line(), the radio widget is output with the
following line
$$r->print("Yes<input type='radio' name=$check value='yes'>No<input type='radio' name=$check value='no' checked>");
The generated page, below the header menus, now looks like this
Submission of the page by pressing the delete button will ultimately lead to
the execution of the page_proc() subroutine, as discussed earlier in this
chapter. The few lines of code currently included within the block executed in
this context implement a different manner of traversing the elements of the
arrays stored in the %params hash than in the previous contexts in which I
simply stepped through the relevant arrays.
In this circumstance the values that determine whether the specific action
should be executed are stored in a series of single element arrays, as
illustrated in the graphic at right. The names of the keys that point to the
arrays that flag selection can be constructed in exactly the same fashion as I
used when I constructed them in the first place within edit_page(). Further,
the integers tacked on to the end of the string correspond to the order in
which records were written to the edit page, transmitted in the request object,
and read into the %params hash. As a result this is also the order in which
elements were stored in the arrays held in the %params hash. (To restate, if I
have not previously done so, arrays are relatively simple data structures.
Unlike hases, which must be sorted in some manner if slices are to be accessed
in any particular order, array elements are going to remain in the order in
which they were stored unless otherwise acted upon.) As a result, remembering
that arrays indexes, like the counter used to assign values in the loops, begin
with 0, the value of the check_1 parameter in the image pertains to the second
element of the value arrays, "035" for part_id and "05" for r_code in that
simplified example.
Once I have constructed this environment, I determine whether the "yes" option on any given radio widget has been checked by incrementing a counter from 0 to the value of $elements that was printed as a hidden field by edit_page().
if ($sub_button=~/delete/) {
my $i;
for ($i=0;$i <= ${$$params{'elements'}}[0];$i++) {
if (${$$params{'check_'.$i}}[0] eq 'yes') {
$del_recs->execute(${$$params{'key'}}[$i]);
}
The rationale for using $elements (here accessed as a value in the appropriate
location in the %params hash) in this fashion lies in trimming unnecessary
steps from the execution of the loop when there are fewer than fifteen records
in the result set. I could do the same thing by ending the loop the first time
there was no parameter array named with the value generated within the loop,
but my inclination at the time I wrote this was that it was more
straightforward. In this bare bones version of the delete function, the key
value that represents the condition for the $del_recs statement handle is
itself accessed from its location in the hash of parameter arrays.
In this implementation, the loop is included in a multi-argument for() statement. At base, all I am doing here is counting from 0 up to and including the value in the first (and only) spot in the parameter array keyed to "elements". For each of these integers, I am checking to see if the parameter with a name of "check_" concantenated with that integer has the value "yes". If it does, I execute the statement held in the $del_recs statement handle with the element of the key array that is in the ith position (the specific integer for which the loop is cuurently executing) as the argument. The $del_recs statement handle refers to an sql statement now added to the set of statements that are prepared in the main section of BB_STACKED:
my $del_recs=$dbh->prepare("delete from participant_game where key=?");
This is, of course, a standard sql delete statement, making use of each
record's unique key as does the update statement used in the previous
chapter. Note that the parameter values are accessed as elements in arrays, no
matter how strange the statement may look, and as such the appropriate index
value for that element much be specified even when that item is the only
element in the list, as in the cases of the elements value and the "check_$i"
value. As an aside, I found the practice of writing external files with
debugging information, including the request object (via the as_string method)
and the values referenced by these rather elaborate expressions, to be very
worthwhile. It is easy to misplace a dollar sign or forget to specify the
specific array element when typing complex expressions such as these for the
first time, and leaving tracks for oneself as subroutines are brought into play
eases the debugging process immensely.
With this in place, flagged records are indeed deleted as I confirmed by browsing the participant_game table in pgaccess, but I wanted the capability to provide feedback to the user similar to the feedback the interface provides after record values are modified. I therefore created an environment similar to that I used there, with a counter scalar tracking loop executions and an accumulator scalar holding a count of successful statement executions. A comparison of the two scalars will provide the capability to generate that feedback, ultimately to be displayed on an intermediate page, but first I am going to work through inserting blank records into the sequence.
As I implied previously, there are two possible contexts in which a suffix will be applied. The first is what I would anticipate to be the standard insert context in which the record preceeding is one of the original records, with a four character long inning order value. The second context, of course, is the one in which the record that will precede the record to be inserted already has a letter in the final position. In the first circumstance, I will simply append a capital "A" to the value for the preceding inning order value to create the inning order value for the new record. In the second circumstance, I will increment the ascii value of the current character in the fifth position and use that value to construct the new inning_order value. In both cases, the next step will be to insert the set of values that represent a skeleton record into the participant_game table.
As I said earlier, the manner in which this is implemented shares the same general structure as the delete function, but this function incorporates more logic within that structure.
if ($sub_button=~/insert/) {
my ($i,$j,$k,$sum)=0;;
for ($i=0;$i <= ${$$params{'elements'}}[0];$i++) {
if (${$$params{'check_'.$i}}[0] eq 'yes') {
my @key_array=split(/::/,${$$params{'key'}}[$i]);
$i_o=$key_array[4];
my ($new_i_o,$new_key);
my $len=length($i_o);
if ($len==4) {
$new_i_o=$i_o."A";
}
elsif (length($i_o) > 4) {
my $suffix=substr($i_o,-1);
$suffix=chr(ord($suffix)+1);
$new_i_o=substr($i_o,0,4).$suffix;
}
$new_key=$key_array[0]."::".$key_array[1]."::".$key_array[2]."::".$key_array[3]."::".$new_i_o;
$k=$ins_blank->execute($key_array[0],$new_key,$key_array[2],$key_array[3],$new_i_o);
$j++;
$sum=$sum+$k;
}
This level of similarity, of course, has led me to consider incorporating the
two processes into a common structure and conditionally executing the code
unique to each of the two processes. Using the rationale I used in the last
chapter when addressing a similar consideration in the store() subroutine, I
have resolved for the present time to maintain seperate sections within the
page_proc() subroutine for these conditions. This may change, if a
sufficiently compelling reason emerges. As with the delete function, the logic
to implement the insertion of records is wrapped in a for() loop that executes
for each of the integers in the number of rows displayed on the submitted page.
For each of these integers, the value of the parameter with a name comprised
of a concantenation of the string "check_" with that integer is checked for the
value "yes". If that is the case, the code specific to the insert process is
executed.
Manipulation of inning_order values is, of course, a major element of what I do in this chunk of code. Those values are not, however, printed back to the browser during generation of the edit page, and as a result cannot be returned to the browser as part of the request object. Luckily, however, that value is part of the key value, so I can extract it from that key. (For that matter, all of the information included in what I have come to call a skeleton record is in that key, which is not all that surprising given that it is that same set of information that represents a unique record. Recall that I did discuss the inherent duplication this involves in the chapter on database design, and at that time decided to accept the relatively modest increase in storage requirements as a trade-off for increased flexibility and speed in building and executing queries.) In the first line of the code executed if the subject line was selected, I split the ith element of the array referenced by the term "key" into its component parts. The inning order for that record is the fifth element of that array, and I assign it to the local scalar $i_o. I then initialize two new scalars, $new_i_o and $new_key, which will hold the constructed inning_order and key values for the new record. I also store the length of the $i_o scalar into the $len scalar, using that value to determine the manner in which the $new_i_o scalar is constructed. If that value is equal to four, $new_i_o is assigned the concantenation of $i_o and "A". If that value is greater than four, the last character in the string stored in $i_o is extracted and stored in the $suffix scalar. Given these conditions, that value must be a previously-appended suffix. Therefore, in the next line the value in $suffix is converted to its ascii value, incremented, and then converted back to its character representation. For example, an uppercase "A" would be converted to its ascii value, 65, incremented to 66, and converted back to the character representation, which is now "B". The $new_i_o scalar is then created by concantenating the first four characters of the $i_o scalar with the new value in $suffix. I have made no effort to trap a context in which the suffix has been incremented out of the range of the capital letters. This is based on two considerations: first, while this range extended only to ascii value 92, other ascii values do extend up to 254, so application functionality will not be threatened. The appropriate order of the records would simply not be easily discernable by looking at the inning order value. Second, this is an extremely unlikely event if the system is well-managed. If the records of a given half-inning, a reasonable average number of which would be in the range of 50 to 100, were to be so mangled that even 20 had to inserted into the order, a far more efficacious manner of addressing that problem would be to simply delete all of that half-inning's records and re-enter them. Ultimately, this is the kind of application domain understanding that should, to the extent possible, be built into the interface. I would not be surprised to see this kind of consideration pop up in a future chapter.<grin>
Now that I have the new inning order value I use that to construct a new value for the key column, and execute the statement referenced by the $ins_blank statement handle with those values and the elements of @key_array that represent the game_id, inning, and half_inning values. As before, the $ins_blank statement handle is initialized in the main section of BB_STACKED.
my $ins_blank=$dbh->prepare("insert into participant_game (game_id,key,inning,half_inning,inning_order) values (?,?,?,?,?)");
The set of columns updated here creates a participant_game row with blank even
characteristics. Re-entering the edit process and choosing the same set of
characteristics for the data to be entered will now generate a set of rows that
look like the sample page above, with blank records in the location previously
specified.
At this point I have the requisite functionality to accomplish the editing functions, but I have to put a few more pieces into place to make the interface usable. First, I want to generate a common intermediate page for any of the actions enabled by the page generated by edit_page(). Given that, it will simply be a matter of making sure that the appropriate information is returned to the browser to allow the client to supply the server with the information appropriate to the action desired.
Incorporation of a common intermediate page is not a quantum leap in any sense, requiring only that the functionality already built into the deletion and modification processes be centralized and extended to be able to reflect what is going on as part of the insert process. A short review of these processes reveals their similarity. The insert and delete operations are very similar, differing primarily in that they execute different statement handles and in the operations required to construct a new skeleton record in the insert operation. Further, while the record modification process follows a slightly different model in processing parameters, the manner in which it counts executions of the $update_record statement handle is the same as that employed in the other two processes. Therefore, all that was required to integrate the manner in which the three provide feedback to the user was to adapt the names of the scalars used in the record modification process to the same convention used in the insert and delete processes, and make sure that the scalars defined in the page_proc() scope are appropriate populated in store(). (As an aside, it would be easy enough to adapt the record modification scheme to the general manner in which parameters are handled in the other two processes. Such a modification would also improve the response of that process, as implicit in that adaptation would be the requirement that users mark as selected those records for which modification should be applied, and only selected records would be processed. If only two records were marked as selected, for example, the full update process would only execute twice, as opposed to the fifteen executions required under the current mode of execution. I have not adopted that mode for applying record modifications simply because it would require additional actions on the part of the user, given that I do not yet have anything running on the client that is capable of independently flagging the record as changed. A general rule of interface design is that, within reason, one should never shift the load from the computer to the user. At the system's current level of response, it is preferable to simply tell the server to put every record in the retrieved record set back into the table, even if it has not been changed, rather than expecting the user to explicitly flag each such record. This would, however, be a prime candidate for change should experience suggest that the process represents a heavier load than currently anticipated.)
Returning to the point at hand, given these modest modifications I can now generate a common intermediate page as in the following snippet
$$forms->print("<html><form action='/bb_app/league/la-game-edit'>");
$$forms-<print("<center><h2>$sum records processed</h2><br>");
$$forms->print("<font color='bright red'>Not all updates applied ... contact system administrator</font><br>") if ($sum < $j);
$$forms->print("<table width='75%' border='1' $colors><tr><td><center>");
$$forms->print("<input type='submit' name='action' value='Re-Display Modified Set'>");
$$forms->print('</center></td><td><center>');
$$forms->print("<input type='submit' name='action' value='Next Set From Same Half-Inning '>");
$$forms->print('</center></td<td><center>');
$$forms->print("<input type='submit' name='action' value='Select Another game or half-inning to Edit'></center></td></tr></table></center>");
$$forms->print("<input type='hidden' name='s_inning' value=$s_inning>");
$$forms->print("<input type='hidden' name='game_id' value=$game_id>");
$$forms->print("<input type='hidden' name='inn_order' value=$inn_order>");
$$forms->print("<input type='hidden' name='s_half_inning' value=$s_half>");
$$forms->print("</form></html>");
This is, of course, much like what I have used previously, with the generic
term "processed" replacing more explicit descriptors. At some point I may add
the conditional statements more specificity would require, but at this point I
would regard such as unwarranted. The resultant page looks like this:
The next step is to make sure that the information required to implement the functionality described on the select buttons be available to the server at the appropriate time. Most of what is required is already there; looping from one set to the next is of course something that I set up in the previous chapter. I have, however, added an option that is especially of pertinence to the insert and delete options: the ability to re-display the set just manipulated. As it is possible that records were added or deleted during that manipulation, it would be more accurate to say "Display the Set of Records Starting with the Same Starting Point as the Previous Set", but that gets a bit wordy.<grin>.
As I have all of the information required to retrieve the next set of records, the only additional bit of information that I require is the starting inning_order used at the time edit_page() generated the previous page. This is not a difficult task, requiring only that I capture that initial value and print it into a hidden field. What subtltey does come into play here comes as the request object is read for the appropriate value to be applied as edit_page() retrieves the result set and generates the page. In some ways this sounds like doubletalk, but in this section of edit_page() I must both 1) read in a previous inning_order value as appropriate and 2) write the appropriate values to hidden fields to support the next invocation. I think the best perspective to maintain here is on the current generation of the page, rather than getting confused over the specific context in which the subroutine is being called. Given that, the subroutine must be able to discern the context in which it is being called, and from that establish the appropriate inning_order value to generate the page and be passed along as a hidden field.
In operation, this sequence assumes that the page is being generated by an initial invocation of the edit process, assigning a string of zeros to the $inn_order scalar and in so doing specifying the first record of the inning as a starting point.
my $inn_order='0000';
##if the action parameter contains the string "Next", assign the value from the s_inn_order parameter to $inn_order
if ($$params{'action'}=~/Next/) {
$inn_order=$$params{'inn_order'};
}
##otherwise, if the action parameter contains the string "Re-Disp"(lay), assign the value from the s_inn_order parameter to $inn_order
elsif ($$params{'action'}=~/Re-Disp/) {
$inn_order=$$params{'s_inn_order'} ;
}
##assign the prevailing inning order value to the $s_innn_order scalar
my $s_inn_order=$inn_order;
If, however, the request object contaings a parameter named "action", the name
of the set of submit buttons generated on the intermediate page, holding either
the string "Next" or the string "Re-Disp" (part of the string "Re-Display"),
that default value is replaced. In the first instance the replacement is with
the value of the parameter "inn_order", the value of the last record processed
during the previous page generation, and in the second the value of the
"s_inn_order" parameter is used, the inning order of the first record processed
during the previous page generation. As the value of $inn_order at this point
will be the value determining the records selected by the execution of the
statement held in the $ed_recs statement handle on the current pass through
edit page generation, I store that value to the scalar $s_inn_order. Once I
have iterated over all of the rows in the result set, the $inn_order scalar
will hold the inning order value for the last record processed, and the
$s_inn_order scalar the value that established the initial lower boundary
condition. When these values are embedded in the generated page as hidden
elements, and in turn read and printed by the intermediate page, they are
available to the next execution of edit_page(), in which they can be used in
the manner described above. It is worth noting that in the generation of the
intermediate page I do not bother to read those values into scalars, instead
simply accessing the %params hash for the value to be printed to the hidden
fields.
$$forms->print("<input type='hidden' name='s_inn_order' value=${$$params{'s_inn_order'}}[0]>");
$$forms->print("<input type='hidden' name='inn_order' value=${$$params{'inn_order'}}[0]>");
I think that the primary down side here is that such references are relatively
difficult to decipher. I think that one can spend all day de-referencing
complex perl data structures and still misread expressions such as those in the
lines above. In this context, however, these lines are not likely to require
much in the way of maintenance, and as this implementation saves a few
execution cycles I regard this as a worthwhile formulation.
So there it is. I can now move through the records for a given half-inning; adding, deleting, and modifying records as I wish. Some might find the use of an intermediate page a bit clunky, especially as record insertion requires both a page generation to identify the insertion point and a second generation to modify the blank values held in the newly-created records. I ultimately intend to implement an interface that embodies the same functionality on a single page (that might be the point at which I implement edit mode selection as a radio widget), but as I said earlier the incorporation of more functionality onto one page is itself a double-edged sword, requiring more interpretation by the user. Regardless, there are some other cool features that I could incorporate into the development of such an interface, so rest assured that I will be returning to the subject. In the next chapter, however, I am going to work through creating a coherent structure to house the mass of data that I am passing around.
Hey, this is me ... speaking from about a week in the future <grin>. Realized that there is a bug in this version, but I am not going to reveal its location, or how to fix it, until the later part of the next chapter. Now you can just wienie out and keep reading, or you can use this as an exercise in debugging and see if you can track down the problem and create a solution. And remember, there is more than one way to do it....