Operability

sandal edited this page Dec 14, 2011 · 13 revisions

In order for a software product to be useful, users need enough control and flexibility to adapt it for their own purposes. This is not to say that the software must anticipate every possible need of its users and provide a high-level, well polished feature that meets that need. Instead, it simply means that whenever it is beneficial to do so, software should its own low level underpinnings so that they can be used to extend the system with new functionality. A good example of a tool that does this well is the RedCarpet 2 markdown processor.

While most markdown processors for Ruby typically expose just a single method which converts Markdown text into HTML, RedCarpet 2 exposes a low level API for building custom renderers. In its README, it shows how this functionality could be used for running pre-formatted text through a syntax highlighter:

class HTMLwithAlbino < Redcarpet::Render::HTML
  def block_code(code, language)
    Albino.safe_colorize(code, language)
  end
end

markdown = Redcarpet::Markdown.new(HTMLwithAlbino, 
                                  :fenced_code_blocks => true)

Because the HTMLwithAlbino class is a subclass of the standard Redcarpet::Render::HTML class, this newly minted renderer acts pretty much like a standard HTML renderer with just a tiny bit of new functionality mixed in. However, RedCarpet allows you to go a lot farther than this through it's Redcarpet::Render::Base class which is essentially a blank-slate renderer object that provides extension points for the following methods:

block_code(code, language)
block_quote(quote)
block_html(raw_html)
header(text, header_level)
hrule()
list(contents, list_type)
list_item(text, list_type)
paragraph(text)
table(header, body)
table_row(content)
table_cell(content, alignment)
autolink(link, link_type)
codespan(code)
double_emphasis(text)
emphasis(text)
image(link, title, alt_text)
linebreak()
link(link, title, content)
raw_html(raw_html)
triple_emphasis(text)
strikethrough(text)
superscript(text)
entity(text)
normal_text(text)
doc_header()
doc_footer()
preprocess(full_document)
postprocess(full_document)

By implementing some or all of these hooks, you can build markdown renderers that take you far afield from the typical HTML based use case. This functionality is currently being used in my experimental eBook formatting library Bookie, which converts markdown formatted documents into PDF, EPub, and MOBI output. While this is currently a work in progress, a patch from Mendicant University student Gregory Parkhurst has proven the concept, and may be worth looking at if you want to see how a custom RedCarpet renderer looks in the wild. It is rough around the edges, but much better than the hand rolled regex based parser I built originally when I couldn't find a markdown library that was easy enough to warp to my own purposes.

The lesson that can be learned here is that without too much effort, your high level functionality in your system can be built on top of well polished and highly organized lower level foundations. This will not only make your software easier to maintain, but will improve its operability so that users have more power to stretch it to meet their needs.


Turn the page if you're taking the linear tour, or feel free to jump around via the sidebar.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.