Liking cljdoc? Tell your friends :D


Setup
API
Changelog
Introduction
Usage
Examples
Glossary
Contact

ReadMoi

A Clojure library for generating a project ReadMe from hiccup/html

Setup

Leiningen/Boot

[com.sagevisuals/readmoi "2"]

Clojure CLI/deps.edn

com.sagevisuals/readmoi {:mvn/version "2"}

Require

(require '[readmoi.core :refer [generate-all]])

Introduction

Software documentation should have lots of examples. But it's kinda a pain to write html or markdown containing code examples. Write some code in the editor, evaluate it, copy and paste it into the ReadMe, back-and-forth. And if the software changes, the examples may no longer be accurate. It sure would be nice if we could write (+ 1 2) directly into the ReadMe, and the document would automatically insert 3 immediately afterwards. And if we ever decide to redefine +, re-generating the document would update all the results.

Developing Clojure is a pleasure because we're writing the code while standing inside the code itself. Markdown and html don't provide that. Plus, my editor is already set up for lisp code structural editing, and I am hesitant to give it up.

Hiccup is a wonderful utility that consumes Clojure code and outputs html. All the benefits of Clojure transfer to authoring html. Code editors can sling around lisp forms with abandon. We have the whole Clojure universe at our disposal. And best of all, we can evaluate code examples, right there in the document itself.

But, GitHub ReadMe documents are generated from markdown files, not hiccup. The ReadMoi library generates html and markdown ReadMe files — with up-to-date, evaluated code examples — from hiccup source.

The resulting ReadMe document is structured exactly as you see here: a Clojars badge, navigation links, one or more html <section>s (Intro, Usage, Glossary, etc.) containing evaluated code examples, a license statement, and a footer with copyright and compilation metadata.

Usage

Overview

We write our document, one .clj file per section. Each section file contains hiccup/html forms with text liberally sprinkled with code examples. Then, we create an options file that tells ReadMoi which section files to load. The options file also contains various…options (see below). Finally, we tell ReadMoi to generate the ReadMe, one markdown file and one html file. Generating the ReadMe files involves processing the hiccup forms, during which the code examples are evaluated and the returned values are inserted immediately next to the Clojure form.

Detailed usage

The following steps assume a Leiningen project.clj file in the project's root directory.

  1. Complete the setup.

  2. Write our ReadMe sections. The format of each section's file is…

    [:section#❬:section-href❭
      ❬hiccup content❭]

    :section-href is the value found in options map. ReadMoi automatically generates navigation links based on those hyperlink references it finds in that map.

    Also, we can show people how to use the software with the following pattern.

    [:pre [:code (print-form-then-eval "(+ 1 2)")]]

    …which gets rendered as…

    (+ 1 2) ;; => 3

    Don't bother inserting the return value. Every time we generate the document, the code is re-evaluated. We can re-write our code examples and quickly see how they'll appear in the document. Also, the code examples stay synchronized as the codebase changes.

    Note: Any definitions (def, defn, etc.) will bind a value to a symbol in that namespace, which is useful and typically what we'd want, but can on occasion, be inconvenient.

    The pretty-printing is delegated to zprint, which has a million and one options. print-form-then-eval provides about four knobs to tweak the line-breaking, which is good enough for most examples in a ReadMe document. See the api documentation for details.

  3. Copy readmoi_options.edn to our project's resources/ directory.

  4. The readmoi_options.edn file assigns all the required information and declares our preferences for optional values. The map contains the following required keys:

    • :sections Vector containing one map for each section of the ReadMe. Each section map — having a one-to-one correspondence with one .clj section files — has the following keys:

      • :section-name The section title (string). Required.

      • :section-href Hyperlink reference, internal or external (string). Required.

      • :section-skip-load? Indicates whether to load section contents from file (boolean). Set to true if external link. For example, the API documentation is on another webpage, so there's no additional section file required to generate the ReadMe document. It's merely a hyperlink to an external webpage. On the other hand, the Usage section is part of this document, so we do need to load the source text to generate the document.

    The following are optional keys:

    • :clojars-badge? Boolean that governs whether to display a Clojars badge. Information used to generate the badge is inferred from project.clj file. Default nil.

    • :copyright-holder String that appears in copyright statement at page footer. Default nil.

    • :fn-map-additions Special :fn-map directives governing how zprint pretty-printer will format a function expression. Defaults to {}.

    • :license-hiccup Hiccup/html forms to replace the default license (MIT license) section.

    • :project-description Alternative project description (string) to use in preference to the project description supplied by defproject in the project.clj file.

    • :project-name-formatted Alternative project name (string) to use in preference to the project name supplied by defproject in the project.clj file.

    • :UUID Version 4 Universally Unique Identifier. Suggestion: eval-and-replace (random-uuid). Default nil.

    • :readme-html-directory Alternative output html directory (string). Include trailing '/'. Defaults to 'doc/'.

    • :readme-html-filename Alternative output html filename (string). Defaults to 'readme.html'.

    • :readme-markdown-directory Alternative output markdown directory (string). Include trailing /. Defaults to '' (i.e., project's root directory).

    • :readme-markdown-filename Alternative output markdown filename (string). Defaults to 'README.md'.

    • :sections-directory Alternative directory to find sections hiccup .clj files. Include trailing '/'. Default resources/readme_sections/.

    • :separator String separating the s-expression and the evaluated result. Defaults to ' => '.

    • :wrap-at Column wrap base condition. Defaults to 80.

  5. Generate the html and markdown files. We could evaluate…

    (generate-all (read-string (slurp "project.clj"))
                  (load-file "resources/readmoi_options.edn"))

    …in whatever namespace we loaded generate-all. Or, we could copy resources/readmoi_generator.clj and evaluate all forms in the namespace (cider command C-c C-k). Some day, I'll make this a command line tool or a Leiningen plugin.

    ReadMoi produces two files. The first is a 'markdown' file that's actually plain old html, abusing the fact that html passes through the markdown converter. By default, this markdown file is written to the project's root directory where GitHub can find and display the ReadMe. We don't need a dedicated markdown converter to view this file; copy it to a GitHub gist and it'll display similarly to when we view it on GitHub. The second file, by default written to the resources/ directory, is a proper html document with a <head>, etc., that is viewable in any browser. We may want to copy over the css file for some minimal styling.

Troubleshooting

If a section's .clj file won't load, check the options map in readmoi_options.edn. The :section-name must correspond to the section's filename.

If a navigation link doesn't work as expected, check that the html section element id in the section's .clj file matches the :section-href in the options map in readmoi_options.edn.

If print-form-then-eval doesn't behave as you'd like, try adjusting the width-fn and width-output parameters first. Then if that doesn't suit, try supplying a function-specific formatting directive in the :fn-map-additions value of the options map in readmoi_options.edn. The zprint pretty-printer has an astronomical amount of settings, but in the end, it just tries to do what its author thinks looks best. Almost all the time it works great. My advice: don't chase perfection, just get it looking pretty good and spend the extra time on editing your prose.

Example ReadMoi documents

Here is some example hiccup/html that might live in a section file named super.clj in the project's resources/readme_sections/ directory.

[:section#super
 [:h3 "Super Awesome Stuff"]
 [:p "Here's how to use " [:code "inc"] "."]
 [:pre [:code (print-form-then-eval "(inc 99)")]]]

Notice that we didn't include the 100 yielded by evaluating (inc 99). During hiccup processing, print-form-then-eval will do that for us, including inserting a separator.

Hiccup extracts id attributes from the thing following an html element's #. In this example, the section element's id is #super.

We include this entry into the :sections map of the options file.

{:sections [{:section-name "Super Awesome Stuff"
             :section-href "super"]}

Notice that the :section-href value in the options map matches the hiccup html element's id attribute. That matching allows the navigation link at the top of the ReadMe to correctly link to the proper section somewhere later in the ReadMe.

After running generate-all, that combination of hiccup/html and options would be rendered in the final ReadMe like this.

Super Awesome Stuff

Here's how to use inc.

(inc 99) ;; => 100

ReadMoi consulted the options file, learned that there was a section called 'super', loaded the contents of the super.clj file, processed the hiccup/html contents of the file — which involved evaluating (inc 99) and then inserting ;; => 100 — and wrote the ReadMe files.

ReadMoi examples from other projects

Speculoos: A data validation library.

fn-in: A data structure handling library.

Glossary

term 1

Term 1 definition...

term 2

Term 2 definition with internal link.


License

This program and the accompanying materials are made available under the terms of the MIT License.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close