qrush / sub

a delicious way to organize programs
http://37signals.com/svn/posts/3264-automating-with-convention-introducing-sub
MIT License
1.74k stars 148 forks source link

Sub sub commands #37

Open jeffreyroberts opened 11 years ago

jeffreyroberts commented 11 years ago

Let me know if there is anything I can do, add, remove, etc..

It's solid, give it a test drive, it rocks

Fixes #7 Fixes #12 Fixes #16 Fixes #22 Fixes #27

Example Multi-level autocompletion for --complete: Fixes #7

[ /sub ] $ sub example advanced fix-7
bar  foo
[ /sub ] $ sub example advanced fix-7 foo b
baz  bla

Nested "sub-sub-commands": Fixes #22

.
├── bin                               # contains the main executable for your program
├── completions                       # (optional) bash/zsh completions
├── libexec                           # where the subcommand executables are
├───> sub-command                     # sub command (Displays summary for b & c)
|  ├────∎ sub-command-b               # sub command command-b <args>
|  └────∎ sub-command-c               # sub command command-c <args>
└── share 

Example Directory and File Structure: Fixes #22

.
├── bin                               # Real Life Example
├── completions                       # Directories return help for children
├── libexec                           # 
├───> sub-example                     # sub example (Returns summary)
|  ├────> sub-basic                   # sub example basic (Returns summary)
|  |   ├────∎ sub-foreach             # sub example basic foreach (Script)
|  |   └────∎ sub-files               # sub example basic files (Script)
|  └────> sub-advanced                # sub example advanced (Returns summary)
|      └────> sub-expert              # sub example advanced expert (Returns summary)
|          └────∎ sub-case-select     # sub example advanced expert case-select (Script)
└── share 

Example For Directory Help Helper File: Fixes #22

├── libexec                           # 
├───> sub-example                     # sub example (Returns help for example)
|  └───∎ sub-example                  # Provides area for usage, summary, and help

Example Help Helper File: Fixes #22

#!/usr/bin/env bash
# Usage: sub example
# Summary: Collection of BASH Sub Example Scripts
# Help: These commands are mostly used as examples and for testing sub modifications

set -e

sub help example
exit

Example Help Display With sub-sub-commands: Fixes #22

[ /sub ] $ sub help
Usage: sub <command> [<args>]

Some useful sub commands are:
   commands     List all sub commands
   -> example   Collection of BASH Sub Example Scripts

See 'sub help <command>' for information on a specific command.

Example Multiple Command w/ Completion: Fixes #22 & Fixes #12

[ /sub ] $ sub example advanced expert case-select --
--break  --name

Example Multiple Commands and Flags: Fixes #22

[ /sub ] $ sub example advanced expert case-select --break 3 --name Jeff
-=> Executing Case Select Example
----> Breaking on 3
----> Counter is called Jeff
------> The Jeff is 0
------> The Jeff is 1
------> The Jeff is 2
- Finished...

Example File Completion: Fixes #16 & Fixes #27

[ /sub ] $ sub example basic files
.git/        .gitmodules  README.md    completions/ plugins/     share/
.gitignore   LICENSE      bin/         libexec/     prepare.sh
jblossomweb commented 11 years ago

+1

gregsuskind commented 11 years ago

+1

jakeasmith commented 11 years ago

:+1:

bndn commented 11 years ago

:+1:

jeffreyroberts commented 11 years ago

Fixed minor typo in auto-completion file

jdevera commented 11 years ago

I'm running this as several levels of subcommands are an essential feature for me, so thanks! However I have noticed that the sub-sh-* commands do not work beyond the first level. It seems like the init script has to support that somehow, I just don't know how. Any ideas?

jdevera commented 11 years ago

It seems that replacing _cl_wrapper with this makes the sh-commands work in lower levels:

_cl_wrapper() {
    local pb=$_CL_ROOT/libexec
    local cb
    local cmd=""
    for c in \$@
    do
        if [[ -d \$pb/cl-\$c ]]; then
            pb=\$pb/cl-\$c
            cb="\$cb \$c"
            shift
        elif [[ -f \$pb/cl-sh-\$c ]]; then
            cmd="\$cb sh-\$c"
            shift
        else
            echo command cl \$cb "\$@"
            command cl \$cb "\$@"
            return \$?
        fi

        if [[ -n \$cmd ]]; then
            eval \`cl \$cmd "\$@"\`
            return \$?
        fi
    done
}

However it's barely tested. I have forked the repo and will apply your changes to play a bit with them.

jdevera commented 11 years ago

I also noticed that because the loop that build the command stops when it sees -*, if you pass a positional parameter to an inner scripts, it gets swallowed by the sub.

sub example advanced expert case-select aaa --name

Here cl-case-select only gets --name, not aaa.

jeffreyroberts commented 11 years ago

Hey, Awesome stuff, I will take a look at all of this over the weekend =D

jdevera commented 11 years ago

I also wonder about the rationale for moving completions to sub-command.

jdevera commented 11 years ago

Just for fun, I gave this a go and used a different implementation to support multi level sub commands. My conclusion is that a) it's a mess b) It gets complex enough that it warrants using a more suitable language. So I'm thinking of writing this in python before adding some of the ideas I've had while developing.

I won't open a pull request because it makes no sense to me since there is this one already, but you can take a look at it here: https://github.com/jdevera/sub/tree/subsub

jdevera commented 11 years ago

I ended up rewriting the whole thing in python because it got too big for shell scripting.

Here it is: https://github.com/jdevera/sub/tree/python in case someone wants to give it a go.

I implemented all the default commands internally so I could reuse code and the sub script itself no longer needs to have its guts changed by sed, since it will pick up the sub name from the name of the file itself.

I also changed the shell wrapper so that the script itself is responsible for determining if certain command is an 'sh' command or not.

I added a ton of logging that can be enabled by running with the SUBDEBUG variable set to something.

The direction I'm headed now is to implement what I call "thin subs". The idea is that the sub core lives in one place, and the user's subs live in another, so you can keep the core updated and get the benefits in all your subs, and you don't have to check the core in with your scripts if you don't want to.

If anybody wants to give it a go, you are very welcome, remember I haven't yet merged it to master, so you'll need to checkout the "python" branch after cloning.