views:

390

answers:

6

I have:

  • existing object oriented native code API (non GUI)
  • GUI application that works with this API

The goal: To create an additional console application that lets user do some set of workflows (similar to ones of the above GUI app) by typing commands. This app should be "stateful" - available commands and their results would depend on the previously issued commands.

The problem: I do not want to "reinvent the wheel". Are there existing patterns for both building the app and defining the "vocabulary"? Currently, it seems to me the best option would be to write a set of helpers and command parser "from scratch".

P.S. If my API would be in .Net, I would look into PowerShell direction, but the API is large and wrapping it into .Net is very time consuming.

+1  A: 

Do you have any COM interfaces? PowerShell can seamlessly script COM, WMI or .NET; you could even do runtime p/invoke calls with pure script; a lot less time consuming to prototype. Once you solidify the design, you can optionally wrap as native Cmdlets later for speed.

-Oisin

x0n
It seems like maintaining state for the 'available commands change based on API state' use case doesn't lend itself to operating in a state-light environment like a login shell... is my impression of PowerShell's ability for cmdlets to interact with a state store incorrect?
Tetsujin no Oni
My thoughts exactly. I would strongly consider creating a com object out of your api. It is most likely going to be the faster approach also.
skamradt
@Tetsujin powershell has the concept of dynamic parameters which are conditionally made available on existing commands, depending entirely on backing-store state - typically encapsulated as a provider. For example, the -Wait switch is added to Get-Content cmdlet by the filesystem provider, which could be conditional on the current path into that provider and/or it's internal state.
x0n
+1  A: 

If you do end up using .net, perhaps you could take a look @ the Mono.Options library: http://tirania.org/blog/archive/2008/Oct-14.html

"Mono.Options is a beautiful command line parsing library. It is small, succinct, a joy to use, easy and powerful, all in one."

Joel Martinez
Even if not using .Net, good as a pattern!
Sergey Aldoukhov
+3  A: 

The pattern of use you are describing sounds like a Read-Eval-Print loop (REPL), the common mode of interaction for interpreted languages.

In point of fact, you seem to be describing a command language and interpreter, so I would suggest examining that domain for patterns matching your existing binding for the API.

Tetsujin no Oni
A: 

I strongly agree with xOn's answer, but to maintain state between calls, just make it a single instance com object, then write a "login" function to call AddRef and a "logoff" function to call Release. That way the object will stick around between calls. The only risk will be multiple runs on the same box with multiple logins... so you will want to trap for that.

Depending on the state you require, if its not much and can be streamed easily to a disk file or database, then a multi-instance com object would behave better, and wouldn't require the addref or release calls.

skamradt
+1  A: 

To get started on the command line, first don't reinvent the wheel. There are a lot of options out there to parse commands.

In Java there is Commons CLI which provides you everything you need. There is a .NET CLI port as well.

InfiniteRed has a good writeup of how to do this in Ruby.

As far as implementation goes, you have the right idea. But don't reinvent the wheel here, either. Encapsulate work in Command objects and look at using the Chain of Responsibility pattern; Commons Chain works well. There is also a .NET Chain port.

If using these frameworks isn't an option, take a look at how they're implemented. Also if you've got a problem doing interop with some of these options, Ruby is really a nice swiss-army knife for doing this type of thing. It's relatively portable and the code can end up being really clean and easy to maintain.

UPDATE: JCommander also looks interesting.

cwash
Chain of Responsibility is pretty close to home, thanks!
Sergey Aldoukhov
I think that's probably all you'd need. Just shove off all the state you need into the Chain's execution context.
cwash
+1  A: 

Wrap your API using automatic tools like SIP or SWIG, import them as a python module into an ipython session, do stuff with your objects from the command line. Job done.

If this fails, it'll be because either:

  • Your objects/API aren't suitable for automatic wrapping (but the process of getting them into a wrappable state generally means improving them).
  • Python isn't really what you had in mind for a command line (even with the ipython enhancements).
timday