Imagine that you and your colleagues are working on a cool new project at work. Everyone's churning out code and firing on all cylinders, and everything seems to be going well, but then you remember that documentation also needs to get done for the project.

What do you choose? HTML could work, but it feels a bit clunky having to write all those tags. What about word processors or something like Google Docs? Well, these are fine, but for this project, you'd prefer something that's as close to your code as possible.

Thus, what should you use? A perfect (or near-perfect) documentation tool meets the following criteria:

  • Has simple syntax that most of your team can learn to use and thereby more easily contribute to the project's documentation.
  • Is as close to your project's code base as possible.
  • Is readable in raw or rendered format.
  • Has a small file footprint.
  • Is easily managed using git tooling.
  • Can be rendered into popular publishing formats, such as HTML and PDF.

Markdown meets these criteria.

What Is Markdown?

Markdown is a simple markup language with the file extension `.md. It was created by John Gruber and Aaron Swartz in 2004 with the goal of making a format that was readable in its source-code form.

TL;DR

For the purposes of getting our project's documentation done, Markdown is perfect. In this article, you'll learn how to use two Ruby libraries to parse Markdown, and in a later section, we'll integrate one of the libraries with Sinatra to create a simple documentation app. You can clone the example app's source code from here.

Now let's get to it.

Parsing Markdown Using the Redcarpet Gem

Redcarpet is a Ruby library for processing Markdown. It's inbuilt renderers, one for outputting HTML and another for XHTML, are written in C, which makes it a very fast Markdown parser compared to other Ruby libraries.

Installing Redcarpet

If you have Ruby 1.9.2 or later, you can install the latest version (as of writing this article) of Redcarpet by running gem install redcarpet -v 3.3.4.

The heart of the library is its Redcarpet::Markdown class, which is used to parse the Markdown document you feed it and then use its attached renderer to do the HTML output.

It's recommended that you instantiate the Redcarpet::Markdown class once and then reuse it as needed.

Using Redcarpet

Initialize it as follows:

parser = Redcarpet::Markdown.new(renderer, extensions = {})

As you'll notice, the Redcarpet::Markdown class accepts two arguments; the first is the renderer you prefer (for the purposes of this tutorial, we'll default to the HTML renderer Redcarpet::Render::HTML), and the second argument is a hash of options.

Let's start with a simple example using the default renderer without specifying any options:

markdown_text = <<-'TEXT'
Why is Ruby *awesome*?
- It's fun to use
- Easy to learn
- And so much more... 
TEXT

parser.render(markdown_text)

The following will be rendered:

<p>Why is Ruby <em>awesome</em>?</p>
<ul>
    <li>It's fun to use</li>
    <li>Easy to learn</li>
    <li>And so much more...</li>
</ul>

The second argument in Redcarpet's class is a hash that accepts several options. Let's go over a few:

  • :tables - Enables you to parse tables.
  • :autolink - Generates autolinks for HTTP, HTTPS, FTP, and even email.
  • :strikethrough - Lets you parse strikethrough; just use two ~~ to mark where the strikethrough starts.
  • :footnotes - If you want to make a reference in your documentation, a footnote will do. Use a marker next to the text you'd like to reference in the footnote, Footnote worthy text[^1], and then the actual footnote text anywhere in your document using [^1]: Actual footnote.

Using our previous example, here's how to include extensions:

parser.render(markdown_text, tables: true, :footnotes: true)

It’s looking good so far, but Redcarpet packs much more punch under the hood. Later in the tutorial, we'll use the library to build a custom Markdown parser and use it in a Sinatra app.

In the meantime, let's turn our attention to another library, Kramdown.

Kramdown

Kramdown is a pure Ruby parser that can parse several formats, including Markdown, HTML, and GitHub-flavored Markdown (GFM), and convert them into HTML, Kramdown, LaTex, and even PDF.

Installation is as easy as adding the latest version of the gem to your Gemfile, gem 'kramdown', '~> 1.11', '>= 1.11.1', or doing a direct install with gem install kramdown -v 1.11.1 and then using its simple API:

require 'kramdown'

Kramdown::Document.new(text_to_be_converted).to_html

Just like Redcarpet, Kramdown's new call can take two parameters. The first is the text to be converted, and the second is a hash of options, which will affect the output you'll get.

You can specify options like so:

Kramdown::Document.new(source_text, {toc_levels: 1..3})

Check out the project's documenation for more information on Kramdown's advanced features. However, there's something almost every blog post and documentation page requires: a table of contents (ToC).

With Kramdown, you can easily generate one using your document's headers.

The table of contents can be generated as an ordered or unordered list. The example below generates a table of contents with items as an unordered list:

# This header would be ignored by the ToC
{:.no_toc}

* This line is necessary but won't appear in the generated ToC
{:toc}

# H1 header

## H2 header

The one below gives us an ordered list table of contents:

# This header would be ignored by the ToC
{:.no_toc}

1 This line is necessary but won't appear in the generated ToC
{:toc}

# H1 header

## H2 header

Great! We've highlighted two of the most popular Ruby Markdown parsing libraries: Redcarpet and Kramdown.

We'll now use one of these libraries to create something more functional - a simple Ruby and Markdown documentation app for our imaginary team.

Ruby and Markdown Documentation App

To build our simple and fast Markdown documentation app, we'll use the Redcarpet gem within a Sinatra app.

Install Sinatra

Sinatra is a stripped down Ruby framework that makes for a super-fast and flexible scripting tool for all sorts of interesting uses, such as making APIs and scraping spiders.

To get started, make sure to have Sinatra installed:

gem install sinatra
gem install puma # optional

You can also clone the source code for this example from here.

Switch to the project folder and run bundle install to get a fresh Gemfile.lock file on your development environment.

The Main Class

require 'sinatra'
require 'sinatra/reloader' if development?
require_relative './lib/helpers/custom_parser'

class Main < Sinatra::Application

# include custom MD parsing helper
helpers Sinatra::CustomParser

  get '/' do
    erb :index, layout: :layout
  end

  post '/parse_md' do
    input = params[:md_input]
    @input = md_parse(input)
    erb :parse_md, layout: :layout
  end

end

Our main class is the "brains" of the app. It includes a root route, a /markdown_output route where the parsed Markdown can be previewed, and, very importantly, a helper that will do the heavy lifting in terms of parsing Markdown.

Inputting Markdown

We’ll include a simple form on the home page where a user can input some Markdown:

<div class="container">
    <div>
        <h3>Input</h3>
        <form method='POST' action='/markdown_output'>
            <div class="mb-3">
                <textarea rows="10" class="form-control" name="md_input" placeholder="Enter your Markdown here"></textarea>
            </div>
            <input type="submit" class="btn btn-primary" value="Parse Markdown" />
        </form>
    </div>
</div>

Markdown Parsing

Markdown parsing is handled by our special helper, lib/helpers/custom_parser.rb.

It's good to highlight that although it's possible to define everything we need within the main class in Sinatra, separating our helper in this way ensures we have a well-organized app and creates a clean way for us to extend functionality when needed.

require 'sinatra/base'
require 'redcarpet'


module Sinatra
  module CustomParser

    def convert_markdown(input)
      # define basic MD renderer
      renderer = Redcarpet::Render::HTML.new(hard_wrap: true)
      markdown = Redcarpet::Markdown.new(renderer, extensions = {})
      output = markdown.render(input)

      # return parsed output
      output
    end

  end

  helpers CustomParser

end

The custom helper includes a convert_markdown method that takes one argument: the input from the form in the homepage. Within this method, we define a new renderer, which is a basic Redcarpet HTML renderer with just one option for now, hard_wrap: true.

With that, we have everything for our basic Markdown parser.

Now, to wrap up our tutorial, let's say we'd like to be able to include code syntax highlighting as part of our output. How can we do that?

Adding Code Highlighting

For this, let's add the Coderay gem into the mix. The Rouge gem could also be used to get the same effects.

Go ahead and add it to the app's Gemfile and run bundle.

# Gemfile
source "https://rubygems.org"

...
gem 'coderay'

We've modified our custom parser helper to include Coderay as follows:

require 'sinatra/base'
require 'redcarpet'
require 'coderay'

module Sinatra
  module CustomParser

    class Markdownray < Redcarpet::Render::HTML
     def block_code(code, language)
       CodeRay.scan(code, language).div
     end
    end

     def convert_markdown(text)
       rndr = Markdownray.new(filter_html: true, hard_wrap: true)
       options = {
         fenced_code_blocks: true,
         no_intra_emphasis: true,
         autolink: true,
         lax_html_blocks: true
       }
       markdown_to_html = Redcarpet::Markdown.new(rndr, options)
       markdown_to_html.render(text)
     end
  end

  helpers CustomParser

end

Then, we use the convert_markdown method to convert the Markdown our users enter on the frontend form:

post '/output' do
    input = params[:md_input]

    # process the input Markdown using Redcarpet and Coderay
    @output = convert_markdown(input)

    erb :markdown_output, layout: :layout
  end

With that, we now have a simple Ruby app capable of taking in Markdown and parsing it correctly with code highlighting included.

A Quick Note

If you're keen enough, you'll notice we haven't implemented any HTML escaping, which might open you up to malicious code injection attacks. Unlike Rails, which comes with the handy html_escape, Sinatra is a bare-bones framework. However, if you are considering expanding our example tutorial into something more production ready, consider using the excellent Rack::Utils module with its ESCAPE_HTML method and build out a helper to fix that problem.

Conclusion

In this tutorial, we've learned how to process Markdown using two different Ruby libraries and built a small app to demonstrate the possibilities available to you.

There's so much more you could do with the combination of Ruby and Markdown. This is just a start; have fun!

Get the Honeybadger newsletter

Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo
    Aestimo Kirina

    Aestimo is a family guy, Ruby developer, and SaaS enterpreneur. In his free time, he enjoys playing with his kids and spending time outdoors enjoying the sun, running, hiking, or camping.

    More articles by Aestimo Kirina
    An advertisement for Honeybadger that reads 'Turn your logs into events.'

    "Splunk-like querying without having to sell my kidneys? nice"

    That’s a direct quote from someone who just saw Honeybadger Insights. It’s a bit like Papertrail or DataDog—but with just the good parts and a reasonable price tag.

    Best of all, Insights logging is available on our free tier as part of a comprehensive monitoring suite including error tracking, uptime monitoring, status pages, and more.

    Start logging for FREE
    Simple 5-minute setup — No credit card required