From 6664835bc1e02caa4c3d4cd43e3b3033166c7489 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Fri, 19 Jul 2013 08:34:01 -0700 Subject: [PATCH 1/4] JMS #14: Moving toward having and app object --- app.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ cli.go | 40 ---------------------------------------- cli_test.go | 23 +++++++++++++---------- command.go | 2 +- context.go | 5 +++-- context_test.go | 10 +++++----- flag.go | 2 +- help.go | 17 +---------------- 8 files changed, 72 insertions(+), 75 deletions(-) create mode 100644 app.go diff --git a/app.go b/app.go new file mode 100644 index 0000000..ebbdd4b --- /dev/null +++ b/app.go @@ -0,0 +1,48 @@ +package cli + +import ( + "os" +) + +type App struct { + // The name of the program. Defaults to os.Args[0] + Name string + // Description of the program. + Usage string + // Version of the program + Version string + // List of commands to execute + Commands []Command + Flags []Flag + // The action to execute when no subcommands are specified + Action Handler +} + +func NewApp() *App { + return &App{ + Name: os.Args[0], + Usage: "A new cli application", + Version: "0.0.0", + Action: ShowHelp, + } +} + +func (a *App) Run(arguments []string) { + set := flagSet(a.Flags) + set.Parse(arguments[1:]) + + context := NewContext(a, set, set) + args := context.Args() + if len(args) > 0 { + name := args[0] + for _, c := range append(a.Commands, HelpCommand) { + if c.HasName(name) { + c.Run(context) + return + } + } + } + + // Run default Action + a.Action(context) +} diff --git a/cli.go b/cli.go index e97e18e..567e04a 100644 --- a/cli.go +++ b/cli.go @@ -1,43 +1,3 @@ package cli -import "os" - -// The name of the program. Defaults to os.Args[0] -var Name = os.Args[0] - -// Description of the program. -var Usage = "" - -// Version of the program -var Version = "0.0.0" - -// List of commands to execute -var Commands []Command - -var Flags []Flag - -// The action to execute when no subcommands are specified -var Action = ShowHelp - -func Run(arguments []string) { - - set := flagSet(Flags) - set.Parse(arguments[1:]) - - context := NewContext(set, set) - args := context.Args() - if len(args) > 0 { - name := args[0] - for _, c := range append(Commands, HelpCommand) { - if c.HasName(name) { - c.Run(context) - return - } - } - } - - // Run default Action - Action(context) -} - type Handler func(context *Context) diff --git a/cli_test.go b/cli_test.go index 1c083bb..813bfe7 100644 --- a/cli_test.go +++ b/cli_test.go @@ -6,38 +6,41 @@ import ( ) func Test_SettingFlags(t *testing.T) { - Flags = []Flag{ + app := NewApp() + app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, IntFlag{"bar", 42, "an int flag"}, BoolFlag{"bat", "a bool flag"}, } - Action = func(c *Context) { + app.Action = func(c *Context) { expect(t, c.String("foo"), "hello world") expect(t, c.Int("bar"), 245) expect(t, c.Bool("bat"), true) } - Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) + app.Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) } func Test_FlagDefaults(t *testing.T) { - Flags = []Flag{ + app := NewApp() + app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, IntFlag{"bar", 42, "an int flag"}, BoolFlag{"bat", "a bool flag"}, } - Action = func(c *Context) { + app.Action = func(c *Context) { expect(t, c.String("foo"), "default") expect(t, c.Int("bar"), 42) expect(t, c.Bool("bat"), false) } - Run([]string{"command"}) + app.Run([]string{"command"}) } func TestCommands(t *testing.T) { - Flags = []Flag{ + app := NewApp() + app.Flags = []Flag{ StringFlag{"name", "jeremy", "a name to print"}, } - Commands = []Command{ + app.Commands = []Command{ { Name: "print", Flags: []Flag{ @@ -49,10 +52,10 @@ func TestCommands(t *testing.T) { }, }, } - Action = func(c *Context) { + app.Action = func(c *Context) { t.Error("default action should not be called") } - Run([]string{"command", "--name", "jordie", "print", "--age", "21"}) + app.Run([]string{"command", "--name", "jordie", "print", "--age", "21"}) } /* Test Helpers */ diff --git a/command.go b/command.go index 0a9a7ec..d8e2011 100644 --- a/command.go +++ b/command.go @@ -12,7 +12,7 @@ type Command struct { func (command Command) Run(c *Context) { set := flagSet(command.Flags) set.Parse(c.Args()[1:]) - command.Action(NewContext(set, c.globalSet)) + command.Action(NewContext(c.App, set, c.globalSet)) } func (command Command) HasName(name string) bool { diff --git a/context.go b/context.go index ae50463..6c4cbad 100644 --- a/context.go +++ b/context.go @@ -10,12 +10,13 @@ import ( // can be used to retrieve context-specific Args and // parsed command-line options. type Context struct { + App *App flagSet *flag.FlagSet globalSet *flag.FlagSet } -func NewContext(set *flag.FlagSet, globalSet *flag.FlagSet) *Context { - return &Context{set, globalSet} +func NewContext(app *App, set *flag.FlagSet, globalSet *flag.FlagSet) *Context { + return &Context{app, set, globalSet} } // Looks up the value of a local int flag, returns 0 if no int flag exists diff --git a/context_test.go b/context_test.go index d859179..3c6bab5 100644 --- a/context_test.go +++ b/context_test.go @@ -10,7 +10,7 @@ func Test_New(t *testing.T) { set.Int("myflag", 12, "doc") globalSet := flag.NewFlagSet("test", 0) globalSet.Int("myflag", 42, "doc") - c := NewContext(set, globalSet) + c := NewContext(nil, set, globalSet) expect(t, c.Int("myflag"), 12) expect(t, c.GlobalInt("myflag"), 42) } @@ -18,28 +18,28 @@ func Test_New(t *testing.T) { func Test_Int(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Int("myflag", 12, "doc") - c := NewContext(set, set) + c := NewContext(nil, set, set) expect(t, c.Int("myflag"), 12) } func Test_String(t *testing.T) { set := flag.NewFlagSet("test", 0) set.String("myflag", "hello world", "doc") - c := NewContext(set, set) + c := NewContext(nil, set, set) expect(t, c.String("myflag"), "hello world") } func Test_Bool(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") - c := NewContext(set, set) + c := NewContext(nil, set, set) expect(t, c.Bool("myflag"), false) } func Test_Args(t *testing.T) { set := flag.NewFlagSet("test", 0) set.Bool("myflag", false, "doc") - c := NewContext(set, set) + c := NewContext(nil, set, set) set.Parse([]string{"--myflag", "bat", "baz"}) expect(t, len(c.Args()), 2) expect(t, c.Bool("myflag"), true) diff --git a/flag.go b/flag.go index 0e28ea0..4601236 100644 --- a/flag.go +++ b/flag.go @@ -9,7 +9,7 @@ type Flag interface { } func flagSet(flags []Flag) *flag.FlagSet { - set := flag.NewFlagSet(Name, flag.ExitOnError) + set := flag.NewFlagSet("cli", flag.ExitOnError) for _, f := range flags { f.Apply(set) } diff --git a/help.go b/help.go index b5cb95e..ffe5b28 100644 --- a/help.go +++ b/help.go @@ -4,14 +4,6 @@ import "os" import "text/tabwriter" import "text/template" -type HelpData struct { - Name string - Usage string - Version string - Commands []Command - Flags []Flag -} - var HelpCommand = Command{ Name: "help", ShortName: "h", @@ -39,16 +31,9 @@ GLOBAL OPTIONS {{range .Flags}}{{.}} {{end}} ` - data := HelpData{ - Name, - Usage, - Version, - append(Commands, HelpCommand), - Flags, - } w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) t := template.Must(template.New("help").Parse(helpTemplate)) - t.Execute(w, data) + t.Execute(w, c.App) w.Flush() } From f14b9c0d819a4f2b73e7b36bd4ecc27092c2d9d3 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Fri, 19 Jul 2013 14:59:57 -0700 Subject: [PATCH 2/4] JMS #4: Better tests --- cli_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cli_test.go b/cli_test.go index 813bfe7..1b362d3 100644 --- a/cli_test.go +++ b/cli_test.go @@ -6,6 +6,7 @@ import ( ) func Test_SettingFlags(t *testing.T) { + msg := "" app := NewApp() app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, @@ -16,11 +17,14 @@ func Test_SettingFlags(t *testing.T) { expect(t, c.String("foo"), "hello world") expect(t, c.Int("bar"), 245) expect(t, c.Bool("bat"), true) + msg = "foobar" } app.Run([]string{"command", "--foo", "hello world", "--bar", "245", "--bat"}) + expect(t, msg, "foobar") } func Test_FlagDefaults(t *testing.T) { + msg := "" app := NewApp() app.Flags = []Flag{ StringFlag{"foo", "default", "a string flag"}, @@ -31,8 +35,10 @@ func Test_FlagDefaults(t *testing.T) { expect(t, c.String("foo"), "default") expect(t, c.Int("bar"), 42) expect(t, c.Bool("bat"), false) + msg = "foobar" } app.Run([]string{"command"}) + expect(t, msg, "foobar") } func TestCommands(t *testing.T) { From 63b9f2823a654645c76e4a86d6fb41b866ee830a Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Fri, 19 Jul 2013 15:10:34 -0700 Subject: [PATCH 3/4] JMS #14: Improved help. Brought help commadn back to commands --- app.go | 6 +++++- help.go | 5 +---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app.go b/app.go index ebbdd4b..974ecd6 100644 --- a/app.go +++ b/app.go @@ -28,14 +28,18 @@ func NewApp() *App { } func (a *App) Run(arguments []string) { + // parse flags set := flagSet(a.Flags) set.Parse(arguments[1:]) + // append help to commands + a.Commands = append(a.Commands, HelpCommand) + context := NewContext(a, set, set) args := context.Args() if len(args) > 0 { name := args[0] - for _, c := range append(a.Commands, HelpCommand) { + for _, c := range a.Commands { if c.HasName(name) { c.Run(context) return diff --git a/help.go b/help.go index ffe5b28..7d5a0c1 100644 --- a/help.go +++ b/help.go @@ -8,10 +8,7 @@ var HelpCommand = Command{ Name: "help", ShortName: "h", Usage: "Shows a list of commands or help for one command", -} - -func init() { - HelpCommand.Action = ShowHelp + Action: ShowHelp, } func ShowHelp(c *Context) { From 580cc011fde9db42be2fcd002d76329a01d16332 Mon Sep 17 00:00:00 2001 From: Jeremy Saenz Date: Fri, 19 Jul 2013 15:23:42 -0700 Subject: [PATCH 4/4] JMS #14: More help cleanup --- app.go | 4 ++-- help.go | 34 ++++++++++++++++------------------ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/app.go b/app.go index 974ecd6..9e540b2 100644 --- a/app.go +++ b/app.go @@ -23,7 +23,7 @@ func NewApp() *App { Name: os.Args[0], Usage: "A new cli application", Version: "0.0.0", - Action: ShowHelp, + Action: helpCommand.Action, } } @@ -33,7 +33,7 @@ func (a *App) Run(arguments []string) { set.Parse(arguments[1:]) // append help to commands - a.Commands = append(a.Commands, HelpCommand) + a.Commands = append(a.Commands, helpCommand) context := NewContext(a, set, set) args := context.Args() diff --git a/help.go b/help.go index 7d5a0c1..39f7d7f 100644 --- a/help.go +++ b/help.go @@ -4,33 +4,31 @@ import "os" import "text/tabwriter" import "text/template" -var HelpCommand = Command{ +var helpCommand = Command{ Name: "help", ShortName: "h", Usage: "Shows a list of commands or help for one command", - Action: ShowHelp, -} - -func ShowHelp(c *Context) { - helpTemplate := `NAME: - {{.Name}} - {{.Usage}} + Action: func(c *Context) { + helpTemplate := `NAME: + {{.Name}} - {{.Usage}} USAGE: - {{.Name}} [global options] command [command options] [arguments...] + {{.Name}} [global options] command [command options] [arguments...] VERSION: - {{.Version}} + {{.Version}} COMMANDS: - {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} - {{end}} -GLOBAL OPTIONS - {{range .Flags}}{{.}} - {{end}} + {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}} + {{end}} +GLOBAL OPTIONS: + {{range .Flags}}{{.}} + {{end}} ` - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - t := template.Must(template.New("help").Parse(helpTemplate)) - t.Execute(w, c.App) - w.Flush() + w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) + t := template.Must(template.New("help").Parse(helpTemplate)) + t.Execute(w, c.App) + w.Flush() + }, }