From: "Marcel Molina Jr." Date: 2005-04-13T04:02:57+09:00 Subject: Re: [ANN] cmd 0.7.0: Library for Line-Oriented Command Interpreters (initial release) On Tue, Apr 12, 2005 at 01:09:39PM +0900, itsme213 wrote: > "Marcel Molina Jr." wrote > > > Any plans to go towards interactive commands e.g. some commands valid > only > > > within scope of other commands? > > > > By interactive commands do you mean something like? > > > > > foo > > # now you are in a context within 'foo' that opens you up to a whole > other > > # set of things > > Sort-of ... lousy description on my part. I can see the command interpreter > as a state machine consisting of a current object + the available commands > on the current object. I'll start with the latter. > > The available commands on the current object could change as you do things > to it. Suppose each of its commands has some declared pre-condition. A > command is disabled if its precondition is false. Here is one of many ways > to do this. > > class PhoneBook < Cmd > # ... define commands & docs > pre :add { |pb| pb.condition_on_phonebook_add } > pre :remove { |pb| pb.condition_on_phonebook_remove } > end > > All commands whose pre is true are 'enabled', all others are 'greyed-out'. > Preconditions could have a descriptive string for help purposes. Perhaps the already existent precmd and postcmd methods could be extracted into something that was a bit more aware and versatile. Like above where rather than having precmd be a hook that is called before all commands, the pre macro here would set up a pre condition for the specified command. But the example above is about what commands you are then afforded, which sort of translates to what commands the completion proc will display you and not much else that I can think of. I imagine the notion of setting pre condition hooks on a per command basis could be generalized to offer more than just what subcommands you are then afforded, like 'input actions' in FSM. Sort of seems to get into the realm of Design by Contract. > Secondly the current object itself could be switched. e.g If you add some > special commands to "cd", "pushd", and "popd" to any command-able object, to > start working in a new command context. > > PBook> pushd `find Sam` > - execute command "find Sam" in current context > - perhaps check return (e.g. a Person) is command-able > - make that the new command context, so ... > Sam> first Sammy > last Brown > popd > PBook> This idea seems quite similar to irb's job management where you can create a subirb which lets you essentially "enter" the object that you invoke the subirb session onto. Perhaps rather than having to fall back on some exiting convention like popd, something sort of like what Luke Graham was suggesting could happen. A thread with a specialized Cmd. Or perhaps there could be come simple protocol where by Cmd instances could interact with one another. It all gets a bit sophisticated after a certain point. I don't know whether this kind of stuff is better suited built into Cmd or on top of it. But I do welcome the ideas and look forward to see what bubbles up as I take the time to think about them. > And, lastly, every command has arguments. Each argument has a range of valid > values that can depend on current state. If you store an array of blocks > that computes the possible values for each argument given the current > context object (when applicable), you can even provide completion for > arguments to commands. > > class PhoneBook < Cmd > # can only delete X if X currently in phonebook > command :delete do > args [ {|pb| pb.people.collect { |p| p.name }} > # even better if returned objects, not strings > # using to_s or some protocol to get strings for completion > # command intereter keeps map of string->object > # refuse to execute if no valid completion > # inner code just deals with objects > ] > end > end > > PBook> delete > #--> Sammy Mary > PBook> delete S > #--> delete Sammy More DbC sort of stuff kind of. More simply, this functionality is actually sort of already implemented. Or at least the blue print is there and a I've commented on the hypothetical API in the TODO. Customized completion per command should be as easy as providing a proc or block or method reference or hard-coded array or whatever that provides a collection of things to complete against. This can be set for commands, subcommands, etc. > btw: have you considered adding a Command class to keep all info about one > command? Yes actually. Here is the branch at the point where I abandoned it for the time being: http://svn.vernix.org/main/library/cmd/branches/custom-classes-for-commands/lib/cmd.rb I set out to make cmd way more compositional. There was a Command class, a Commands container, Shortcut, SubCommand and I was thinking of Documentation. It would be really useful to be able to have a command object and just ask it if it has subcommands and get a list of them or ask it for its documentation. That's what I was aiming for. It ended up just adding way more complexity (which I was aiming to diminish by taking that approach). It was kludgy and sucked. This was largely just because I did a bad job of it. I have the feeling there could be a nice balance, where the composition of objects approach is succinct and elegant and simple. I just had to back out and approach it again from a different angle at the time. So after hanging out on that branch for a few days I just moved back to the original approach. Now the plan, enumerated in the TODO file, is to have Cmd be a module that you mix in. When you mix it in you define methods with whatever name you want and then you a 'command' class method to designate something a command. For example: command :foo def foo # ... end This makes it easy to take code that was developed previously or without Cmd in mind and stick a Cmd type interface onto it. Alternatively, there would be some Cmd::Base class that has the Cmd module mixed in. You could instead inherit from this and rather than use the 'command' class method or have some naming system, work off the convention that all public methods of the subclass are implicitly commands. After doing that I think I would tackle wrapping command and subcommand discovery into specialized instances of stuff like a Command class. Thanks for the suggestions! Glad you're interested, marcel -- Marcel Molina Jr.