cli4clj Version 1.2.3 Released

cli4clj is a library for easing the implementation of simple interactive command line interfaces (CLIs) for Clojure applications. In this post, I briefly announce the release of cli4clj version 1.2.3.

The most important changes in version 1.2.3 are:

  • Add support for hiding commands in help and completion.
    Hidden commands are prefixed with “_”.
  • Hide the “enable-trace” command by changing it to “_enable-trace”.
  • Add a hidden “_sleep” command.

For hiding commands, the convention of prefixing hidden commands with “_” was introduced. This means that all commands that begin with an underscore, “_”, will neither be shown in the help nor in the command completion.

The rationale for hiding certain commands is to improve the usability by removing everything a “typical” user does not need. In this process, I also changed the “enable-trace” command to “_enable-trace” in order to hide it from the user.

The “_sleep” command was added as a stopgap solution for testing multi-threaded CLI applications. The current CLI test functionality works very well. However, there is a problem with multi-threaded applications.

Clearly, when a command triggers interaction with another thread that eventually prints a result, a corresponding unit test needs to wait until the processing finished and the result was printed. The CLI “UI thread” however, cannot know how long it has to wait. Thus, I added the “_sleep” command as a simple stopgap solution.

In the following, the code of the newly added unit tests for testing the “_sleep” command is shown for illustrating the problem and the current stopgap “solution”:

(deftest async-cmd-not-finished-test
  (let [cli-opts {:cmds {:async-foo {:fn (fn []
                                           (println "Starting...")
                                           (let [tmp-out *out*]
                                             (doto
                                               (Thread.
                                                 (fn []
                                                   (binding [*out* tmp-out]
                                                     (sleep 500)
                                                     (println "Finished."))))
                                               (.start)))
                                           "Started.")}}}
        test-cmd-input ["async-foo"]
        out-string (test-cli-stdout #(start-cli cli-opts) test-cmd-input)]
    (is (= (expected-string ["Starting..." "\"Started.\""]) out-string))))

(deftest async-cmd-sleep-finished-test
  (let [cli-opts {:cmds {:async-foo {:fn (fn []
                                           (println "Starting...")
                                           (let [tmp-out *out*]
                                             (doto
                                               (Thread.
                                                 (fn []
                                                   (binding [*out* tmp-out]
                                                     (sleep 500)
                                                     (println "Finished."))))
                                               (.start)))
                                           "Started.")}}}
        test-cmd-input ["async-foo" "_sleep 1000"]
        out-string (test-cli-stdout #(start-cli cli-opts) test-cmd-input)]
    (is (= (expected-string ["Starting..." "\"Started.\"" "Finished."]) out-string))))

Actually, using sleeps for this is not really an ideal solution. However, for now, this approach serves its purpose. In future, I may come up with something better that still allows me to keep cli4clj simple.

As usual, constructive criticism, feedback, and comments are always appreciated.

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

1 Response to cli4clj Version 1.2.3 Released

  1. Pingback: cli4clj 1.2.5 – Improved Testability of Multi-threaded Command Line Applications in Clojure | ruedigergad

Leave a comment

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