Cincom

Web Log Stats Lesson 10
Pulling it all Together


| Table of Contents | Lesson 9 | Lesson 11 |


As you become more experienced in writing Smalltalk code, you will be using the System Browser more and more and the Workspace less and less. However, at this point in our evolution of writing Smalltalk code, we are still using the Workspace for the majority of our work then moving it to the System Browser and using it to create new methods. There's nothing wrong with this method - we will do that again in this lesson. The Workspace will become more of a place for testing small snippets of code (to see how they work) before moving them into your methods. But for now, it's a good learning exercise.

In this lesson, we'll take code from the Workspace (which collects page counts from a log file) and move it into new methods of the WebLog class so that you can perform this on not just one log file but for as many as there are in a given directory. Since a lot of groundwork has already been done (categories, classes and protocols), this lesson will much shorter (and hopefully) easier than the first time you did this.

You might even notice some redundancy in your code (i.e. some methods that look like they do almost the same thing). If you're wondering if it's possible to combine them or make them more generic (efficient and re-usable), that's excellent and very perceptive of you. You'll get that chance in the next lesson.

1. Launch the System Browser (from the main VisualWorks Launcher window, click the fourth button on the Toolbar or select the menu option Browse >> System).

2. In the package pane (furthest left) scroll down to the bottom and highlight (select) the WebLogStats package.

If you did not see the WebLogStats package, then you will need to file it in. This lesson requires that the work you did in the previous lessons is loaded into the VisualWorks development environment.

3. Make sure the Instance tab above the protocol pane is selected. Click (select) the private protocol. You should see a window like the one below.


Figure 10-1. Our WebLog Class viewed in the System Browser

4. With the private protocol still selected, replace the text in the method code area with the following:

showPageCounts: aFile
| stream line bag xFound sort out outName|
bag := Bag new.
stream := (logDirectory asFilename construct: aFile) readStream.
[ stream atEnd ] whileFalse: [
line := stream upTo: Character cr.
line := line copyFrom: 50 to: line size.
line := line copyFrom: (line indexOf: $/) to: line size.
line := line copyUpTo: $,.
xFound := line findString: '.asp' startingAt: 1.
xFound > 0
ifTrue:[ bag add: line. ]. ].
stream close.
sort := SortedCollection sortBlock: [:a :b| a >= b].
bag valuesAndCountsDo: [ :each :count |
sort add: (Core.Association key: count value: each)].
outName := (logDirectory asFilename construct: aFile) asString. out := (outName, '.stats') asFilename writeStream.
sort do: [ :each | out cr; nextPutAll: each printString.].
out close.

5. Now <Operate-Click> in the bottom pane and select Accept. The showPageCounts: method will appear in the Method pane.

You should see a window like the one below.


Figure 10-2. Our new method (showPageCounts) viewed in the System Browser

6. With the private protocol still selected, replace the text in the method code area with the following:

getLogFilesForPageCounts

| workingDir contents xFound |
Transcript clear.
workingDir := logDirectory asFilename.
contents := workingDir directoryContents.
contents do: [ :each |
xFound := each findString: filter startingAt: 1.
xFound > 0
ifTrue: [ self showPageCounts: each.].].
Dialog warn: 'Done'.

7. Now <Operate-Click> in the bottom pane and select Accept. The getLogFilesForPageCounts method will appear in the Method pane.

You should see a window like the one below.


Figure 10-3. Our new method (getLogFilesForPageCounts) viewed in the System Browser

8. With the private protocol still selected, replace the text in the method code area with the following:

startPageCount

filter := (Dialog request: 'Please enter a filter ' initialAnswer: 'ws00').
(filter size) > 0
ifTrue: [self getLogFilesForPageCounts]

9. Now <Operate-Click> in the bottom pane and select Accept. The startPageCount method will appear in the Method pane.

You should see a window like the one below.


Figure 10-4. Our new method (startPageCount) viewed in the System Browser

10. Open a workspace and enter the following:

WebLog new startPageCount

Highlight all of this text, <Operate-Click> and select Do it.

The code should now run just like it did in the previous lesson, except that now you will have page count statistics on not just one file but every log file in that directory.

11. Open the File Browser and navigate to the directory where the log files are. You can view them from here.


Figure 10-5. Our page count files listed and viewed from the File List dialog box

It's a fact of life that software never stands still. People always want more enhancements and even functional specifications can change either during or after development. This is no exception.

Someone discovered that in addtion to the ASP pages listed in the log files, a few HTM files were discovered. Guess what? Management wants to count those too. They also didn't care too much for the pages that got just a single hit. In fact, they didn't care too much for any page that didn't make the "top ten" list. So these changes need to be made.

