Back: Context unwinding
Up: Features
Forward: Blox
 
Top: GNU Smalltalk User's Guide
Contents: Table of Contents
Index: Class index
About: About this document

3.7 Packages

Thanks to Andreas Klimas' insight, GNU Smalltalk now includes a powerful packaging system which allows one to file in components (goodies in Smalltalk's very folkloristic terminology) without caring of whether they need other goodies to be loaded.

The packaging system is implemented by a Smalltalk class, PackageLoader, which looks for information about packages in the file named (guess what) `packages', in the current image directory. There are two ways to load something using the packaging system. The first way is to use the PackageLoader's fileInPackage: and fileInPackages: methods. For example:
 
    PackageLoader fileInPackages: #('Blox' 'Browser').
    PackageLoader fileInPackage: 'Compiler'.

The second way is to use the `Load.st' file which lies in the GST image directory. For example, start GNU Smalltalk with this command line:

gst -qK Load.st -a Browser Blox Compiler(5)

and GST will automatically file in:

  • BloxTK, needed by Blox
  • Blox, loaded first because Browser needs it
  • Tokenizer, not specified on the command line, but needed by Parser
  • Parser, also not specified, but needed by Browser and Compiler
  • Browser
  • Compiler (Blox is skipped because it has already been loaded)

Then it will save the Smalltalk image, and finally exit!

