* Implemented MagicCollectingArray, which acts like MagicPipingArray except that the result is collected into one big array instead of multiple arrayrefs
* Added the relevant getCollecting*List methods to the Controller class
* Added DESTROY methods to the magic array to prevent DESTROY method calls from being propagated
* Made the debug.dumpVars string more robust when the data hash includes one of the three magic characters '(', '.' or ')'
* Implemented sanitation in COSES so that any keys in the data hash containing one of the three magic characters '(', '.' or ')' will have them transliterated to '[', ':' and ']' respectively (sanitation is optional and can be bypassed)
* Changed the API of the generic output module's 'output' method to take the optional $session argument last, thus simplifying the call sites a lot
* Implemented the 'dispatcher.output' service in several classes to return strings that must be support in variants (required for a good UI in the COSES editor)
* Renamed the Login module's strings so that they use the dot-notation separating significant parts
Added an optimisation to Controller.pm so that service names will be hashed once accessed. This should make multiple accesses of the same service a lot quicker. To go with this I added some diagnostics code (on exit) and moved the code around a little bit.
Fixed a minor transgression of the coding style guidelines in Magic*Array.pm. :-)
Neatened up the code in Output.pm.
Made it so Session objects assume they have an 'app' property, and so don't need to be passed $app all the time. (Sessions are objects now.)
Added an unimplemented getAddress() method to the Session class.
* factored out some of the method dispatching code by adding a dispatchMethod() method to the controller;
* turned the Dispatcher class into simply a function on the base Service class and removed Dispatcher.pm;
* made it possible for services to be both services and objects and provide different services depending on which context they were called in (and used this to make the AdminCommands module actually do what it was intended to in the first place, namely, only work for CommandLine access);
* fixed it so if a service is first created by getServiceList the constructed version will actually be cached;
* made output more generic by allowing services to implement arbitrary parts of the output API, used that to make AdminCommands usable without requiring additional code to support it;
* added some documentation;
* added some dump(10) statements to help debugging;
* fixed the string datasource SQL;
* fixed the DBI database so it can handle errors;
* added tableExists API to the DBI database helper.
Thanks to myk, justdave and zach for some ideas.