cli4clj Version 1.3.2 – New: Embedded CLIs

In this post, I announce cli4clj version 1.3.2 and write a bit about the newly added functionality for creating embedded Command Line Interfaces (CLIs). For those unfamiliar with cli4clj: the aim of cli4clj is to ease the implementation of simple interactive CLIs for Clojure applications.

The original and still primary focus of cli4clj is on implementing interactive CLIs intended to be used in interactive terminals/shells. However, in one of my other applications, I needed an interactive CLI that can be used via network, actually via a Message-oriented Middleware (MoM).

The primary aspect in the CLI via network use case, from the cli4clj perspective, is that cli4clj leverages jline for handling interactive input from a terminal/shell. In the CLI via network use case, however, input cannot be read from an interactive terminal/shell.

In the concrete scenario of using an MoM, I wanted to send commands via messages and receive the output via corresponding reply messages. Furthermore, I wanted to use the existing MoM infrastructure without the need of opening yet another socket. Hence, networked CLIs such as nREPL did not appear as a good match to me.

Furthermore, I wanted to re-use the same map-based approach for configuring the jline-based CLI for the “embedded” CLI. The reason for this is that this will allow to easily switch between a jline-based and an embedded CLI or use both at the same time, e.g., when an application is intended to be used interactively in a terminal and, at the same time, the same functionality should be exposed via network. Moreover, I wanted to make the new “embedded” CLI generic such that it can also be used in other places.

The embedded CLI functionality uses the map-based cli4clj configuration format and creates a “CLI-function” instead of an interactive input. The generated function accepts a string of commands and arguments as input and returns the string result of “executing” the provided input. Thereby, the string result is the output that was produced as a result of executing the supplied command and arguments string.

Below, an example for using the embedded CLI functionality is shown. The example was executed in the cli4clj project in a “lein repl”. In order to illustrate that the output is really returned as string from the “CLI-function” I used the indirection of putting the results in the “ret” var and showed them by printing the value of “ret”.

cli4clj.example=> (use 'cli4clj.cli)
nil
cli4clj.example=> (def cli-opts {:cmds {:print {:fn #(println %)} :divide {:fn (fn [x y] (/ x y))}}})
#'cli4clj.example/cli-opts
cli4clj.example=> (def cli-fn (embedded-cli-fn cli-opts))
#'cli4clj.example/cli-fn
cli4clj.example=> (def ret (cli-fn "print foo"))
#'cli4clj.example/ret
cli4clj.example=> ret
"foo"
cli4clj.example=> (def ret (cli-fn "divide 4 2"))
#'cli4clj.example/ret
cli4clj.example=> ret
"2"
cli4clj.example=> (def ret (cli-fn "divide 4 0"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Divide by zero"
cli4clj.example=> (def ret (cli-fn "bar"))
#'cli4clj.example/ret
cli4clj.example=> ret
"ERROR: Invalid command: \"[bar]\". Please type \"help\" to get an overview of commands."
cli4clj.example=> (def ret (cli-fn "help"))
#'cli4clj.example/ret
cli4clj.example=> ret
"divide\n\n\nhelp [? h]\n\tShow help.\n\tDisplay a help text that lists all available commands including further detailed information about these commands.\n\nprint\n\n\nquit [q]\n\tQuit the CLI.\n\tTerminate and close the command line interface."
cli4clj.example=> (println ret)
divide
help [? h]
 Show help.
 Display a help text that lists all available commands including further detailed information about these commands.
print
quit [q]
 Quit the CLI.
 Terminate and close the command line interface.
nil
cli4clj.example=>

Besides adding the embedded CLI functionality, I also fixed some issues, as you can see in the version number. However, as things usually go with adding new functionality, even though it was tested and implemented following test-driven development, I likely added new bugs as well. So, whenever you spot a problem, just let me know.

I hope cli4clj is useful for you. I am always happy about constructive feedback and comments.

This entry was posted in cli4clj, Libs. and tagged , , . Bookmark the permalink.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.