Desertbit/grumble: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
(Created page with "=External= * https://github.com/desertbit/grumble =Internal= * Go Open Source Packages =Overview= =Unit Testing Commands= ==Option 1== <syntaxhighlight lang='go'> command := somepackage.SomeCommand(...) app := grumble.New(&grumble.Config{Name: "test"}) app.AddCommand(command) args := []string{ "somecommand", "--arg1", "test1", "--arg2", "test2", } err := app.RunCommand(args) </syntaxhighlight> ==Option 2== <syntaxhighl...")
 
 
(21 intermediate revisions by the same user not shown)
Line 4: Line 4:
* [[Go_Open_Source_Packages#Overview|Go Open Source Packages]]
* [[Go_Open_Source_Packages#Overview|Go Open Source Packages]]
=Overview=
=Overview=
=Concepts=
==Context==
<code>grumble.Context</code> is passed to a command's <code>Run</code> function as argument. The context contains the values for flags and arguments for one command run instance, and it passed to a [[#Command|command]]'s <code>Run</code> function.
==Flags==
===Flag Types===
==Arguments==
The arguments that don't have a <code>grumble.Default()</code> <code>ArgOption</code> are mandatory and must be specified on command line.
Mandatory arguments not allowed after the optional ones.
===Argument Types===
==Command==
The values for the flags and arguments come via the <code>[[#Context|Context]]</code> instance passed to the command's <code>Run</code> function (<code>func(c *grumble.Context) error</code>).
=Programming Model=
==Define an <tt>app</tt>==
<syntaxhighlight lang='go'>
app := grumble.New(&grumble.Config{
Name:                  "someapp",
Description:          "A grumble app",
HistoryFile:          "/tmp/someapp.history",
Prompt:                "smapp> ",
PromptColor:          color.New(color.FgBlue, color.Bold),
HelpHeadlineColor:    color.New(color.FgBlack, color.Bold),
HelpHeadlineUnderline: true,
HelpSubCommands:      true,
Flags: func(f *grumble.Flags) {
// App-level flags
},
})
</syntaxhighlight>
<font color=darkkhaki>TODO: more about app-level flags.</font>
==Define a Number of Commands==
<syntaxhighlight lang='go'>
gc := &grumble.Command{
Name:    "create",
Help:    "creates a widget.",
LongHelp: "creates a widget with the specified color, size. The widget is disabled by default.",
Aliases:  []string{"c"},
Flags: func(f *grumble.Flags) {
f.String("c", "color", "blue", "A color")
f.Int("s", "size", 10, "A size")
f.Bool("e", "enabled", false, "Whether is enabled or disabled by default")
},
Args: func(a *grumble.Args) {
a.String("arg1", "First string arg") // no Default, mandatory argument
a.Int("arg2", "Second int arg", grumble.Default(20))
a.Bool("arg3", "Third bool arg", grumble.Default(true))
},
Run: func(c *grumble.Context) error {
clr := c.Flags.String("color")
size := c.Flags.Int("size")
enabled := c.Flags.Bool("enabled")
arg1 := c.Args.String("arg1")
arg2 := c.Args.Int("arg2")
arg3 := c.Args.Bool("arg3")
_, err := c.App.Printf("creating a %s widget of size %d, enabled: %t [%v, %v, %v] ...\n", clr, size, enabled, arg1, arg2, arg3)
if err != nil {
return err
}
return nil
},
}
</syntaxhighlight>
==Add Commands to <tt>app</tt>==
<syntaxhighlight lang='go'>
app.AddCommand(gc)
</syntaxhighlight>
==Run the <tt>app</tt>==
To handle the errors by yourself:
<syntaxhighlight lang='go'>
err := app.Run()
</syntaxhighlight>
To have the errors handled for you:
<syntaxhighlight lang='go'>
grumble.Main(app)
</syntaxhighlight>
==Run an Individual Command Programmatically==
<syntaxhighlight lang='go'>
app.RunCommand([]string{"somecommand", "--someflag", "some value")})
</syntaxhighlight>
=Unit Testing Commands=
=Unit Testing Commands=
These options do not capture the command output, just side effects:


==Option 1==
==Option 1==
Line 21: Line 115:


==Option 2==
==Option 2==
In this case, a flag '''must''' be registered, even if it has a zero value, otherwise the attempt to get the flag will panic the runtime.


<syntaxhighlight lang='go'>
<syntaxhighlight lang='go'>
flags := map[string]*grumble.FlagMapItem{
flags := map[string]*grumble.FlagMapItem{
"arg1":    flagValue(arg1),
"arg1":    return &grumble.FlagMapItem{Value: arg1},
"arg2":    flagValue(arg2),
"arg2":    return &grumble.FlagMapItem{Value: arg2},
}
}
ctx := &grumble.Context{
ctx := &grumble.Context{
Line 34: Line 130:
err := command.Run(ctx)
err := command.Run(ctx)
</syntaxhighlight>
</syntaxhighlight>
==Option 3==
The test fixture added to the devicecompute repository.
==Capture and Test Output==

Latest revision as of 05:32, 23 November 2024

External

Internal

Overview

Concepts

Context

grumble.Context is passed to a command's Run function as argument. The context contains the values for flags and arguments for one command run instance, and it passed to a command's Run function.

Flags

Flag Types

Arguments

The arguments that don't have a grumble.Default() ArgOption are mandatory and must be specified on command line.

Mandatory arguments not allowed after the optional ones.

Argument Types

Command

The values for the flags and arguments come via the Context instance passed to the command's Run function (func(c *grumble.Context) error).

Programming Model

Define an app

app := grumble.New(&grumble.Config{
	Name:                  "someapp",
	Description:           "A grumble app",
	HistoryFile:           "/tmp/someapp.history",
	Prompt:                "smapp> ",
	PromptColor:           color.New(color.FgBlue, color.Bold),
	HelpHeadlineColor:     color.New(color.FgBlack, color.Bold),
	HelpHeadlineUnderline: true,
	HelpSubCommands:       true,
	Flags: func(f *grumble.Flags) {
		// App-level flags
	},
})

TODO: more about app-level flags.

Define a Number of Commands

gc := &grumble.Command{
	Name:     "create",
	Help:     "creates a widget.",
	LongHelp: "creates a widget with the specified color, size. The widget is disabled by default.",
	Aliases:  []string{"c"},
	Flags: func(f *grumble.Flags) {
		f.String("c", "color", "blue", "A color")
		f.Int("s", "size", 10, "A size")
		f.Bool("e", "enabled", false, "Whether is enabled or disabled by default")
	},
	Args: func(a *grumble.Args) {
		a.String("arg1", "First string arg") // no Default, mandatory argument
		a.Int("arg2", "Second int arg", grumble.Default(20))
		a.Bool("arg3", "Third bool arg", grumble.Default(true))
	},
	Run: func(c *grumble.Context) error {
		clr := c.Flags.String("color")
		size := c.Flags.Int("size")
		enabled := c.Flags.Bool("enabled")
		arg1 := c.Args.String("arg1")
		arg2 := c.Args.Int("arg2")
		arg3 := c.Args.Bool("arg3")
		_, err := c.App.Printf("creating a %s widget of size %d, enabled: %t [%v, %v, %v] ...\n", clr, size, enabled, arg1, arg2, arg3)
		if err != nil {
			return err
		}
		return nil
	},
}

Add Commands to app

app.AddCommand(gc)

Run the app

To handle the errors by yourself:

err := app.Run()

To have the errors handled for you:

grumble.Main(app)

Run an Individual Command Programmatically

app.RunCommand([]string{"somecommand", "--someflag", "some value")})

Unit Testing Commands

These options do not capture the command output, just side effects:

Option 1

command := somepackage.SomeCommand(...)
app := grumble.New(&grumble.Config{Name: "test"})
app.AddCommand(command)
args := []string{
    "somecommand",
    "--arg1", "test1",
    "--arg2", "test2",
}
err := app.RunCommand(args)

Option 2

In this case, a flag must be registered, even if it has a zero value, otherwise the attempt to get the flag will panic the runtime.

flags := map[string]*grumble.FlagMapItem{
	"arg1":    return &grumble.FlagMapItem{Value: arg1},
	"arg2":    return &grumble.FlagMapItem{Value: arg2},
}
ctx := &grumble.Context{
	Flags:   flags,
	Context: context.Background(),
}
command := somepackage.SomeCommand(...)
err := command.Run(ctx)

Option 3

The test fixture added to the devicecompute repository.

Capture and Test Output