http://qs1969.pair.com?node_id=601767

Ace128 has asked for the wisdom of the Perl Monks concerning the following question:

Greetings!

Ok, I have been thinking about this on and off for a while now, but having trouble starting with the code as Im not sure how others may wanna use it. And the main problem is the structure to create to make it easy to traverse through it all.. The idea is to create something that is easy to expand, and as much errorprone as possible. It feels to me that I should use some kind of "state machine" or something. So, I turn torwards you bright monks here, for feedback and suggestions.

So, I'm doing my own version of a shell, and beeing creative as I am, I wanna implement the following:

Say a user types:

>> search
and press enter. Now it may display something like:

>> <cursor is here>
Mode: search

Showing that we are now in "search mode" where a number of additional funktions may be available. Since this shell is a module, one is supposed to register a callback with a function. Now, say we continue by typing:

>> file perlmonks*
and we use the shell for searching. This may then show:

>> <cursor is here>
Mode: search
Query: SELECT database.file FROM database WHERE database.file LIKE("perlmonks%");

where the query output is for now just debuginfo showing up, but that is what is created in the background later.


If we now continue with:
>> size > 50mb <enter is pressed>
we get:

>> <cursor is here>
Mode: search
Query: SELECT database.file, database.size FROM database WHERE database.file LIKE("perlmonks%") AND database.size > 50000000;

Seems pretty nice eh? Now, to expand this functionality abit, I also want the following to be possible. Notice we have "file" above. Then we are now also able to do:
>> file <and press enter>
We get:
Mode: search -> file
and the query is still the same. However if we type:

>> *wisdom NOT *stupid*
we get:
Mode: search -> file
Query: SELECT database.file, database.size FROM database WHERE database.file LIKE("perlmonks%") AND database.file LIKE("%wisdom") AND NOT database.file LIKE("%stupid%") AND database.size > 50000000;

So, building the query up dynamicly as we type! If we press ESCAPE, we go up one level like:

>> <ESC>
Mode: search
but the query hasn't changed.

So, we have "functions" and "keywords" (or whatever name is suitable). Functions are those that we can "enter" by just typing them and pressing enter. Or, by typing them and then keywords afterwards. If we enter the function, we have now access to the keywords directly.

I now turn to you and ask the big question. What structure is the best to implement to make this reality?

So far I've come up with the following hash structure (which is what users of the shell module pass in to the shell module):
::Core:: 'search' => { '-command' => 'search', '-autocomplete' => ['file', 'full_path', 'just_dir', 'extensio +n'], '-information' => "Search the database.", '-command_function' => \&searchDatabase, '-mode_function' => \ +&searchMode, }, 'search::file' => { '-command' => 'file', '-autocomplete' => ['file', 'full_path', 'just_dir', 'extensio +n'], '-information' => "Search the database.", '-command_function' => \&searchDatabase, '-mode_function' => \ +&searchMode, }, 'search::show' => { '-command' => 'show', '-autocomplete' => { '-pattern' => '/modtime|modification\stime/modtime +/', # like a s/// '-namespace' => 'search::*', '-exact' => ['file', 'full_path', 'just_dir', 'ext +ension'], } '-information' => "Search the database.", '-command_function' => \&searchDatabase, '-mode_function' => \ +&searchMode, }, ::mp3Plugin:: 'search' => { '-command' => 'search', '-autocomplete' => ['artist', 'title'], '-information' => "Search the mp3 music.", }, 'search::artist' => { '-command' => 'artist', '-autocomplete' => [], '-information' => "Search the artist.", '-command_function' => \&searchDatabase, '-mode_function' => \ +&searchMode, },
I love using hashes like this since this makes it easy to add more functionality without breaking things too much. The hashkeys are "namespaces". Meaning that when you say "search::file", "file" is a child to "search".

With the "mp3Plugin" we have a small problem though. The first namespace (search) collide with the one in the core. I guess the best way to solve that is to ignore it if it's already created...

Otherwise, I guess this is rather OK way to define everything in a readable fashion. The problem now is to create a nice structure out of this that isnt too complicated. I was thinking some kind of tree, and then I recalled the lovely CPAN... and then I became little confused as I'm not sure what could be the most suitable to use for this!

What say you?
/ Ace

This idea is patented and copyrighted and all that legal stuff! ;)