Skip to content

Declarative CLI

Tackle files can be turned into a self documenting CLI by wrapping business logic with declarative hooks which also serve as interfaces to calling other tackle hooks / providers. This document shows the semantics of this pattern.

Example tackle.yaml

<-:
  help: This is the default hook
  target:
    type: str
    description: The thing to say hello to
  exec<-:
    greeting->: select Who to greet? --choices ['world','universe']
    hi->: greeter --target {{greeting}}
  greeting-method<-:
    help: A method that greets
    # ... Greeting options / logic
greeter<-:
  help: A reusable greeter object
  target: str
  exec<-:
    hi->: print Hello {{target}}

Running tackle help in the same directory as the file above produces its own help screen.

usage: tackle hello.yaml [--target]

This is the default hook

options:
    --target [str] The thing to say hello to
methods:
    greeting-method     A method that greets
    greeter     A reusable greeter object

Default Hook

When calling a tackle provider without any arguments, the parser first calls the default hook which is a key simply with a left facing as shown below.

<-:
  help: This is the default hook
  a_field: str  
  exec<-:
    # Your business logic here
    p->: print {{a_field}}

Default hooks when run without the help argument may require kwargs, flags. For instance running the above example without any arguments will result in an error such as:

1 validation error for
a_field
  field required (type=value_error.missing)

This is because no default value has been given for the field a_field and so tackle raises an error for that. Check out declarative hook, input fields docs for further information on the options with creating fields.

Default Hook Additional Context

In the event that there is some additional context outside of the default hook, it is parsed after the default hook has been executed. For instance given this tackle file:

<-:
  help: This is the default hook
  a_field: str  
  exec<-:
    foo->: var {{a_field}}
    # Your business logic here
bar->: {{foo}}

When running tackle example.yaml --a_field baz -pf yaml the result is:

foo: baz
bar: baz

Notice how the result of the default hook is made available to be used for rendering the key bar.

Default Hook Methods

Default hooks, like other declarative hooks, can have methods that can be embedded within one another. For given the example from the top, when running tackle example.yaml greeting-method help, we get a help screen like this:

usage: tackle example.yaml greeting-method

A method that greets

Note: Issue #101 updating this

Normal Hooks

When running tackle help, a help screen is not only produced for the default hook but for any other hooks that exist in the public hook namespace.

For instance to call the greeter hook directly from the command line in the top example, one would call:

tackle example.yaml greeter --target foo

In this case, the outer context is not parsed as in the case of the default hook.

Importing Hooks

Any hook defined in the hooks / .hooks directory are made available when calling tackle. So for instance lets say we had the following file in .hooks/some-hooks.yaml:

public<-:
  help: A public hook
  public: foo
private<_:
  help: A private hook
  private: bar

When running tackle example.yaml help, we would get the public hook showing in the help screen and the private not. On the same vein, both hooks can be used from within the example.yaml.

call-public->: public
call-private->: private

Results in the following with the command: tackle example.yaml -pf yaml

call-public:
  stuff: public stuff
call-private:
  things: private things

Future version will also allow you to import hooks from remote locations that along with making python hooks public which are private by default.