- Overview
- Installation
- Install the ox-gfm Emacs package
- Install the gem
- Update Your
Rakefile
- Usage
- Create a skeleton README.org file
- Add proper
#+PROPERTYheaders inREADME.org:rake docs:headers- Output Tables
- Output Values
- Run the Code Blocks in README.org:
rake docs:tangle - Ensure that a Badge is Present in
README.md:rake docs:badge - Export
README.orgtoREADME.md:rake docs:export - Generate Yard Documents:
rake docs:yard - Generate an Overview Comment for the Main gem File:
rake docs:overview
- Configuration
- Development
- Contributing
- License
Overview
One of the more onerous tasks when writing a gem or other github project is maintaining the documentation, and keeping it consistent and up-to-date. One of the better options for writing the README file that gets displayed by github as the main documentation is to write the file in org-mode, README.org the export it to markdown README.md for display by github.
Doing so gives you access to org-mode's code blocks for writing example code to demonstrate the gem or other documents being presented. If you do so, github will render your README.org file in HTML and give you a credible result. However, github cannot handle some org-mode features, and in particular, it will not render #+RESULTS blocks showing the results of code block execution unless you wrap the results in something like a #+begin_example block and manually delete the #+RESULTS markers. Exporting to markdown eliminates that hassle.
This gem contains rake tasks to facilitate the production of documentation in other gems.
It provides tasks for:
- ensuring that proper code block setup is included in
README.orgto facilitate runningrubycode blocks in a session using the current version of the library, - running the code block examples in a
README.orgby invokingemacsclient, - exporting
README.orgto Git-flavored markdown inREADME.md - ensuring a workflow or ci badge is present in the
README.md - generating yard documents for your repo, and
- copying the introductory contents of the README as a leading comment in your main gem library file so it gets picked up as an overview for
riandyri
Installation
Install the ox-gfm Emacs package
The export of README.org to README.md requires the ox-gfm package to be installed in Emacs. If you use use-package, this will suffice:
(use-package ox-gfm)Install the gem
Install the gem and add to the application's Gemfile by executing:
bundle add gem_docsIf bundler is not being used to manage dependencies, install the gem by executing:
gem install gem_docsUpdate Your Rakefile
To use this gem place the following lines in your gem's Rakefile:
require "gem_docs"
GemDocs.installUsage
Create a skeleton README.org file
This is a simple task that creates a bare-bones README.org file to get started with. It does create the file with the name of the gem and other repo detains filled in. If there is already a README.org file, it does nothing.
rake docs:skeletonAdd proper #+PROPERTY headers in README.org: rake docs:headers
Getting emacs code blocks to render well in your README.org takes proper configuration of the code block headers in Emacs.
rake docs:headersBy default, the gem_docs rake docs:headers task will add the following headers to the top of your README.org file. It does nothing if any ruby header args are already present, so remove them if you want these installed.
#+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
#+PROPERTY: header-args:ruby+ :wrap example :session gem_docs_session
#+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require '%n'"
#+PROPERTY: header-args:sh :exports code :eval no
#+PROPERTY: header-args:bash :exports code :eval no
Here's what the ruby headers buy you:
-
:results value: the value of the last expression in the block is rendered as the results of code execution. If you want the output instead for a particular block, just add the block argument:results outputto the code block. -
:colnames no: prevents org from processing the column headers in tables it renders. It is better for you to control column headers, and this setting allows this. -
:hlines yes: prevents org from stripping hlines from tables, which also allows you to control the insertion of hlines in tables. -
:exports both: causes both your code and the results of evaluation to be exported to theREADME.md. Your example blocks should demonstrate the use of the gem, and the reader will want to see both the code and its results. -
:dir "./": causes each code block to execute with your gem's root directory as its current directory. -
:wrap example: this wraps the result of code block evaluation in#+begin_example/#+end_exampleso that the results are displayed literally. -
:session gem_docs_session: causes the code blocks to execute in a continuous session, so that variables set up in one code block are accessible in later code blocks. Without this, you would have to build the code environment anew with each code block which obscures the readability of yourREADME. The session name is set to '<gem_name>_session' automatically, where <gem_name> is the name of your gem. -
:prologue "$:.unshift('./lib') unless $:.first == './lib'; require 'gem_name'":: this prologue gets executed before each code block execution and ensures that the version of the gem library is you current development version; otherwise, your code blocks could be running a version from a prior installation of the gem. The 'gem_name' in the require is set to the name of your gem automatically.
The docs:headers task also turns off evaluation of shell code blocks since these will often be such things as demonstrating the shell commands to install the gem, etc. Of course, you can override this for particular code blocks.
Those headers are in fact what I am using in this README, and here is how they work.
Output Tables
You can build table for org to display in the output by returning an array of arrays, which org-mode renders as a table in the output. You can add an hline to the output table by simply adding nil to the outer array where you want the hline to occur.
result = []
result << ['N', 'exp(N)']
result << nil
0.upto(10) do |n|
result << [n/3.0, Math.exp(n/3.0)]
end
result| N | exp(N) |
|--------------------+--------------------|
| 0.0 | 1.0 |
| 0.3333333333333333 | 1.3956124250860895 |
| 0.6666666666666666 | 1.9477340410546757 |
| 1.0 | 2.718281828459045 |
| 1.3333333333333333 | 3.7936678946831774 |
| 1.6666666666666667 | 5.29449005047003 |
| 2.0 | 7.38905609893065 |
| 2.3333333333333335 | 10.312258501325767 |
| 2.6666666666666665 | 14.391916095149892 |
| 3.0 | 20.085536923187668 |
| 3.3333333333333335 | 28.03162489452614 |
Output Values
Sometimes, however, you just want the result of the code block evaluated without building a table. To do so, just set the block header to :results value raw.
To compute the value of an $1,000 asset gaining 5% continuously compounding interest over four years, you might do this:
rate = 0.05
time = 4
principal = 1000
principal * Math.exp(rate * time)1221.40275816017
Apart from all the convenient markup that org-mode allows, the ability to easily demonstrate your gem's code in this way is the real killer feature of writing your README in org-mode then exporting to markdown.
Run the Code Blocks in README.org: rake docs:tangle
You can invoke emacsclient to run all the example code blocks in your README.org that are set for evaluation:
Note that the tangle task relies on emacsclient to evaluate the code blocks in README.org, so your Emacs init files should start [the Emacs server](info:emacs#Emacs Server) in order to work properly.
I use the following snippet in my Emacs init file:
(require 'server)
(unless (server-running-p)
(message "Starting Emacs server")
(server-start))Then, you can evaluate all the code blocks in your README.org like this:
rake docs:tangleWith the default headers provided by `rake docs:headers`, the buffer is evaluated in a session so that code blocks can build on one another. However, `docs:tangle` kills any existing session buffer before it runs so that each buffer evaluation is independent of earlier runs.
Ensure that a Badge is Present in README.md: rake docs:badge
It is reassuring to consumers of your gem that your gem passes its workflow tests on github. This task checks to see if a "badge" indicating success or failure is present and, if not, inserts one at the top of the README.org such that it will get exported to README.md when rake docs:export is run.
If you want to place the badge somewhere else in you README.org, place the special comment #badge where you want the badge located and the task will place it there.
If there is already a badge present, the task will not modify the README.org file.
Export README.org to README.md: rake docs:export
You can write the README in Emacs org-mode, using all its features including the execution of code blocks, and then export to git-flavored markdown.
If your repo contains both README.org and README.md, github (and gitlab) will render the markdown version.
Github renders markdown better than it renders org files, so this helps with the readability of the README on github. For example, if you write the README in org mode without exporting to markdown, github will not render the #+RESULTS blocks unless you manually delete the #+RESULTS tag from the output. This is tedious and error-prone, so it is best that you write the README in org-mode and export to markdown. That's what this task enables.
Also note that when github renders your README.md, it automatically adds a table of contents, so putting one in the README.org file is redundant. If you want to have one for your own purposes, just set the :noexport tag on it so it doesn't get put into the README.md
rake docs:exportGenerate Yard Documents: rake docs:yard
This task generates a suitable .yardopts file if none exists and then generates yard documents into the gem's doc directory. It also makes sure that yard knows about your README.md file so user's of your gem will be able to get an overview of how to use your gem.
rake docs:yardGenerate an Overview Comment for the Main gem File: rake docs:overview
Gem's typically gather into a central library file all the require's and other setup needed for the gem and the file is given the same name as the gem. For example, this gem uses the file lib/gem_docs.rb for this purpose. Since this lib directory is placed in the user's LOADPATH, a require 'gem_docs' or require '<gemname>' effectively initializes the gem.
By convention the comment immediately above the 'module' definition in your main library file is used by yard and ri as the overview for the gem.
rake docs:overviewThis extracts the "Introduction" section from README.org and makes it the overview comment in the gem's main library file. If it already exists, it replaces it with any newer version of the "Introduction" section, otherwise, it does not change the file.
Configuration
The tasks defined by gem_docs should work out of the box with no configuration. They determine things like the name of the gem, your github user name, etc., by examining files in your repo. If this process gets things wrong, it should be considered a possible bug and reported.
Still, you can configure gem_docs with a configure block in your Rakefile. In this gem, for example, my Rakefile contains this:
require "gem_docs"
GemDocs.install
GemDocs.configure do |c|
c.overview_headings = ["Overview", "Usage"]
endIt tells the docs:overview task to use the "Overview" and "Usage" sections of this README.org as the overview comment. This is the most common reason you may want to add a configure block.
The following are the items that can be configured:
GemDocs.configure do |c|
c.overview_headings = ["Introduction"]
c.headers =
<<~HEADER
#+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
#+PROPERTY: header-args:ruby+ :wrap example :session %n_session :eval yes
#+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require '%n'"
#+PROPERTY: header-args:sh :exports code :eval no
#+PROPERTY: header-args:bash :exports code :eval no
HEADER
c.repo_host = nil
c.repo_name = nil
c.repo_user = nil
c.repo_branch = nil
c.repo_workflow_dir = ".github/workflows"
c.repo_workflow_name = nil
c.badge =
<<~BADGE
#+BEGIN_EXPORT markdown
[](https://github.com/%u/%n/actions/workflows/%w)
#+END_EXPORT
BADGE
end#+BEGIN_EXPORT markdown
[](https://github.com/%u/%n/actions/workflows/%w)
#+END_EXPORT
The items that are set to nil here are populated at runtime by examining files in the gem's directory. In the strings for headers and badge, several markers are available as standins for these runtime values:
- %h: the repo host, i.e., 'github.com', 'gitlab.com', etc.
- %u: the repo's username on the host, such as 'ddoherty03' for me,
- %n: the name of the gem, i.e., 'gem_docs' for this gem,
- %b: the branch of the repo, by default 'master',
- %r: the directory where the repo root is located,
- %w: the name of the workflow to be used in the badge, for example.
Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ddoherty03/gem_docs.
License
The gem is available as open source under the terms of the MIT License.