|
Refactoring the ASP solution |
|
|
In the previous phase of our application, it was possible for an employee to instanciate
the Toyz class three separate times. Also, another page needs to be written, a
search of employees who have taken a particular course. These improvements will
necessitate the refactoring of our current solution.
|
||||
|
In this last lesson of the ASP model, we will create just one instance of the
Toyz class and use it in all of our SSP pages. Also, the page that lists employees
who have taken a particular course will have to be written and then refactored. You will
see why the refactoring is needed once the first solution has been written.
|
||||
|
1.
If VisualWorks is not already running, please start running it now, load the Web Toolkit
parcel and start a Wave HTTP server. You should also file in the code from
Toyz Inc 5 and make sure the initialize method of the
Filestuff class sets the correct location of the directory variable
(i.e. the directory that contains the 3 data files).
2. This phase of the application will be quite similar to the last phase but with some subtle changes making it more efficient. Perhaps the easiest place to start would be to copy all files used in the last phase (xxxxxxx5.zzz) to "version 6".
4. Our first big change will be in the validate file. Start with validate5.ssp. If a valid user, create a session variable containing an instance of the Employee class. Also create an instance of the Toyz class and store that into a session variable. Redirect user to home6.ssp. All else remains the same. Save the file as validate6.ssp. The file should look like as follows:
<%
5.
At this point, you should be able to sign on. The home page does not make use of the
Toyz object. Our first test of this change would be with one of the other SSP
pages. Start with the file employeecourses5.ssp. The code change is just one
line of code. Change
session timeout: 5. session removeKey: 'signon'. firstName := request anyFormValueAt: 'firstName'. lastName := request anyFormValueAt: 'lastName'. passWord := request anyFormValueAt: 'passWord'. errorMessage := '111'. toyz := Toyz new. employees := toyz getEmployees. employees do: [ :each | (passWord = each number) ifTrue: [ (lastName = each lastName) ifTrue: [ (firstName = each firstName) ifTrue: [ session at: 'signon' put: each. ]. session at: 'toyz' put: toyz. ]. ]. ]. ]. session at: 'signon' ifAbsent: [ response redirectTo: ('login4.ssp?msg=',errorMessage). ]. session at: 'signon' ifPresent: [ :signon | response redirectTo: 'home4.ssp'. ]. %>
toyz := Toyz new.
with
toyz := session at: 'toyz' ifAbsent: [Toyz new].
Although the ifAbsent: method isn't really necessary (a simple at: method would
suffice since it wouldn't reach this line of code if the signon session had timed
out), it does show you that this can be done should the need every arise in your own
applications.
Save this file as employeecourses6.ssp and test it out. 6. Make the same change for allemployeecourses6.ssp and test it out. 7. Now for the page that displays a list of employees who have taken a particular course. This page will seem a little strange at first since it is referred to as a "postback" form page. What this means is that the page will submit itself to itself. The diagram below depicts this graphically.
Figure 1. A form that submits itself to itself The users want a drop-down list of all avialable courses. They will select the course they want, click the submit button and see a list of employees who have taken that course. They intend to do this serveral times (over and over again). Because they want a drop-down list, the page has to be a form since a drop-down list is a form control. And since they want to submit the form over and over again, it does not make sense to take them to a separate page - keep sending them to this same page. This is what's called a postback form - it posts back to itself. |
||||
|
This form (page) will feature some new ideas which are outlined below.
<form action="listbycourse6.ssp" method="post">
The name of this file is listbycourse6.ssp and in order to create a postback form, simply specify the name of the file itself in the action attribute of the form tag.
<select name="cboCourse" size="1">
<option value="---">[ Select a course ] <% courses := toyz getCourses. courses do: [ :each | response write: ('<option value=', each courseNumber, '>', each courseName). ]. %> </select> These lines of code will create a drop-down (selection) list. The first line is a standard HTML tag that informs the browser that a drop-down (selection) list is about to be created. The value of what the user choses goes into the named-pair of cboCourse. The first option tag after the select tag is commonly used for 2 purposes. The first is to give instructions to the user as to what to choose ([ Select a course ]). The second purpose is to initialize this choice with some "nonsense" value (---) so that if this value is extracted when the form is submitted, you can tell that they did not choose any real value while they were on the form.
<%
courseNumber := request anyFormValueAt: 'cboCourse'. ((courseNumber size) > 3) ifTrue: [ courses := toyz getXrefs. courses do: [ :each | (each courseNumber = courseNumber) ifTrue: [ response write: '<tr>'. response write: ('<td>',each employeeNumber,'</td>'). response write: ('<td>',each employeeName,'</td>'). response write: ('<td>',each courseNumber,'</td>'). response write: ('<td>',each courseName,'</td>'). response write: '</tr>'. ]. ]. ]. %> This code block is very powerful. First, it extracts the value of cboCourse from the form. The next line (the test for the value of cboCourse) does two things. First, if the employee comes into the form for the first time, the value of cboCourse will be nil and our test will fail. Second, if the employee fails to choose an option, the value of cboCourse will be "---" (which does not have a size greater than 3) and it will fail as well. Naturally, this line of code was dependent on the fact that all courses have a course number of 3 letters and 3 numbers, a size of 6. |
||||
|
The code for this page will look something like this:
<% title := 'Employees with a certain course'. toyz := session at: 'toyz' ifAbsent: [Toyz new]. %> <!-- #include file= "header6.inc" --> <table width=90% border=0> <tr> <td width=200 align=center> <!-- #include file= "navigation6.inc" --> </td> <td align=left> <form action=listbycourse6.ssp method=post> <table cellpadding=5 border=0> <tr> <td bgcolor=mediumblue colspan=2> <font color=#ffffff><b>Please choose a course</b></font> </td> </tr> <tr> <td width=30% align=right>Course:</td> <td width=70% align=left> <select name=cboCourse size=1> <option value="---">[ Select a course ] <% courses := toyz getCourses. courses do: [ :each | response write: ('<option value=', each courseNumber, '>', each courseName). ]. %> </select> </td> </tr> <tr> <td colspan=2 align=center> <input type=submit name=action value=" Proceed "> </td> </tr> </table> <table border=1> <% courseNumber := request anyFormValueAt: 'cboCourse'. ((courseNumber size) > 3) ifTrue: [ courses := toyz getXrefs. courses do: [ :each | (each courseNumber = courseNumber) ifTrue: [ response write: '<tr>'. response write: ('<td>',each employeeNumber,'</td>'). response write: ('<td>',each employeeName,'</td>'). response write: ('<td>',each courseNumber,'</td>'). response write: ('<td>',each courseName,'</td>'). response write: '</tr>'. ]. ]. ]. %> </table> </td> </tr> </table> <!-- #include file= "footer6.inc" -->
Figure 2. First time through - the drop-down list works perfectly
Figure 3. Results look fine but the drop-down list has doubled
Figure 4. The results have doubled and the drop-down list has tripled |
||||
|
What is going on with these crazy results?Actually, nothing. In fact, the code is working just as it should have and is a verifcation that we are indeed re-using our session object of the Toyz class. In other words, it proves that the same instance of Toyz is being used in our application. Here's why.
Figure 5. The initialize method of the Toyz class When we create an instance of the Toyz class, we create an instance of the FileStuff class.
Figure 6. The initialize method of the FileStuff class When we create an instance of the FileStuff class, we create sorted collections for our instance variables of courses, employees and xrefs.
Figure 7. The getCourses method of the FileStuff class Note that each time we execute this method, we keep adding to the collection. Since the session object of our Toyz class is the same instance of the original object, the list of courses is appended to the list that already exists. This explains why each time you submit the form on this page, the list keeps getting longer and longer. This is also true for the getXrefs method and explains why the list of courses displayed keeps getting longer each time you submit the form. How do we fix this? One fix would be not to use a session variable for our instance of the Toyz class. If we wrote this page for phase 5 or lower, it would work. But the drawback would be that the web server would cause Smalltalk to work very hard by always creating instances of our classes. This is something we wanted to avoid. The desired fix would be to modify our methods.
|
||||
|
8.
File in the code from Toyz Inc 6 and make sure the initialize
method of the Filestuff class sets the correct location of the directory
variable (i.e. the directory that contains the 3 data files). Since all the "refactoring
work" has been done on the custom objects, we can simply retest our pages without any
further modifcation to them.
Figure 8. The drop-down list after several iterations of submitting this form |
||||
|
Congratulations! The ASP model of the TEACH application is complete
Our code (application) is now more efficient since we are able to reuse obects in the web environment just as we would have in a typical Smalltalk GUI application. Although there are some other coding techniques in the ASP model, the major concepts have been covered up to this point. You now should be able to:
|