######## Examples ######## As ``luci`` is designed to help with a fairly generic (and weird) pattern, I find it quite hard to describe what problems it can help you with. I figured, showcasing examples might make more sense. Even if -- as you'll probably discover -- some of those examples might seem a bit far-fetched... - Metadata_ - `Extract metadata from any textfile`_ - Templating_ - `Super-basic example`_ - `Using default value for user arg`_ - `Add boilerplate to a project`_ - Download_ - `Include update information into a shell script`_ - Scripting_ - `Executable dictlets`_ Metadata ======== Extract metadata from any textfile ---------------------------------- (...as long as the file's format allows for comments) Say, you've got a bunch of *csv* files that you use for your research project. Now, the all contain 'raw' data, but you might want to 'tag' them with certain metadata fields to be able to process them in groups, or for whatever else reason. You can of course use the filename and a certain folder structure to organize them (which you should do in either case). But that can be fairly limited if there are overlapping categories. So, why not add metadata directly into the data files (provided the file-format you use allows for comments or similar)? In that case ``luci`` can help you extract it. Of course, you could do that anyway and write your own script to get the metadata out. But why would you, now that ``luci`` exists, right? Here's how the csv file (let's call it ``data.csv``) could look (to be honest, I have no idea how to add comments to *csv* files, but let's just assume we can use '#' line-prefixes): .. code-block:: console # lucify: start # author: Markus Binsteiner # date: "2018-03-03" # tags: # - luci # - example date,subject,result 2018-01-01,alice,okish 2018-01-02,tom,gone crazy And here's how to get the metadata out, using '``json``' as output format: .. code-block:: json ❯ luci metadata --format json data.csv { "author": "Markus Binsteiner", "date": "2018-03-03", "tags": [ "luci", "example" ] } You could use that output in a pipeline, possibly with ``jq_`` or in a script. Templating ========== Super-basic example ------------------- Say, we have a `file `_ containing: .. code-block:: none # __luci__: # vars: # - he # - she What he said: {{ he }} Then, she said: {{ she }} Save that to a file named '``who_said_what``'. Users of ``luci`` can query this *dictlet* (this is what we call files that are consumed by ``luci``) now for it's interface: .. code-block:: console ❯ luci template who_said_what --help Usage: luci template who_said_what [OPTIONS] Options: --he TEXT n/a --she TEXT n/a --help Show this message and exit. And, to do the actual templating, we do something like: .. code-block:: console ❯ luci template who_said_what --he "I don't care" --she "I don't care either" What he said: I don't care Then, what she said then: I don't care either This of course is not really useful, but those *dictlets* can become much more complex (using loops, conditional statements, etc.), as ``luci`` uses the powerful jinja2_ as it's templating engine. Using default value for user arg -------------------------------- In the previous example we used the most basic user input options, 2 strings. *luci* supports much more specific options though. For example, say we want to add an optional name for the 2nd person saying stuff. We'd change the *dictlet* like so: .. code-block:: none # __luci__: # vars: # - he # - she # - name: # default: she What he said: {{ he }} Then, {{ name }} said: {{ she }} Now, the ``--help`` options gives us: .. code-block:: console ❯ luci template who_said_what --help Usage: luci template who_said_what_name [OPTIONS] Options: --he TEXT n/a --she TEXT n/a --name TEXT --help Show this message and exit. And we can run it like: .. code-block:: console ❯ luci template who_said_what --he "Wha?" --she "Ya!" --name "Lola" What he said: Wha? Then, Lola said: Ya! As *luci* uses the click_ Python library to dynamically create command-line linterfaces, we can use all of *Click*'s `option features `_ to control the created interface. More details on how to create a cli with *luci* can be found :doc:`here `. Add boilerplate to a project ---------------------------- When developing, often you need to add boilerplate code to a file or project, with just a few strings different between each instance. Perfect use-case for templating. Let's use ``luci`` itself as an example. ``luci`` uses what is called a :doc:`lucifier ` to implement different types of tasks that use dictlet metadata and content as input. Those all inherit from a base class, and need to contain a few attributes and functions. We can easily use a template, combined with user input, to either attach source code describing such a class to a file (or just create a new file). Here's how that particular '`add-lucifier `_' *dictlet* looks like: .. literalinclude:: ../luci/dictlets/add-lucifier :language: python Note how we use click_ - specific parameters ``param_decls`` and ``multiple`` for the ``lucifier.var_names`` variable: .. literalinclude:: ../luci/dictlets/add-lucifier :language: python :lines: 38-48 To output the assembled boilerplate template to *stdout*, all we need to do is: .. code-block:: console ❯ luci template add-lucifier --help Usage: luci template add-lucifier [OPTIONS] Options: -n, --name TEXT the name of the lucifier (without the 'Lucifier'-postfix) [required] --lucifier-help TEXT help string for this lucifier --lucifier-short-help TEXT short help string for this lucifier --lucifier-epilog TEXT epilog string for this lucifier -v, --var-names TEXT names of cli variable names --help Show this message and exit. ❯ luci template add-lucifier --name example --lucifier-short-help "An example lucifier" --lucifier-help "An example lucifier" -v input_var_1 -v input_var_2 class ExampleLucifer(Lucifier): """An example lucifier """ LUCI_EXAMPLE = { "doc": { "short_help": "An example lucifier", # "epilog": "" }, "vars": { "__example__.input_var_1": { "type": str, "required": False, "default": False, "doc": { "help": "" }, ... ... ... Download ======== Include update information into a shell script ---------------------------------------------- Say, we wrote a cool bash-script we are really proud of: .. code-block:: bash #!/usr/bin/bash # # Copyright Markus Binsteiner, 2018 # version 0.1 echo "Awesome!" Now, somebody found a bug in the script. So, we fix that bug, and we release a new version. How do we get that new version to our users easily, given that it's only a shell script, not packaged. And a git checkout of a whole repository doesn't make all that much sense for only one file either? With ``luci``, we can add the remote url of the script in a commented section somewhere, like: .. code-block:: bash #!/usr/bin/env bash # # lucify: start # # url: # default: https://raw.githubusercontent.com/makkus/luci-examples/master/download/awesome/awesome.sh # version: https://raw.githubusercontent.com/makkus/luci-examples/{{ version }}/download/awesome/awesome.sh # # lucify: stop # # Copyright Markus Binsteiner, 2018 # version 0.2 echo "Super awesome!" Then, if one wanted to update the script itself, they'd issue: .. code-block:: console ❯ luci download awesome.sh --replace Downloading: https://raw.githubusercontent.com/makkus/luci-examples/master/download/awesome/awesome.sh Saving file: /home/markus/.local/bin/awesome.sh Done. The ``--replace`` option is necessary, as otherwise the ``download`` command would only print out the content of the remote file. To see all the available options for a particular task, we can use the ``--help`` flag: .. code-block:: console ❯ luci download awesome.sh --help Usage: luci download awesome.sh [OPTIONS] Options: -r, --replace if set, the target file will be overwritten -t, --target PATH if specified, the url content will be saved into the specified file, otherwise the dictlet itself will be replaced --version VERSION the version of the dictlet to download (optional) --help Show this message and exit. Of course, this example only makes limited sense, as we'd have to ask our users to install a Python package (*luci*) just to update a single bash script. Let's ignore that though, shall we? Scripting ========= Executable dictlets ------------------- You can make every dictlet (that is not a script by itself in the first place) executable by setting *luci* as it's interpreter in the she-bang line, and adding the ``__default_lucifier__`` key in the ``__luci__`` metadata: .. code-block:: none #! /usr/bin/env luci # # __luci__: # __default_lucifier__: template # vars: # - he # - she What he said: {{ he }} Then, what she said: {{ she }} Make this file executable (``chmod +x who_said_what``), put it somewhere in your ``$PATH`` and you've got a dynamic text emitter script: .. code-block:: console ❯ who_said_what --help Usage: luci dev_input [OPTIONS] Options: --he TEXT n/a --she TEXT n/a --help Show this message and exit. ❯ who_said_what --he Right --she Ok What he said: Right Then, what she said: Ok .. _click: http://click.pocoo.org/6/ .. _jinja2: http://jinja.pocoo.org