With Store
tiny2 Example
By integrated with hedzr/store1, your app can manage its configuration dataset seamlessly and fluently.
In tiny1 example app, A DummyStore will be created by cmdr internally, which receives any accessing requests to app configuration dataset, and discard them.
As above, we adds the integration to Store (hedzr/store), which provides a powerful in-memory hierarchical configuration store.
The multilevel subcommands jump > to was been also added.
hedzr/cmdr-loaders provides a cmdr.Create()-like API (lite.Create()) with further support to TOML and JSON file formats. It will load those configuration files from standard locations compliant with GNU File strandard and Unix File specification, ....
hedzr/cmdr-loaders is similar with it but has more codecs, such as for YAML, for HCL, and so on. This helper module would import more third-party packages into your project.
On the above, lite.PrepareApp() or loaders.PrepareApp() provides more wrapper codes.
All purposes of these helpers are:
- integrated with
store.Store, - integrated with default loaders so the OS env and disk files can be loaded automatically,
- flatten the basis of an app such as app name, author and others.
In total, lite.Create() is first of our recommended list, else are cmdr.Create() or loaders.Create().
When a step by step initializing style is what you desire, please use cmdr.New().
Howto integrate Store and loaders manually
This is a sample code to show how to integrate Store and loaders manually, which is similar with lite.Create() or loaders.Create().
Associate your logics with OnAction handler
The OnAction handler is a callback function that will be executed when the command is invoked. It is a good place to put your business logic.
The normally usage is to attach it to the end of a command building sequence, like this:
We do not recommend to use OnAction on the jump command, because it is a multi-level subcommand. The end users won't realize that jump can be executed directly, as it is not the final command they are interested in.
Instead, it is better to use OnAction on the to command, which is the final command that will be executed.
cmd.Store() vs cmd.Set()
cmd.Store() vs cmd.Set()
Programmatically, you can code with app.Store()/app.Set().
The cmd.Store() returns a subset of app's whole Store, which have a root key pointed to key path app.cmd.jump.to. That means, cmd.Store().MustBool("full") can read and write the state of a command line flag --full.
The cmd.Set() returns the app's whole Store, which equals to app.Set(),So you need a cmd.Set().MustBool("app.cmd.jump.to.full") to access the state of --full. Another way is,
The similar call to app.Store() will get a subtree pointed to app.cmd, which stores the values of all of command-line flags. So app.Store().MustBool("jump.to.full") can query the same key above.
For a short and concise calling statement, it's useful by using GetXXX/MustXXX on the object of cmd.Store()/app.Store().
The prefix app.cmd is used for serializing the Option Store, such as YAML, TOML, etc.. The notable point is app will be stripped when you're really serializing it. So a key app.some.path.debug will be stored as some.path.debug in a YAML file:
As a sample, here is a Store's YAML file content:
Build() and `With(cb)
As a Builder Pattern, Build() will end a building sequence in app/b.Cmd("long","short",...)...Build().
But you can always end the building sequence by With(cb) call, the form is app/b.Cmd("long","short",...)...With(cb).
In With(cb) code block of jump, to subcommand was been created and attached to.
Similar with it, inside With(cb) block of to, full option was been attached to.
So a end-user can type tiny2 jump to --full in shell to enable it.
In OnAction(cb) code block, --full enables dumping the internal Store data structure to console. Just like this following,
Operate to Store
In this above tree structure,
app.cmd subtree includes the mapping to the cmdline options
and its final value of tiny2 app.
Also, you can read/write the configuration key and value by
app.Store()/app.Set().
As a shortcut, you can use cmd.Store() and cmd.Set() in
the OnAction handler func.
cmd.Store() will return a reference to its associating subtree
in the whole Store. So cmd.Store().MustBool("full") would
access the final value of full option.
cmd.Set() returns the whole Store, just like app.Set().
Use it you can pass key path as cmd.Set().MustBool("app.cmd.jump.to.full")
to get the value of full option.
app.Set() is like above, a uncut complete store will be given to you.
app.Store() returns the subtree to app.cmd, so you can
inspect the values of cmdline options in a better way.
For example, app.Store().MustBool("jump.to.full") is
whether a end-user input --full at least once at command
line or not.
Accessing an integer value by cmd.MustInt("option-long-name"), or
a time duration value by `cmd.MustDuration("option-long-name"),
and so on.
Load external sources
you may load configuration dataset from external sources, such as a .toml file, a .yaml file, or a remote config center like consul or etcd.
We will discuss it at next section.
App ret-code
The subcommand to shows you how to terminate the app and
exit to OS with a ret code.
To mark a ret code, invoking app.SetSuggestRetCode(retCode).
It will be retrieved at main() function later.
main() will use the code to exit to OS, by invoking os.Exit(app.SuggestRetCode()).
Why so complex?
A incorrect way is exiting at anywhere.
For a service app or anyelse, you should end a program normally with graceful cleanup,
which means, the executing step will end at end of main(). Thus the running go routines,
opened resources, connections will be shut down gracefully.
As a best practise, marking ret code with app.SetSuggestRetCode(retCode),
and returning to main() by return err deeply,
so any cleanup codes has chances to be called,
now you can end the program with os.Exit(app.SuggestRetCode()) finally,
which will notify the ret code to OS.
Footnotes
-
hedzr/storeis a high-performance configure management library. ↩
How is this guide?
Last updated on