Refactoring to introduce Tabs in The Console
That’s why I need to create so-called ConsoleContexts dynamically on tab view creation. I keep in there a few things like error stream, console input (prompt) interface or console output interface. Since scripts and commands are launched synchronously inside one tab, there’s only one object for temporary arguments that are passed into executed script.
Keep ‘em separated
Tabs are separated. They work in their own “sandboxes”. Every command or a script that is launched in some tab should have printed it’s output to the same tab. And here’s the thing. Notice a simple situation:
- you run a script that downloads currency data from some website
- it takes some time
- you don’t bother about finishing and just close tab that owns this script
- script finished downloading data, parses it and wants to print it into output
- oops, the tab doesn’t exist anymore!
Software shouldn’t crash. What’s correct catch for this situation? Print output to nowhere? I decided to print it to Default tab. Default tab is a first tab by… default - but it’s been thought to be changable.
“Chicken or the egg” problem often relies to a situation of confusion what is/was/should come first. Remember paragraph about contexts? I bring them back!
First note that I keep logic code and view code somehow indepedently. Dependencies that need to exist are mostly referenced through interfaces (in the meaning of language construct, not the abstraction). And here’s the problem - should I create the view or the logic first?
To create a tab I need:
- instantiate a tab that contains WebView (output) and TextField (input prompt)
- create a Context for this tab
- inject Context into tab
Sounds simple, doesn’t it? Now imagine this - every software often logs information or errors, e.g. information about initialization. To show a real example, I have a ScriptManager that loads scripts on software initialization where some logs need to be presented into the console output.
So, before an initialization of “logic” I need to initialize the “view” first. Of course I can’t do that because I need user settings first. And if settings loading outputs some errors (or non-error info logs) I want to present it in the console output.
My logic/view abstraction wasn’t really done well since one thing needs other and the other needs the first one first. Chicken or the egg? I ended up with creating a initialization context that collects logs until WebView (text output control) initializes. When view initialization is done I simply push the entries to the real output.
Probably, to make a better abstraction would need introducing more code that handles situations of asynchronousness. I decided to KISS here.
After all, I have to agree with Edsger Dijkstra who used to say
Two or more, use a for.
Every time I see a pattern that could be multiplicated in code or runtime I experience it’s often worth thinking forward about writing a code that’s ready for a multiplication - in code (DRY rule) or in runtime, as here for my tabs.
My code wasn’t really prepared for it, because in previous iteration of The Console I didn’t really think about having tabs. Refactoring to introduce those made me feel pain.