Working With Markdown in Ruby

This article explores how to use markdown in Ruby. Learn how to use two popular Ruby libraries—Redcarpet and Kramdown—to parse markdown and create a simple documentation app using Redcarpet and Sinatra.

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!

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. 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
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial
    Stop digging through chat logs to find the bug-fix someone mentioned last month. Honeybadger's built-in issue tracker keeps discussion central to each error, so that if it pops up again you'll be able to pick up right where you left off.
    Start free trial
    “Wow — Customers are blown away that I email them so quickly after an error.”
    Chris Patton, Founder of Punchpass.com
    Start free trial