Guide for developers
Soo... you want to help hack on Gjallar?
- Start with getting a clone of the Mercurial repository.
- Download and unzip the latest Developer image into the "dist" directory.
- Then read Readme.txt and get your image running before continuing to read.
All classes in Gjallar has the prefix "Q2". This was the temporary internal project name until we chose Gjallar, and we are keeping it at least a little longer. :) All class categories are called "Q2-xxx" so the Q2 Monticello package contains all Q2 code. There is also a Q2-Morphic package but that is just a graphical addon tool and not essential.
We are using Squeak3.8.1 with the latest published Win32 VM, both are included (for Win32) in the repository under directory "base". The Gjallar system is meant to be portable so we are not relying on components that are only available for Win32.
The current list of "third party" Squeak packages used in Gjallar are:
- Seaside (slightly patched, intend to feed back)
- ODBC for Squeak
- InstallerBuilder (slightly patched, Gjallar specifics)
- INIFile (slightly patched, intend to feed back)
In the beginning of development we also used Kilauea and Mewa, but both those components have been replaced with rewrites that we feel are simpler, even though specific for Gjallar.
The current list of addon tools that we are using in the dev image are:
And the list of non Squeak components used currently are:
- Swish-e (very nice free text indexing engine)
- wget (simple command line URL downloader, used for fast download of a potentially large file when making an offline installation)
- unzip + zip (used for fast decompression of a potentially large file when making an offline installation)
- Graphviz (used to autogenerate pictures of the workflow graph)
- NSIS Installer (very good free Win32 installer builder)
This is the top "root" of the domain model. There is only one instance and it is the top object that we store in Magma - everything reachable from this instance is the contents of the Magma database.
This is the component that is registered as the Gjallar application in Seaside in the class side initialize method. There we also set the session class to use for Gjallar - Q2Session. Q2Main has two child components; the left side panel and the right side content. It asks the left side panel about its selection (in #updateRoot:) to know what to show as the content. We haven't yet used WAApplication etc - someone who knows how this should be done better - feel free to fix, including Seaside configuration stuff.
This is the left side table of contents component and it acts as the "top" component in Gjallar, even though compositionally Q2Main is the logical top. As the "top" it is used a bit for inter component communication. There are two methods of special interest; loggedInNavigation and loggedOutNavigation. These methods build the menu for the user which is an instance of Q2Navigation. This class is a bit clever, it can take blocks as arguments like:
add: [Q2UserView parent: self] label: 'Settings' description: 'Personal settings for using the system'
...and thus create the components lazily when the user clicks on it. This shortens the login time since the components - while fast to create typically needs to look up objects from Magma, so they can take a bit of time to create all at once. And we don't read unneeded objects this way. The description shows up as tooltip btw.
As you can see these methods also check stuff like #isAdmin etc and then gives more entries in the toc. At the end you see:
initialRequest ifNotNil: [self handleInitialRequest]
...which is the hook into dealing with a RESTish URL containing the case number.
There are a few base classes used in Gjallar for Seaside components. Q2Session is the Gjallar specific session class. Basically it couples a Magma session with the Seaside session, but it also keeps track of the current logged in user object, the top domain model object and a few other things. We also have Q2WithoutSeasideSession, the session class used when working with the system outside of Seaside - like in Q2Services, see Q2Scheduler below or when being called from SOAP. The third session class is Q2MockSession which is used when simulating Magma in memory for running unit tests much faster.
Q2Canvas is the specific subclass of WARenderCanvas for Gjallar. Q2Canvas mainly adds some convenience methods for rendering fields, labels, tooltips, buttons etc.
Most Gjallar Seaside components keep track of their parent using an extra instvar and they are typically instantiated by the parent using code like "Q2Blabla parent: self" - this is inherited from Q2ChildComponent which also collects useful behaviour that can be called from many different places in the Seaside part of Gjallar.
Q2ChildComponent currently has three subclasses; Q2Component, Q2FormComponent and Q2FieldComponent.
Q2Component corresponds to a "functional part" of Gjallar that can be selected using the left side navigation panel. It has a titel, a label and a description (showed as tooltip).
Q2FormComponent is the Seaside component that can render a Q2Form with Q2Fields in it. Q2FieldComponent is the component for a single Q2Field. These classes constitute the "Meta" mini framework for dynamically constructing, viewing and editing forms with fields. It was created based on experiences of using Mewa - which was the precursor of Magritte.
Q2Log is a simple front end used throughout Gjallar for code level logging. By default it logs to Transcript and to "Gjallar.log" on disk in parallell and is thread safe using a Monitor. Do NOT print to Transcript directly since Transcript is not thread safe by default, use Q2Log instead.
Q2Win32InstallerBuilder is the developer "script book". On the class side we have a few important image maintenance messages:
...and when the installer is built we must follow by:
SmalltalkImage current openSourceFiles
This is because the installer needs to be able to copy the changes file, so it must temporarily be closed. The above is only used when we prepare an installer of Gjallar to be used for offline installations or installation on windows servers.
Three commonly used do-its during development are:
This will delete, create, initialize and load sample data into the Magma database. There are also a few similar methods building other sample databases.
This should close down all Processes and clear all registries etc - it is like a "reset" for the whole image. If it complains about instances hanging you probably have a debugger or inspector you should close. Otherwise you will just need to use "PointerFinder on: Q2Model allInstances last" to figure out why they do not go away. :)
The methods startServer and startClient calls cleanImage before they start the services that should be running in a server image and an offline client installation image. A client offline installation only runs Seaside. The server additionally runs a SOAP server service, a HttpView based Q2MirrorServer that offers prepared mirror zip files for download and a Q2Scheduler that runs regular background services on the server, much like cron in a unix system.
So clearing and getting a server up for development is done using:
Q2Gjallar recreateSimpleDatabase; startServer
Then you should be able to login to: http://localhost:8081/seaside/Q2
The above step takes a while to run, but it ensures all class shape changes etc are dealt with properly since Magma is totally recreated.
Before saving the image you can execute cleanImage which makes the image much smaller to save (all sessions cleared etc).
Q2Scheduler is a Process that wakes up every 10-20 seconds and checks if any of the active Q2Services is due for another run. If so each such service is forked in a background process running #do. This way long running services do not prevent other services from being run. Currently all subclasses of Q2Service are automatically being activated when the scheduler is started, so adding a service is done by simply adding a subclass to Q2Service.
Forms and Fields
The mini framework in Gjallar for working with forms has no name. It is inspired by Mewa but follows a different style of coding and has different objectives. Q2Form and Q2Field (with subclasses) are meant to be persisted in Magma. A Q2Form is simply the domain model of a form that you can fill out on the screen. It has an ordered collection of fields, which are instances from subclasses of Q2Field.
Q2Form and Q2Field are only meant to be rendered in Seaside, which is different from Mewa/Magritte. Mewa used lots of blocks for pluggability which made the code slightly hard to deal with, Q2Form probably is more direct. Mewa used a visitor approach for rendering in order to decouple the actual rendering of the fields - since the "fields" in Mewa were meant to be meta descriptions that could drive different kinds of user interfaces. Q2Form only deals with Seaside, so the rendering code has deliberately been kept as render-methods on Q2Field/Q2Form.
But these two objects represent the definition of the form and its fields, not the actual runtime representation of the Seaside components. Q2Field does not inherit from WAComponent etc. So there are corresponding Seaside component classes - Q2FormComponent and Q2FieldComponent. But these classes do not mirror the larger hierarchy of Q2Field - instead they use the Q2Field instance to render themselves, either as a viewer or an editor by sending renderViewOn:component: or renderEditOn:component:. They send self as the component so that the Q2Field instance has access to all runtime state of the field - like its value etc.
The net effect is that we keep the rendering code in the Q2Field hierarchy, which is where we keep all configuration of the fields. But Q2Field and Q2Form are still easily persistable. Sure, we need to send the Q2FieldComponent along as an argument etc, but so far it has been working out pretty good. And it is very nice to only have a single hieararchy of "widgets".