12. Make sure the Instance tab above the protocol pane is selected. Click (select) the private protocol and click (select) the showPageCounts: method. Make the following changes:

showPageCounts: aFile
| stream line bag xFound sort out outName |
bag := Bag new.
stream := (logDirectory, '\', aFile) asFilename readStream.
[ stream atEnd ] whileFalse: [
line := stream upTo: Character cr.
line := line copyFrom: 50 to: line size.
line := line copyFrom: (line indexOf: $/) to: line size.
line := line copyUpTo: $,.
xFound := (line findString: '.asp' startingAt: 1) +
(line findString: '.htm' startingAt: 1).

xFound > 0
ifTrue:[ bag add: line. ]. ].
stream close.
sort := SortedCollection sortBlock: [:a :b| a >= b].
bag valuesAndCountsDo: [ :each :count |
sort add: (Core.Association key: count value: each)].
outName := (logDirectory asFilename construct: aFile) asString.
out := (outName , '.stats') asFilename writeStream.
1 to: 10 do: [ :each | out cr; nextPutAll: (sort at: each) printString.].
out close.

13. Now <Operate-Click> in the bottom pane and select Accept. This will "re-compile" the code you just entered. Barring any typos, if no dialog boxes popped up (i.e. it looked as though nothing happened), then Smalltalk liked the code change you made and compiled it.

14. Open a workspace and enter the following:

WebLog new startPageCount

Highlight all of this text, <Operate-Click> and select Do it.

Note: You probably got an error, a window that looks like Fig 10-6 (see below):


Figure 10-6. The infamous Notifier Window

15. Since we are writing out these statistics files to the same directory as the log files, and we are using the name of the log file as part of our statistics file, our filter (of looking for ws00 files) will now include these .STATS files as well. We don't want to pull these in and this is what is causing the error. We will fix this problem in the next lesson by writing out a file that has a very different name, but for now, we'll solve this problem by just deleting the .STATS files.

The debugger window, even after it has been closed, will "lock" the first .STATS file it encounters. It cannot be deleted unless VisualWorks is totally exited. (There are ways of dealing with this, but that's beyond the scope of this lesson). Therefore, make sure you perform the following steps exactly in this order.

  1. Click the Terminate Button
  2. File out all your changes
  3. Exit VisualWorks
  4. Delete all of the ".STATS" files where your .log files reside
  5. Relaunch VisualWorks
  6. File in your WebLogStats.st file

16. Open a workspace and enter the following:

WebLog new startPageCount

Highlight all of this text, <Operate-Click> and select Do it.

The code should now run just like it did in the previous lesson, except that now you will have page count statistics on not just one file but every log file in that directory. Our "stats" files should now include counts to "HTM" files and they should only have the "top ten" list in them.

17. Open the File List dialog box and navigate to the directory where the log files are. You can view them from here.


Figure 10-7. Our new page count files listed and viewed from the File List dialog box

Those code changes did quite a bit. Let's look at them and make sure you understand what they do.

xFound := (line findString: '.asp' startingAt: 1) +
(line findString: '.htm' startingAt: 1).

This one is really quite simple. If (line findString: '.asp' startingAt: 1) found an ASP page, then (line findString: '.htm' startingAt: 1) should find an HTM page. Since all that this expression returns is a number (the position in our string where "ASP" or "HTM" starts), then adding these two values together will accomplish the same thing. The next line simply tests for a non-zero value.

1 to: 10 do: [ :each | out cr; nextPutAll: (sort at: each) printString.].

This is an example of a fixed count iteration (1 to: 10 do:). The numbers 1 through 10 will be passed (one at a time) to the temporary variable of each and used later on in the code section of the block. Since we have a collection, you can think of each element in the collection as having a number or an index. To reference each element of the collection, use the at: method. The (sort at: each) expression will do the trick.

Summary

Learning how to move working code from a single Workspace into the class library (by creating your own classes and methods) is big step. It challenges your ability to know what's really going on especially when you split it into separate parts and find a way to get them working together again. This, like any other skill, becomes easier with more practice.

Often times, when you start moving code around, you will notice that some methods look very similar and you begin to think that, with a little re-coding and effort, they could be combined into one method. In the next lesson, you will see an example of this and actually combine methods together in order to make your code more efficient and re-usable.

You now should know how to:

How to iterate through a collection a fixed number of times

How to move code from the Workspace into the VisualWorks image

Modify an existing method using the System Browser


| Table of Contents | Lesson 9 | Lesson 11 |