DEV Community

Cover image for Working With Markdown in Ruby
Honeybadger Staff for Honeybadger

Posted on • Edited on • Originally published at honeybadger.io

Working With Markdown in Ruby

This article was originally written by Aestimo Kirina on the Honeybadger Developer Blog.

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:

Enter fullscreen mode Exit fullscreen mode


ruby
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:

Enter fullscreen mode Exit fullscreen mode


ruby
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:

Enter fullscreen mode Exit fullscreen mode


html

Why is Ruby awesome?

  • It's fun to use
  • Easy to learn
  • And so much more...

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 ```

```.

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

Enter fullscreen mode Exit fullscreen mode


ruby
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](https://github.com/gettalong/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:

Enter fullscreen mode Exit fullscreen mode


ruby
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:

Enter fullscreen mode Exit fullscreen mode


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


Check out the project's [documenation](https://kramdown.gettalong.org/documentation.html) 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:

Enter fullscreen mode Exit fullscreen mode


markdown

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:

Enter fullscreen mode Exit fullscreen mode


markdown

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:

Enter fullscreen mode Exit fullscreen mode


ruby
gem install sinatra
gem install puma # optional


You can also clone the source code for this example from [here](https://github.com/iamaestimo/sinatra_redcarpet_app).

Switch to the project folder and run ```

bundle install

``` to get a fresh Gemfile.lock file on your development environment.

### The Main Class

Enter fullscreen mode Exit fullscreen mode


ruby
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:

Enter fullscreen mode Exit fullscreen mode


html

Input


### 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.

Enter fullscreen mode Exit fullscreen mode


ruby
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
Enter fullscreen mode Exit fullscreen mode

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](https://github.com/rubychan/coderay) into the mix. The [Rouge](https://github.com/rouge-ruby/rouge) gem could also be used to get the same effects.

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

bundle

```.

Enter fullscreen mode Exit fullscreen mode


ruby

Gemfile

source "https://rubygems.org"

...
gem 'coderay'


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

Enter fullscreen mode Exit fullscreen mode


ruby
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
Enter fullscreen mode Exit fullscreen mode

end

helpers CustomParser

end


Then, we use the ```

convert_markdown

``` method to convert the Markdown our users enter on the frontend form:

Enter fullscreen mode Exit fullscreen mode


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

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

erb :markdown_output, layout: :layout
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode


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!

Top comments (1)

Collapse
 
cicirello profile image
Vincent A. Cicirello

Your post seems like it would be interesting, other than that your code blocks, etc are all out of place, making it nearly impossible to read. A bit of irony that you have markdown bugs in a post about markdown.