To provide support for this system, you have to give away with your GST goodies a small file (say you call it `mypkg') which looks like this:
 
# This is the comment for the package file for
# the absolutely WONDERFUL package MyPackage

MyPackage
    MyPrereq1 MyPrereq2 C:MyCallout1 C:MyCallout2
    MyFilein1.st MyFilein2.st libmymodule
    ../yourDirectoryName      # absolute or relative to kernel path

then people who install your package will only have to do
 
    gst-package mypkg

which is a small shell script which will execute these two commands
 
    cat packages mypkg > packages
    gst -qK Load.st -a MyPackage

Simple, isn't it? For examples of package declarations, have a look at the `packages' file as distributed with GNU Smalltalk.

The rest of this chapter discusses the packages provided with GNU Smalltalk.

3.7.1 Blox  
3.7.2 The Smalltalk-in-Smalltalk compiler  
3.7.3 Dynamic loading through the DLD package  
3.7.4 Internationalization and localization support  
3.7.5 The SUnit testing package  
3.7.6 TCP, WebServer, NetworkSupport  
3.7.7 An XML parser and object model for GNU Smalltalk  
3.7.8 Minor packages  


3.7.1 Blox

Blox is a GUI building block tool kit. It is an abstraction on top of the a platform's native GUI toolkit that is common across all platforms. Writing to the Blox interface means your GUI based application will be portable to any platform where Blox is supported.

Blox is a wrapper around other toolkits, which constitutes the required portability layer; currently the only one supported is Tcl/Tk but alternative versions of Blox, for example based on Gtk+ and GNOME, will be considered. Instead of having to rewrite widgets and support for each platform, Blox simply asks the other toolkit to do so (currently, it hands valid Tcl code to a standard Tcl 8.0 environment); the abstraction from the operating system being used is then extracted out of GNU Smalltalk.

Together with the toolkit, the `blox' directory contains a browsing system that will allow the programmer to view the source code for existing classes, to modify existing classes and methods, to get detailed information about the classes and methods, and to evaluate code within the browser. In addition, some simple debugging tools are provided. An Inspector window allows the programmer to graphically inspect and modify the representation of an object and, based on Steve Byrne's original Blox-based browser, a walkback inspector was designed which will display a backtrace when the program encounters an error. Finally, the Transcript global object is redirected to print to the transcript window instead of printing to stdout.

This browser evolved from a Motif-based version developed around 1993 written by Brad Diller (bdiller@docent.com). Because of legal concerns about possible copyright infringement because his initial implementation used parts of ParcPlace's Model-View-Controller (MVC) message interface, he and Richard Stallman devised a new window update scheme which is more flexible and powerful than MVC's dependency mechanism, and allowed him to purge all the MVC elements from the implementation.

Four years later I--Paolo Bonzini--further improved the code to employ a better class design (for example, Brad used Dictionaries for classes still to be fleshed out) and be aesthetically more appealing (taking advantage of the new parser and Blox text widget, I added syntax highlighting to the code browsers).

To start the browser you can simply type:

 
	gst -qK blox/Run.st

This will load any requested packages, then, if all goes well, a worksheet window with a menu named Smalltalk will appear in the top-left corner of the screen. You might want to file-in `blox/Run.st' from your `.stinit' file (see section Startup sequence) or to run it automatically through ObjectMemory (see section 3.1 Memory accessing methods).


3.7.2 The Smalltalk-in-Smalltalk compiler

The Smalltalk-in-Smalltalk compiler is a nice compiler for Smalltalk code which is written in Smalltalk itself. Ideally, the C compiler would only serve to bootstrap the system, then a fully working Smalltalk compiler would start compiling methods.

The current status of the Smalltalk-in-Smalltalk compiler can be summarized thus: it does work, but it does not work well. This for many reasons: first of all it is slow (10-15 times slower than the C compiler), and it does not produce very optimized code. Anyway it has very few bugs (it does have some), it is a good example of programming the GNU Smalltalk system, and its source code (found in the `compiler' directory) provides good insights into the Smalltalk virtual machine: so, after all, it is not that bad. If want to give it a try, just file in the Compiler package.

The compiler is built on a recursive descent parser which creates parse nodes in the form of instances of subclasses of STParseNode. Then the parser instantiates a compiler object which creates the actual method; more information on the inner workings of the compiler can be found in the comment for the STCompiler class.

The parser's extreme flexibility can be exploited in three ways, all of which are demonstrated by source code available in the distributions:

  • First, actions are not hard-coded in the parser itself: the parser creates a parse tree, then hands it to methods in STParser that can be overridden in different STParser subclasses. This is done by the compiler itself, in which a subclass of STParser (class STFileInParser) hands the parse trees to the STCompiler class.

  • Second, an implementation of the "visitor" pattern is provided to help in dealing with parse trees created along the way; this approach is demonstrated by the Smalltalk code pretty-printer in class STFormatter.

  • Third, just like all recursive descent parsers, it is pretty easy to figure out which part of the stream a method takes care of parsing, and you can override the parsing methods in a subclass to do "something interesting" even while a parse tree is created. This is demonstrated by the syntax highlighting engine included with the browser, implemented by the STPluggableParser and BCode classes.


3.7.3 Dynamic loading through the DLD package

DLD is the Dynamic LoaDer package. This is a peculiar package in that it is always loaded if your system supports it; currently supported architectures include dlopen (used in Linux, BSD, Solaris and many more systems), Win32, HPUX, GNU DLD (Linux ELF), AIX and GNU libtool (which includes a portable dlopen for a lot of systems).

The DLD package enhances the C callout mechanism to automatically look for unresolved functions in a series of program-specified libraries. To add a library to the list, evaluate code like the following:
 
     DLD addLibrary: '/usr/lib/libc.a'

You will then be able to use #defineCFunc:... (see section 4.2 Using the C callout mechanism) to define all the functions in the C run-time library. Note that this is a potential security problem (especially if your program is SUID root under Unix), so you might want to disable DLD when using GNU Smalltalk as an extension language. To disable DLD, configure GNU Smalltalk passing the --without-dld switch.

Note that a DLD class will be present even if DLD is disabled (either because your system is not supported, or by the --without-dld configure switch) but any attempt to perform dynamic linking will result in an error.


3.7.4 Internationalization and localization support

Different countries and cultures have varying conventions for how to communicate. These conventions range from very simple ones, such as the format for representing dates and times, to very complex ones, such as the language spoken. Provided the programs are written to obey the choice of conventions, they will follow the conventions preferred by the user. GNU Smalltalk provides the I18N package to ease you in doing so.

Internationalizing software means programming it to be able to adapt to the user's favorite conventions. These conventions can get pretty complex; for example, the user might specify the locale `espana-castellano' for most purposes, but specify the locale `usa-english' for currency formatting: this might make sense if the user is a Spanish-speaking American, working in Spanish, but representing monetary amounts in US dollars. You can see that this system is simple but, at the same time, very complete. This manual, however, is not the right place for a thorough discussion of how an user would set up his system for these conventions; for more information, refer to your operating system's manual or to the GNU C library's manual.

GNU Smalltalk inherits from ISO C the concept of a locale, that is, a collection of conventions, one convention for each purpose, and maps each of these purposes to a Smalltalk class defined by the I18N package, and these classes form a small hierarchy with class Locale as its roots:

  • LcNumeric formats numbers; LcMonetary and LcMonetaryISO format currency amounts.

  • LcTime formats dates and times.

  • LcMessages translates your program's output. Of course, the package can't automatically translate your program's output messages into other languages; the only way you can support output in the user's favorite language is to translate these messages by hand. The package does, though, provide methods to easily handle translations into multiple languages.

Basic usage of the I18N package involves a single selector, the question mark (?), which is a rarely used yet valid character for a Smalltalk binary message. The meaning of the question mark selector is "Hey, how do you say ... under your convention?". You can send ? to either a specific instance of a subclass of Locale, or to the class itself; in this case, rules for the default locale (which is specified via environment variables) apply. You might say, for example, LcTime ? Date today or, for example, germanMonetaryLocale ? account balance. This syntax can be at first confusing, but turns out to be convenient because of its consistency and overall simplicity.

Here is how ? works for different classes:

Method: LcTime ? aString
Format a date, a time or a timestamp (DateTime object).

Method: LcNumber ? aString
Format a number.

Method: LcMonetary ? aString
Format a monetary value together with its currency symbol.

Method: LcMonetaryISO ? aString
Format a monetary value together with its ISO currency symbol.

Method: LcMessages ? aString
Answer an LcMessagesDomain that retrieves translations from the specified file.

Method: LcMessagesDomain ? aString
Retrieve the translation of the given string.(6)

The package provides much more functionality, including more advanced formatting options support for Unicode, and conversion to and from several character sets (including ISO-8859, KOI-8, and East-Asian double-byte character sets). For more information, refer to the sources and to the class reference.

As an aside, the representation of locales that the package uses is exactly the same as the C library, which has many advantages: the burden of mantaining locale data is removed from GNU Smalltalk's mantainers; the need of having two copies of the same data is removed from GNU Smalltalk's users; and finally, uniformity of the conventions assumed by different internationalized programs is guaranteed to the end user.

In addition, the representation of translated strings is the standard MO file format adopted by the GNU gettext library.


3.7.5 The SUnit testing package

SUnit is a framework to write and perform test cases in Smalltalk, originarily written by the father of Extreme Programming(7), Kent Beck. SUnit allows one to write the tests and check results in Smalltalk; while this approach has the disadvantage that testers need to be able to write simple Smalltalk programs, the resulting tests are very stable.

What follows is a description of the philosophy of SUnit and a description of its usage, excerpted from Kent Beck's paper in which he describes SUnit.


3.7.5.1 Where should you start?

Testing is one of those impossible tasks. You'd like to be absolutely complete, so you can be sure the software will work. On the other hand, the number of possible states of your program is so large that you can't possibly test all combinations.

If you start with a vague idea of what you'll be testing, you'll never get started. Far better to start with a single configuration whose behavior is predictable. As you get more experience with your software, you will be able to add to the list of configurations.

Such a configuration is called a fixture. Two example fixtures for testing Floats can be 1.0 and 2.0; two fixtures for testing Arrays can be #() and #(1 2 3).

By choosing a fixture you are saying what you will and won't test for. A complete set of tests for a community of objects will have many fixtures, each of which will be tested many ways.

To design a test fixture you have to

  • Subclass TestCase
  • Add an instance variable for each known object in the fixture
  • Override setUp to initialize the variables


3.7.5.2 How do you represent a single unit of testing?

You can predict the results of sending a message to a fixture. You need to represent such a predictable situation somehow. The simplest way to represent this is interactively. You open an Inspector on your fixture and you start sending it messages. There are two drawbacks to this method. First, you keep sending messages to the same fixture. If a test happens to mess that object up, all subsequent tests will fail, even though the code may be correct.

More importantly, though, you can't easily communicate interactive tests to others. If you give someone else your objects, the only way they have of testing them is to have you come and inspect them.

By representing each predictable situation as an object, each with its own fixture, no two tests will ever interfere. Also, you can easily give tests to others to run. Represent a predictable reaction of a fixture as a method. Add a method to TestCase subclass, and stimulate the fixture in the method.


3.7.5.3 How do you test for expected results?

If you're testing interactively, you check for expected results directly, by printing and inspecting your objects. Since tests are in their own objects, you need a way to programmatically look for problems. One way to accomplish this is to use the standard error handling mechanism (#error:) with testing logic to signal errors:

 
2 + 3 = 5 ifFalse: [self error: 'Wrong answer']

When you're testing, you'd like to distinguish between errors you are checking for, like getting six as the sum of two and three, and errors you didn't anticipate, like subscripts being out of bounds or messages not being understood.

There's not a lot you can do about unanticipated errors (if you did something about them, they wouldn't be unanticipated any more, would they?) When a catastrophic error occurs, the framework stops running the test case, records the error, and runs the next test case. Since each test case has its own fixture, the error in the previous case will not affect the next.

The testing framework makes checking for expected values simple by providing a method, #should:, that takes a Block as an argument. If the Block evaluates to true, everything is fine. Otherwise, the test case stops running, the failure is recorded, and the next test case runs.

So, you have to turn checks into a Block evaluating to a Boolean, and send the Block as the parameter to #should:.

In the example, after stimulating the fixture by adding an object to an empty Set, we want to check and make sure it's in there:

 
SetTestCasee>>#testAdd
    empty add: 5.
    self should: [empty includes: 5]

There is a variant on TestCase>>#should:. TestCase>>#shouldnt: causes the test case to fail if the Block argument evaluates to true. It is there so you don't have to use (...) not.

Once you have a test case this far, you can run it. Create an instance of your TestCase subclass, giving it the selector of the testing method. Send run to the resulting object:

 
(SetTestCase selector: #testAdd) run

If it runs to completion, the test worked. If you get a walkback, something went wrong.


3.7.5.4 How do you collect and run many different test cases?

As soon as you have two test cases running, you'll want to run them both one after the other without having to execute two do it's. You could just string together a bunch of expressions to create and run test cases. However, when you then wanted to run "this bunch of cases and that bunch of cases" you'd be stuck.

The testing framework provides an object to represent a bunch of tests, TestSuite. A TestSuite runs a collection of test cases and reports their results all at once. Taking advantage of polymorphism, TestSuites can also contain other TestSuites, so you can put Joe's tests and Tammy's tests together by creating a higher level suite. Combine test cases into a test suite.

 
(TestSuite named: 'Money')
    add: (MoneyTestCase selector: #testAdd);
    add: (MoneyTestCase selector: #testSubtract);
    run

The result of sending #run to a TestSuite is a TestResult object. It records all the test cases that caused failures or errors, and the time at which the suite was run.

All of these objects are suitable for being stored in the image and retrieved. You can easily store a suite, then bring it in and run it, comparing results with previous runs.


3.7.6 TCP, WebServer, NetworkSupport

GNU Smalltalk includes an almost complete abstraction of the TCP, UDP and IP protocols. Although based on the standard BSD sockets, this library provides facilities such as buffering and time-out checking which a C programmer usually has to implement manually.

The distribution includes a few tests (mostly loopback tests that demonstrate both client and server connection), which are class methods in Socket. This code should guide you in the process of creating and using both server and client sockets; after creation, sockets behave practically the same as standard Smalltalk streams, so you should not have particular problems.

In addition, package WebServer implements a servlet-based web serving framework engine, including support for file servers as well as Wiki-style servers(8); each server is a subclass of Servlet, and different servers can live together under different paths. See the class side examples protocol of WebServer to get it up and running quick.

The server is based on the GPL'ed WikiWorks project. For up to date/more info go see http://wiki.cs.uiuc.edu/VisualWorks/WikiWorks>. Many thanks go to the various people who had worked on the version on which the server is based:

 
Joseph Bacanskas joeb@mutual.navigant.com
Travis Griggs tgriggs@keyww.com
Ralph Johnson johnson@cs.uiuc.edu
Eliot Miranda eliot@objectshare.com
Ken Treis ktreis@keyww.com
John Brant brant@cs.uiuc.edu
Joe Whitesell whitesell@physsoft.com

Apart from porting to GNU Smalltalk, a number of changes were made to the code, including refactoring of classes, better aesthetics, authentication support, and HTTP 1.1 compliance.

There is also code implementing the most popular Internet protocols: FTP, HTTP, NNTP, SMTP, POP3 and IMAP. These classes are derived from multiple public domain and open-source packages available for other Smalltalk dialect and ported to GNU Smalltalk.


3.7.7 An XML parser and object model for GNU Smalltalk

The XML parser library for Smalltalk, loaded as package VWXML (XML is still reserved for the InDelv parser provided with old versions of GNU Smalltalk) includes a validating XML parser and Document Object Model. In future versions the InDelv parser will be removed and this library will be loaded when the PackageLoader is asked for the XML package. Unluckily, the libraries are incompatible because they come from two completely different sources.

There were many reasons to upgrade to the VisualWorks library. First of all, it is rapidly becoming a standard in the Smalltalk world; it looks more like Smalltalk than the InDelv parser, which is written in a minimal Smalltalk subset so that its source can be automatically converted to Java; it is a validating parser and in general more modern (for example it supports XML namespaces); and finally, an XSL interpreter based on it is available as open-source and will be ported to GNU Smalltalk soon.

The parser's classes are loaded in their own namespace, named XML.

Documentation for the parser is not available yet. To have some clue, look at the class-side protocol for XML XMLParser and at the `printing' protocol for XML Node and its subclasses.


3.7.8 Minor packages

Various other "minor" packages are provided, typically as examples of writing modules for GNU Smalltalk (see section 4.1 Linking your libraries to the virtual machine). These are Regex, providing Perl5 regular expressions, GDBM, which is an interface to the GNU database manager, and MD5, which provides a simple class to quickly compute cryptographically strong hash values.




This document was generated on May, 12 2002 using texi2html