Words and Code

One writer’s journey from words to code.

Rolling Out the Redcarpet for Rendering Markdown

#technicaltuesdays, markdown, rails, ruby

Sometimes, it’s the little things that matter the most. As cool and complex as your giant application might be, small details like a toggleable button, or beautiful-formatted and styled dropdown select can have the biggest impact on the look and feel of the rest of your application.

Once you’ve built out the skeleton of your application and filled in the backend functionality, the next step is to connect the all the logic with your frontend framework. And once you’ve done that, you start to see all the gaping holes that you need to fix next. One of the applications that I have been working on recently got to that state. Once I ensured that the user-facing page corresponded to the admin panel, it was time to fix all the little things that I had been putting off until we got the application working!

One of those minute details was rendering markdown. While building out the application, we were just using Ruby string primitives in our database to render text. But no one wants to see a long five-paragraph article rendered as a single, huge block of text, right? So, we had to make our text easily editable on the admin panel and renderable — that’s word, right? — on the user-facing side. At first, I had no idea how to go about doing this. But it turns out that (no surprise here!) this problem has been solved before! All we had to do next was figure out how to implement someone else’s solution on our own application. Luckily, the answer to our prayers is as simple as rolling out the redcarpet gem.

Markdown: What is it?

The Markdown language didn’t exist until 2004, when it was created by John Gruber of Daring Fireball blog fame. Unlike markup languages such as HTML or RTF (Rich Text Format), Markdown was created to not have any tags for formatting “instructions” when rendered. Instead, it is structured to be readable without formatting or any tags. Ultimately, the language converts simple text into structurally valid markup HTML (or XHTML).

We interact with Markdown a lot, even though we might not always realize it. Files that end in .md, for example, are written in Markdown, and a lot of blog generators (including octopress and jekyll) use Markdown as their formatting language. In fact, this language is exactly what allows us to write something like this into a code snippet or CMS parser:

1
2
3
4
5
6
7
8
9
# I'm a heading

This is a list:

* thing one
* thing two
* thing three

And here's some *italics* and some **bold**.

which will then be processed by Markdown to render actually this HTML in our templates:

1
2
3
4
5
6
7
8
9
10
11
<h1>I'm a heading</h1>

<p>This is a list:</p>

<ul>
  <li>thing one</li>
  <li>thing two</li>
  <li>thing three</li>
</ul>

<p>And here's some <em>italics</em> and some <strong>bold</strong>.</p>

Not only is that incredibly cool, but it’s also a powerful templating tool! There’s one slight problem, however: our templates don’t look anything like this. We have neither any Markdown, nor any HTML! Instead, we’re just rendering long strings (saved in our Postgres database as text attributes) that look like this:

Here's a news article about Harper Lee's new novel, Go Set A Watchman, but it has no formatting at all...

So, how do we get our text to look like the beautiful markup from above? Why, by using a special Markdown gem, of course!

HTML All Of The Things

Markdown processig in Ruby is made especially easy thanks to a library called redcarpet, created by a Github developer named Vicent Marti (thanks, Vicent!). The library is available as a gem, and its parser doesn’t require any additional installed libraries aside from Ruby itself.

Let’s get set up by adding it to our Gemfile:

gem "redcarpet"

and bundling up this gem:

$ bundle install

Great. Now we need figure out how to recarpet our application using this library! The first thing I like to do whenever I’m working with a library or gem that’s completely new to me is decipher what exactly I’m working with here. Thankfully, the redcarpet documentation is really fantastic, and they answer this question almost immediately:

“The core of the Redcarpet library is the Redcarpet::Markdown class. Each instance of the class is attached to a Renderer object; the Markdown class performs parsing of a document and uses the attached renderer to generate output.”

Okay…what does this mean, exactly? Well, it’s actually not too complicated. The library has a Redcarpet::Markdown class, which — you guessed it — is going to be responsible for handling the transforming/parsing of Markdown to markup (we’ll be using HTML, in our case). But it’s also attached to a Renderer object, it seems.

An example will help us visualize what exactly is going on here:

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

Interesting! the new instance of our Redcarpet::Markdown class takes two arguments, one of which is our Renderer object. It also seems to take a hash of extensions, but we’ll come back to that in a bit.

We know that we want to pass in a renderer to our Redcarpet::Markdown class…but what are our options? It turns out that there are two built-in renderers that the redcarpet library uses: Redcarpet::Render::HTML and Redcarpet::Render::XHTML.

Protip: the redcarpet renderers are actually implemented in C, which means that they are incredibly performant and so much faster than other Ruby Markdown renderers!

Since we want our articles to render HTML, we’ll stick with the former renderer rather than the latter:

markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)

It’s important to note that what’s actually happening here is that we are creating a new instance of an HTML renderer, and passing that to our Redcarpet::Markdown object. This is crucial to remember because our Redcarpet::Render::HTML can also take its own set of options! When we initialize our HTML renderer, we have the option of passing some flags to it. And then, we pass our HTML renderer to our markdown object, which will actually handle parsing it:

