Dictlets¶
The most generic definition of dictlet is: an item that can be queried for metadata. For all practical intends and purposes this definition is silly though, as currently luci only supports dictlets that are text files. I’ll leave that generic definition here to indicate that, in theory, every thing could theoretically be a dictlet: a folder, a url, a jpeg…
But, for now, let’s just assume a dictlet is a text file that is enriched with some metadata that is not necessary for it’s primary purpose.
Development¶
Adding metadata¶
‘Developing’ a dictlet means adding structured metadata to it, in a way that doesn’t affect it’s original function. Practically that means adding comments that luci can read and interpret as a dictionary. luci uses so-called dictlet readers to extract relevant metadata. The only implemented reader at the moment is the YamlDictletReader
, which inherits most of it’s logic from the base class TextFileDictletReader
.
This reader checks a text file for certain marker string, and then extracts the metadata according to a set of rules:
- check if the file contains the
lucify
keyword. if it does not:- if the file doesn’t contain the
lucify
keyword, check if it contains the default ‘root’ key for the lucifier that is used at the moment (most often that keyword is__luci__
, but for example theDownloadLucifier
usesurl
– check the source code of the lucifier in question for details)
- if the file doesn’t contain the
- if neither the
lucify
keyword is found, nor the lucifier specific keyword, luci will read the whole file - now, the file is read in this way:
- store the characters that precede the keyword (on the line that contains it) as prefix (if any)
- read all lines below that keyword into a buffer, removing the prefix for each line (usually those are comment indicators)
- ignore all lines that do not start with the prefix
- stop once the end of the file is reached, or a line contains the two keywords
lucify
andstop
- hand the content of the buffer to the right reader sub-class, which parses the text into a dictionary
So, let’s say we have a csv file (people.csv
) with the following content:
# lucify
# author: Markus Binsteiner
# date: "2018-03-03"
# tags:
# - luci
# - example
date,subject,result
2018-01-01,alice,okish
2018-01-02,tom,gone crazy
Using the luci metadata
lucifier, we would extract the following metadata:
❯ luci metadata text_file_metadata_example.csv
author: Markus Binsteiner
date: '2018-03-03'
tags:
- luci
- example
The reader stops reading, once it doesn’t find a line starting with #
anymore. We can make it stop beforehand though, if we want:
# lucify
# author: Markus Binsteiner
# date: "2018-03-03"
# lucify: stop
# tags:
# - luci
# - example
This would stop after the date key. The ‘:’ character after lucify
is optional.
We can also split up our metadata into two blocks if we want to, getting the same result as above:
# lucify
# author: Markus Binsteiner
# date: "2018-03-03"
date,subject,result
2018-01-01,alice,okish
2018-01-02,tom,gone crazy
# tags:
# - luci
# - example
To control luci behavior like the adding of a command-line interface, we put the metadata relevant to this under the special __luci__
key. Like so:
#! /usr/bin/env luci
#
# __luci__:
# __default_lucifier__: template
# vars:
# - he
# - she
#
What he said: {{ he }}
Then, she said: {{ she }}
The __default_lucifier__
key is optional, but handy if you use the luci she-bang (#! /usr/bin/env luci
) as it tells luci which lucifier to use in this case (unfortunately, it’s not possible to specify more than one argument in a she-bang line). Everything under var
determines how the command-line interface for the dictlet is rendered. As this behavior can be controlled quite detailed, check out the section below and/or the relevant help page.
Debugging¶
Until you get familiar with dictlets, it’s quite likely you get the formatting wrong, miss a whitespace, or use the wrong key-word. In order to help with those issues, you can use both the metadata
and debug
lucifiers.
Let’s assume we have a (very weird) dictlet (called debug_example
) like this:
# lucify: start
#
# defaults:
# default1: value1
# default2: value2
# help_string: "{{:: defaults.default2 ::}} help string"
# user_default: 42
#
# __luci__:
# defaults_luci:
# luci1: "{{:: defaults.default1 ::}}"
# doc:
# help: "{{:: defaults.help_string ::}} for dictlet"
# short_help: "dictlet short help example"
# epilog: "dictlet epilog example"
# vars:
# __user_input__.arg1:
# meta:
# alias: number
# type: list
# required: false
# doc:
# help: {{:: defaults.help_string ::}}
# short_help: {{:: __luci__.defaults_luci ::}}
# cli:
# enabled: true
# option:
# type: int
# multiple: false
# default: {{:: defaults.user_default ::}}
# lucify: stop
And the user said: "{{ __user_input__.arg1 }}"
We can get the resulting metadata, with and without user input using the metadata
lucifier:
❯ luci metadata debug_example
__user_input__:
arg1: 42
defaults:
default1: value1
default2: value2
help_string: value2 help string
user_default: 42
❯ luci metadata debug_example --number 12
__user_input__:
arg1: 12
defaults:
default1: value1
default2: value2
help_string: value2 help string
user_default: 42
This gives us the ‘useable’ metadata we could use for example when doing template
-ing:
❯ luci template debug_example
And the user said: "42"
❯ luci template debug_example --number 12
And the user said: "12"
If we need more information, about what goes on internally, we can use the debug
lucifier. This lucifier comes with a lot of options to control the values to display (``luci debug –help``_). Here’s how to display the variables resulting from user input:
❯ luci debug -mu debug_example --number 12
metadata: user vars:
__user_input__:
arg1: 12
Command-line interface basics¶
Getting a dictlet (called minimal_arg
) to create a command-line interface can be as easy as adding:
# __luci__:
# vars:
# - arg1
# - arg2
This adds two command-line options, named arg1
and arg2
, which both are optional. This is what it looks like when called with the metadata
lucifier:
❯ luci metadata minimal_arg --help
Usage: luci metadata minimal_arg [OPTIONS]
Options:
--arg1 TEXT n/a
--arg2 TEXT n/a
--help Show this message and exit.
User input will go into the keys with the same name:
❯ luci metadata minimal_arg --arg1 value1
arg1: value1
Now we can make one of the arguments mandatory, like so:
# __luci__:
# vars:
# - arg1
# - arg2:
# meta:
# required: true
We use the meta
key to provide generic information about the option. There is another key, cli
which is used for more command-line specific properties of a cli parameter. Let’s call the above dictlet without the required option:
❯ luci metadata minimal_arg --arg1 value1
Usage: luci metadata minimal_arg [OPTIONS]
Error: Missing option "--arg2".
And we can give the other argument a default value:
# __luci__:
# vars:
# arg1:
# meta:
# default: VALUE_1
# arg2:
# meta:
# required: true
Notice how the value of the var
key is a dict now, instead of a list. luci doesn’t really care which you use. Let’s try to call the command again, providing arg2
for a change:
❯ luci metadata minimal_arg --arg2 value2
arg1: VALUE_1
arg2: value2
If you need more fine-grained control, check out the cli specific help page.