A chain-of-command framework written in Go
Go
Permalink
Failed to load latest commit information.
cli Adding support for CLI Subcommands. Sep 23, 2014
convert More documentation fixes. Sep 19, 2014
database Add sync to statement cache. May 8, 2014
doc ServeTLS should also not use mux. Oct 11, 2014
example Added a Codegansta-like API. Jun 12, 2015
fmt changed param contents to content to match web.Flush. Sep 17, 2014
io When muliwriter fails logging, send messages to stderr. Mar 22, 2014
log Fixed broken send() function. Jul 15, 2015
safely Added safely.GoDo. Sep 5, 2014
web Added GuessContentType. Sep 21, 2015
.gitignore Ignore .test files. Sep 5, 2013
.travis.yml Now require 1.3 for graceful shutdowns. Sep 17, 2014
CHANGELOG.md Updated CHANGELOG for 1.2 release. Jun 17, 2015
LICENSE.txt Updated documentation. Jul 27, 2014
README.md Added a Codegansta-like API. Jun 12, 2015
commands.go Fixes to Godocs. Jul 27, 2014
commands_test.go Cleaning up the comments and adding tests to commands.go Mar 4, 2014
context.go Fixes to Godocs. Jul 27, 2014
context_test.go Completed the code test coverate for context.go and cleaned up some c… Mar 4, 2014
cookoo.go Added exampels in the test files. Jun 12, 2015
cookoo_test.go Added exampels in the test files. Jun 12, 2015
env.sh Fixed complaints from shellcheck on env.sh. Aug 3, 2015
getter.go Fixed tests and added docs. Jul 23, 2014
getter_test.go Fixed tests and added docs. Jul 23, 2014
glide.yaml Added glide.yaml. Jul 15, 2015
mapper.go Support CmdSpec in addition to commands. Jun 16, 2015
mapper_test.go Tests are good. Jun 17, 2015
params.go Fixes to Godocs. Jul 27, 2014
params_test.go Passed the codebase through go fmt. Oct 17, 2013
registry.go Added DoesCmdDef to registry. Jun 17, 2015
registry_test.go Support CmdSpec in addition to commands. Jun 16, 2015
router.go Fixed documentation formatting. Jun 28, 2015
router_test.go Added tests for CmdDef. Jun 16, 2015
sync_context.go Initial add of SyncContext. Apr 26, 2014

README.md

Cookoo

A chain-of-command framework written in Go

Build Status GoDoc

Usage

$ cd $GOPATH
$ go get github.com/Masterminds/cookoo

Use it as follows (from example/example.go):

package main

import (
    // This is the path to Cookoo
    "fmt"
    "github.com/Masterminds/cookoo"
)

func main() {

    // Build a new Cookoo app.
    registry, router, context := cookoo.Cookoo()

    // Fill the registry.
    registry.AddRoutes(
        cookoo.Route{
            Name: "TEST",
            Help: "A test route",
            Does: cookoo.Tasks{
                cookoo.Cmd{
                    Name: "hi",
                    Fn:   HelloWorld,
                },
            },
        },
    )

    // Execute the route.
    router.HandleRequest("TEST", context, false)
}

func HelloWorld(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) {
    fmt.Println("Hello World")
    return true, nil
}

Documentation

A Real Example

For a real example of Cookoo, take a look at Skunk.

Here's what Skunk's registry looks like:

    registry.
    Route("scaffold", "Scaffold a new app.").
        Does(LoadSettings, "settings").
            Using("file").WithDefault(homedir + "/settings.json").From("cxt:SettingsFile").
        Does(MakeDirectories, "dirs").
            Using("basedir").From("cxt:basedir").
            Using("directories").From("cxt:directories").
        Does(RenderTemplates, "template").
            Using("tpldir").From("cxt:homedir").
            Using("basedir").From("cxt:basedir").
            Using("templates").From("cxt:templates").
    Route("help", "Print help").
        Does(Usage, "Testing")

This has two routes:

  • scaffold
  • help

The help route just runs the command Usage, which looks like this:

func Usage(cxt cookoo.Context, params *cookoo.Params) interface{} {
    fmt.Println("Usage: skunk PROJECTNAME")
    return true
}

That is a good example of a basic command.

The scaffold route is more complex. It performs the following commands (in order):

  • LoadSettings: Load a settings.json file into the context.
  • MakeDirectories: Make a bunch of directories.
  • RenderTemplates: Perform template conversions on some files.

The MakeDirectories command is an example of a more complex command. It takes two parameters (declared with Using().From()):

  1. basedir: The base directory where the new subdirectories will be created. This comes from the cxt:basedir source, which means Cookoo looks in the Context object for a value named basedir.
  2. directoies: An array of directory names that this command will create. These come from cxt:directories, which means that the Context object is queried for the value of directories. In this case, that value is actually loaded from the settings.json file into the context by the LoadSettings command.`

With that in mind, let's look at the command:

// The MakeDirectories command.
// All commands take a Context and a Params object, and return an
// interface{}
func MakeDirectories(cxt cookoo.Context, params *cookoo.Params) interface{} {

    // This is how we get something out of the Params object. This is the
    // value that was passed in by `Using('basedir').From('cxt:basedir')
    basedir := params.Get("basedir", ".").(string)

    // This is another way to get a parameter value. This form allows us
    // to conveniently check that the parameter exists.
    d, ok := params.Has("directories")
    if !ok {
        // Did nothing. But we don't want to raise an error.
        return false
    }

    // We do have to do an explicit type conversion.
    directories := d.([]interface{})

    // Here we do the work of creating directories.
    for _, dir := range directories {
        dname := path.Join(basedir, dir.(string))
        os.MkdirAll(dname, 0755)
    }

    // We don't really have anything special to return, so we just
    // indicate that the command was successful.
    return true
}

This is a basic example of working with Cookoo. But far more sophisticated workflows can be built inexpensively and quickly, and in a style that encourages building small and re-usable chunks of code.