Wednesday, August 21, 2024

ScriptFormatter main()


Putting in place all the regression testing I wanted took more time than I would have liked. There were a number of reasons for this.
  • Actually isolating the intermediate form and writing it to a file took some time;
  • Reading this back in and decoding it to text, along with making sure everything was correct took a while;
  • Finding all the places where I had used ScriptFormatter, adding them to a regression script and then getting them all to work again (ScriptFormatter has "drifted" over the years, and with no automated testing :-));
  • Of particular issue was the "presentation" module, which (understandably, if wrongly) worked completely differently and didn't split into a "front" and "back" end.
The last one required me to do considerably more surgery than I was comfortable with on a piece of code that I'm trying not to adjust. But there you go. The price you pay.

But once I had done all that, I had a regression suite I was happy with, and that seemed to work quite well.

Replacing main()

My first goal was to try and pull main() into the shape I wanted. I suspected it would not be too hard, and it wasn't. It was basically in the right shape already, just with too many bits and pieces distributed in the wrong places for my liking. While I was about it, I moved "everything else" somewhere else. The idea of course, is to be able to make all of "everything else" be completely testable, and for main() to be the only thing that deals in "real" classes. It's worth pointing out that I haven't done that yet, but it gives that vague appearance. I will come back to that, although I haven't quite decided what gets shared here and what doesn't.

So, for now, main() looks like this:
package com.gmmapowell.script;


import com.gmmapowell.geofs.Universe;
import com.gmmapowell.geofs.lfs.LocalFileSystem;
import com.gmmapowell.geofs.simple.SimpleUniverse;
import com.gmmapowell.script.config.Config;
import com.gmmapowell.script.config.ConfigArgs;
import com.gmmapowell.script.intf.FilesToProcess;


public class Main {
        public static void main(String[] args) {
                Universe uv = new SimpleUniverse();
                LocalFileSystem lfs = new LocalFileSystem(uv);
                try {
                        Config cfg = ConfigArgs.processConfig(lfs, args).read();
                        FilesToProcess files = cfg.updateIndex();
                        cfg.generate(files);
                        cfg.show();
                        cfg.upload();
                } catch (Throwable t) {
                        ExceptionHandler.handleAllExceptions(t);
                }
        }
}
Yes, I've included the whole thing (imports and all) to show that there isn't any trickery going on. I did keep two other (exception-related) classes in the same package, but that's it. Yes, the ExceptionHandler class is static, but even so, it can be unit-tested if you are that way inclined (I'm not, at the moment, but the goal here is testability, not test coverage).

The eagle-eyed among you will notice that even now this does not quite reflect the outline I presented above. Partly because the names are wrong, but mainly because the steps themselves are wrong. But, remember, this is just the first step of a much bigger task: as that task unfolds, I will come back to this and make subtle adjustments.

The key thing to note is that everything already goes through a single object - the configuration cfg - and this is already an interface. The static class ConfigArgs is responsible for creating an instance of the ConfigReader interface and present it to us, at which point we call the exposed read() method. This separation allows us to test the ReadConfig class directly without worrying about the pesky details of file systems and the like.

Moving On

Having got this into shape, I can now tackle the thing I most want to tackle - reading the configuration file and breaking this up into "modules" and then introducing a class to handle modular configuration and dispatch. I'm hoping that this will turn out to be just what I need when I come to processing the modules within my text files. Oh, and the thing that started me down the path: when I want to add nested modules within the modules.

I think this is the design I had in mind when I started this project, although after four years it's hard to be sure. But it certainly seems that a lot of the code falls that way. So let's get started and start refactoring the code that reads the configuration.

No comments:

Post a Comment