Dawson's Creek Trapper Keeper Ultra Keeper Futura S 2000
dctk: organize your programs withdctk helps you set up your own command or commands, which use separate scripts as subcommands. Such subcommands work much like git's or rbenv's subcommands.
Subcommands are scripts or programs, independent of one another and dealing with individual concerns.
Think of it as a way to organize a large amount of functionality into a single command, like pages in a trapper-keeper, to keep the joke going.
examples
Here are some quick examples in the model of rbenv, if rbenv were implemented as a trapper-keeper:
$ rbenv # prints out usage and lists available subcommands
$ rbenv versions # runs the "versions" subcommand
$ rbenv shell 1.9.3-p194 # runs the "shell" subcommand, passing "1.9.3-p194" as an argument
Each subcommand maps to a separate, standalone executable file.
dctk operates with the following basic tree structure (all are directories):
.
├── bin # your command's link to the dctk executable
├─┬ dctk # built-in functionality
│ └── …
├── completions # autocompletion for your command
├── [command] # your command's subcommands
└── …
creating your first command
Here's an example of adding a new subcommand. Let's say your trapper-keeper is named "rush". Run:
touch rush/who
chmod +x rush/who
Edit rush/who
with the following:
#!/usr/bin/env bash
who
Now you can run rush who
:
$ rush who
qrush console Sep 14 17:15
That's it. You now have a working subcommand.
You can add autocompletion support and help documentation as well, by adding a little bit more to your script, as you'll see.
dctk the project versus the command
dctk, the project, is this repo and the code of which it is comprised.
While it is invoked using the command dctk
at the outset, the dctk
command is replaced with your own after your customize it to your own
needs. dctk
is just a link in bin
, a link which will ultimately be
renamed to your command's name.
This readme refers to the project and how it works as dctk, even though
there won't be a dctk
command after you run the prepare
script to
customize it. Your project will contain dctk's bones but will add the
functionality that makes it your own command.
prerequisites
dctk requires the kaizen bash library.
Clone that repo and either add its lib
to your PATH or put
lib/kzn.bash
somewhere already on your PATH.
installation
First clone this repository to your own command's name:
$ git clone --depth=1 git://github.com/binaryphile/dctk [your command name]
Then run the prepare
script while in this directory:
$ cd [command]
$ ./prepare [command]
Third, add the following to your shell's startup script (.bash_profile or .zshrc, for example):
eval "$("$HOME"/[path to your dctk]/bin/[command] init -)"
Finally, you may decide to get rid of the .git
folder and start your
own repository.
Once prepare
d, dctk becomes the new command is invoked like so:
$ [command] [subcommand] [(args)]
the dctk command
After preparation, your command in bin
is a link to the dctk script in
dctk/bin/dctk
. You can examine the file there to see the guts of dctk
in action.
dctk determines your command's name from how it was invoked on the command-line, so the fact that its filename doesn't match the command is not important.
From here on out, when this document refers to the dctk
command, it
means your command instead, once you've prepared the project, so just
visualize that command's name wherever it says dctk
.
what's in your trapper-keeper
Your trapper-keeper comes with a few subcommands already built-in:
-
dctk commands
- print available subcommands -
dctk completions
- provide command completion details for use by shell autocompletion -
dctk help
- parse and display subcommand help, if provided in the script -
dctk init
- hook completions into the shell, usually when invoked from a shell startup file (e.g..bash_profile
)
subcommands
Subcommands live in the directory named for your command and are simply
named for the subcommand, e.g. rbenv/versions
. For this reason you
can't make a command with the same name as the existing directories,
bin
, dctk
, completions
or shpec
. Anything else is fair game.
Subcommands don't have to be written in bash. They can be any executable, scripted or compiled. Even symlinks work.
Subcommands can provide documentation in comments, automatically made
available through the dctk
and dctk help [subcommand]
commands.
Subcommands can also provide shell autocompletion by implementing a
well-known flag (--complete
). dctk handles the details of informing
the shell (bash and zsh supported).
self-documenting subcommands
Documentation is provided by adding leading comments to your script. This feature is limited to scripts which use "#" as their comment delimiter.
Here's an example from rush who
:
#!/usr/bin/env bash
# Usage: rush who
# Summary: Check who's logged in
# Help: This will print out when you run `rush help who`.
# You can have multiple lines even!
#
# Show off an example indented
#
# And maybe start off another one?
who
Now, when you run rush
, the "Summary" magic comment will now show up:
usage: rush <command> [<args>]
Some useful rush commands are:
commands List all rush commands
who Check who's logged in
And running rush help who
will show the "Usage" magic comment, and
then the "Help" comment block:
Usage: rush who
This will print out when you run `rush help who`.
You can have multiple lines even!
Show off an example indented
And maybe start off another one?
Because it is free-form, the Help section must be the last of your leading comments in the script.
autocompletion
dctk provides autocompletion at two levels:
-
autocompletion of subcommand names (what subcommands are available?)
-
(optional) autocompletion of subcommand arguments (what arguments does this subcommand take?)
The second requires that you add another leading comment, like so:
#!/usr/bin/env bash
# Completions: true
# Usage: rush who
Your script must also parse the flag --complete
. For example:
#!/usr/bin/env bash
# completions: true
if [[ $1 == "--complete" ]]; then
echo "--path"
fi
# etc...
Your subcommand should echo the list of valid completions and exit when
receiving the --complete
flag instead of normal operation. dctk feeds
this output to your shell's autocompletion handler for you.
multiple commands
When you saw the directory tree earlier, it was for a single command. However you can have multiple commands as well, independent of each other.
.
├── bin # your commands' links to the dctk executable
├── completions # your commands' links to the completions script
├── …
├── [command] # one command's subcommands
├── [another command] # you can have more than one
└── …
Each command exists side-by-side in its own directory. Each has its own subcommands.
All that is required to become a command is for the directory to exist, and for there to be link with the command's name to the dctk executable:
$ mkdir [command]
$ cd bin
$ ln -sf ../dctk/bin/dctk [command]
You should also enable completions for your command with a link:
$ cd ../completions
$ ln -sf ../dctk/completions/dctk.bash [command].bash # or .zsh, if that's your shell
Add subcommands to the [command] directory and you're good to go.
structured commands
So far, you've seen simple commands. Subcommands exist simply as executables in their command's directory.
However, your commands may require more structure. For example, you may
have data files which might go in [command]/share
.
Or you may have a command which is already a project of its own, such as an existing sub or a repo you'd like to add as a git submodule.
.
├── …
├─┬ [command]
│ ├── bin # if bin and/or libexec exist, look there instead for subcommands
│ ├── libexec # for example, when importing an existing project or [sub]
│ ├── share # or to separate out supporting files
│ └── other # whatever you like
└── …
If dctk sees a [command]/bin
and/or [command]/libexec
directory, it
will treat the command as structured. A structured command finds its
subcommands in the bin
and libexec
subdirectories instead of the
main [command]
directory (with bin
taking priority in conflicts).
If you ever need to reference files in other subdirectories of your
structured command, say to access a file in the [command]/share
directory, dctk provides the environment variable _[COMMAND]_ROOT
. For
example, if your command is named "rush", then the variable is
_RUSH_ROOT
, which expands to [path to dctk]/rush
. That would make
the share folder $_RUSH_ROOT/share
.
why "trapper-keeper"?
dctk is meant to make it easy to assimilate any kind of technology into your trapper-keeper as a subcommand, much like Dawson's Creek Trapper Keeper Ultra Keeper Futura S 2000 assimilated all of Cartman's belongings (as well as Cartman himself). His trapper-keeper ultimately became sentient and took over the world. Hopefully yours will be less ambitious, while still as powerful.
sub?
isn't dctk just a rip-off ofdctk is inspired by sub but shares almost no code, being a ground-up rewrite. It also sports numerous additional features:
-
multiple top-level commands in one trapper-keeper
-
structured commands
Future:
-
hierarchical subcommands by nesting directories
-
functions as subcommands
-
docopt support
License
Apache 2.0. See LICENSE.md
.