1
2
renderer = Redcarpet::Render::HTML.new(render_options)
markdown = Redcarpet::Markdown.new(renderer, extensions)

We won’t add any options to start, but don’t forget that this is what’s actually happening! Now it’s time for a deep breath; next, we’ll need to actually implement this markdown object in our application!


The first thing we’ll want to do is add our markdown object to the class that will need our Markdown parser. For our Bookstore application, we’ll have a set of news articles that will show up on our homepage when a user signs in. This means that we’ll want our Article objects to be able to implement markdown. Since this is something our entire class should be able to do, we’ll add our markdown object as a class method to our Article model:

1
2
3
4
5
6
7
class Article < ActiveRecord::Base
  class << self
    def markdown
      Redcarpet::Markdown.new(Redcarpet::Render::HTML)
    end
  end
end

This method isn’t doing anything aside from creating a new instance of a Redcarpet::Markdown parser, with a Redcarpet::Render::HTML renderer. Next, we’ll want to add write a migration that will create another column in our database that will save our markdown content:

1
2
3
4
5
class AddMarkdownContentToArticles < ActiveRecord::Migration
  def change
    add_column :articles, :markdown_content, :text
  end
end

The reason that we have two different columns for persisting content is simple: we have two different types of content that we’re dealing with. The content column in the database is what the admins will edit in the admin panel, while the markdown_content column is what will be rendered to the user.

This will be especially evident when we integrate this with our Ember frontend, which would require us to serialize all the data that Rails is passing to the Ember models. In our ArticleSerializer, we are are only passing our markdown_content through to the frontend:

1
2
3
class ArticleSerializer < ApplicationSerializer
  attributes :id, :slug, :title, :markdown_content, :published_at
end

On the other hand, in our Admin::ArticleSerializer, which is hooked up to our admin panel, we are only passing the raw :content, which is our content before is has been parsed through our markdown object:

1
2
3
4
5
module Admin
  class ArticleSerializer < ApplicationSerializer
    attributes :id, :slug, :title, :content, :published_at
  end
end

Nice! We have everything set up, finally. Let’s make sure that we’re actually parsing our content and assigning our markdown_content when our article is saved. We can do this by adding a before_save hook, which will call whatever method we pass it right before our Article model is saved:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Article < ActiveRecord::Base
  class << self
    def markdown
      Redcarpet::Markdown.new(Redcarpet::Render::HTML)
    end
  end

  before_save :assign_markdown_content, if: -> { content_changed? }

  def assign_markdown_content
    assign_attributes({
      markdown_content: self.class.markdown.render(content)
    })
  end
end

Cool! We wrote a assign_markdown_content method that assigns the markdown_content attribute on our Article. And what exactly is it using to assign the markdown_content? Oh, that’s right: our markdown object! We’re calling the markdown class method, and passing the Article’s content to its render method, which means that the Redcarpet::Markdown instance will parse the content into HTML!

We’re also doing a pre-check in our before_save by passing a proc to this callback which will only call the assign_markdown_content method if our content column in the database has changed (isn’t that content_changed? method is a cool ActiveRecord trick?). This means that we’re not assigning attributes on our Article unless we really need to. This is super efficient because it means avoiding unnecessary queries to our database!

Carpeting Options

The last part of this redcarpet magic is rendering our content in our views and adding any options we might need. If we wanted to do this in a simple Rails view, we could just ask the Article object for its markdown_content:

1
2
article
  = @article.markdown_content.html_safe

We might need to call html_safe on this particular column since Rails will try to escape the content automatically. We could do a similar thing in an Ember view as well, using triple handlebars to handle the HTML safe issue.

Now that we finally have everything working, we can add some cool options to our renderer! You’ll remember that the Redcarpet::Markdown class takes a hash of options.

In my opinion, the most helpful and important option to pass in is autolink. It’s super crucial if you don’t know what your content might look like, since it parses links even when they’re not enclosed in < > characters. It will detect autolinks for http, https, and ftp protocols, and it also handles email addresses and links that start with www.

We can pass in this option directly as second arguments after we pass in our Redcarpet::Render::HTML object:

1
2
3
def markdown
  Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true)
end

Other helpful options on the Redcarpet::Render::HTML object include tables, space_after_headers, lax_spacing, and highlight. Of course, just as you can pass in options to the markdown object you can do the same for the renderer:

Redcarpet::Render::HTML.new(hard_wrap: true, safe_links_only: true, escape_html: true)

The escape_html option is particularly interesting because it takes precedence over :no_styles, :no_links, :no_images and :filter_html, which means that any existing html tags will be escaped instead of being removed. I’m also pretty sure that it would escape any cats you try to pass in through params as well…although I haven’t tried that one yet.


tl;dr?

  • The redcarpet gem is an awesome way to handle rendering Markdown in Rails. It creates a new instance of a Markdown object, which takes either a HTML or XHTML renderer object. You can also write your own, custom render! Check out the documentation to learn more on how to do that.
  • Want to see a step-by-step Rails tutorial on redcarpet? Check out this Railscast.
  • Here’s a super detailed blog post on implementing redcarpet, with some great code snippets and examples.