question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Separate initialization from execution in `CommandApp`

See original GitHub issue

As a user of the API, I’d like the ability to perform global setup and teardown in my application without utilizing base classes for my Command<> / AsyncCommand<> subclasses.

Is your feature request related to a problem? Please describe.

At the moment, if I want to perform some global initialization and/or cleanup, I am required to use base classes or other obscure hacks. The problem with the base class solution is that it creates a lot of constructor boilerplate and makes the code difficult to maintain when changes are needed in that common base class, especially when that change involves modifying dependencies (parameters for DI).

Describe the solution you’d like

I’d like the logic in CommandApp.RunAsync() to be separated such that the following is separated into distinct phases of execution controlled by the user of the API:

  1. Parsing of the command tree. Registration of internal types and dependencies. Finally, creation of the implementation for ITypeResolver.
  2. Execution of the actual commands.

This allows me to do the following:

  1. Perform setup of my command line application as normal.
  2. Invoke a new method (or even better: Use the builder pattern) to initiate step 1 above (e.g. CommandApp.Setup()).
  3. Access the ITypeResolver via some property (e.g. CommandApp.Resolver) to perform DI resolution for any dependencies needed for my global initialization/cleanup.
  4. Perform global initialization as needed in Program.Main()
  5. Invoke CommandApp.Run() and store the result.
  6. Perform global cleanup as needed in Program.Main()
  7. Return the result of the previously executed CommandApp.Run() command.

Describe alternatives you’ve considered

  1. Abusing my implementation of ITypeRegistrar to invoke a callback to perform custom initialization when ITypeRegistrar.Build() is called.
  2. Implementing a base class shared by all command handlers that implements Command<> or AsyncCommand<>.

Additional context

I am willing to implement the changes needed here. I’ve already played around with some ideas in a local branch and landed on two approaches:

  1. Quick & dirty: Add new methods to CommandApp with the goal of reducing footprint. Not the most ideal change and am not completely happy with it, but it at least gets that separation I’m aiming for.
  2. Implement something like CommandAppBuilder that uses a syntax similar to new CommandAppBuilder().WithConfiguration(...).Build() to construct a CommandApp and return it, which will be responsible for the first phase of initialization. The returned CommandApp would actually be composed in a wrapper object that only allows users to invoke Run() / RunAsync() on it.

The second approach is more drastic, but I think there’s a way to maintain backward compatibility in both scenarios.

The reason I’m creating an issue instead of a pull request:

  1. I want to discuss the change before I spend time coding it. The change is non-trivial so I need to consider the maintainers’ opinions.
  2. I’m uncertain about how to split the logic gracefully in CommandExecutor.Execute(). There’s a lot of separation of concerns issues in this method:
    • Initialization (registration) logic is mixed in with what appears to be processing logic.
    • There’s a circular dependency between DI registration and command tree processing which further complicates separation of the two.
    • There’s CLI parsing code for the version options here that probably belongs in its own Command subclass; however I believe cleaning this up properly requires implementing a way to add “global options” to the base command without introducing new subcommands/verbs. There are other issues here that call for this, and I imagine this code exists because of the lack of this functionality.

Happy to help invest time in this library if the developers have the time to help guide me! Thank you for reading!

Issue Analytics

  • State:open
  • Created 10 months ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
rcdaileycommented, Jul 18, 2023

Thank you for the update! It is very promising that progress is being made that gets us closer to being able to do this. I’m still using my rather unsavory workarounds to have more control over initialization logic. I’m definitely still interested in the outcomes being shared here.

As always, let me know if I can help in any way. You’re doing great!

1reaction
FrankRay78commented, Jul 18, 2023

Hello @rcdailey, just checking in to say this has not been forgotten.

Rather the work on exposing the help writer needed to be re-implemented to better support DI in CommandExecutor.Execute() (see my comment here: https://github.com/spectreconsole/spectre.console/discussions/1224#discussioncomment-6476669)

Once done, I think the general usage of DI, and the further potential usage of DI for other, currently internal spectre.console classes, becomes easier and cleaner to do.

Read more comments on GitHub >

github_iconTop Results From Across the Web

how to handle lots of separate ui commands c#, how to get ...
so I can do this: // create command var GuiRoomSelectionCommand = new GuiCommandTest(); // setup and execute (I have to provide additional data ......
Read more >
Commands — FIRST Robotics Competition documentation
Commands specify what the command will do in each of its possible states. This is done by overriding the initialize() , execute() ,...
Read more >
Command: init | Terraform
This command performs several different initialization steps in order to prepare the current working directory for use with Terraform. More details on these ......
Read more >
Coding the initialization routine
Your initialization routine determines whether the subsystem can respond to the SETSSI command by using the options function of the IEFSSI macro. See...
Read more >
The Rails Initialization Process
For this guide, we will be focusing on what happens when you execute bin/rails server to boot your app. Paths in this guide...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found