Markdown blockquote attribution with Redcarpet

For a recent Facebook Stories post, we were asked to allow quotes to be attributed to a person. The Stories site uses Markdown for text formatting which does not have a method for blockquote attribution within its syntax. To add support I mixed some regex and Ruby on Rails together.

The HTML5 Doctor mentions <cite> as a way to attribute blockquotes. In Markdown I thought it would be nice to be able to do something like:
> this is my blockquote -- this is my attribution

On Facebook Stories we use Redcarpet for Markdown processing and in a quick read of its documentation I couldn’t see an obvious way to add a new rule to spot some new syntax and generate the <cite> I wanted. There is a link entitled “How to extend the Redcarpet 2 Markdown library“, though frustratingly, the page made little sense to me. I should point out here that I’m a FE dev, happy in my world of CSS, HTML, and Javascript; I view Ruby on Rails with a little trepidation. I’m OK editing views and adding simple things to routes and controllers but I’m always convinced that I’ll add some massive security flaw. Or, that I might somehow make the site and server totally inoperable and I’ll have to ring technology director, Luis Lavena, and tell him I’ve broken it all (again).

Lately I’ve been trying to shift this fear and be more useful with application integration and so I read the documentation again. You can override methods with your own method but I wasn’t drawn to trying this as a <blockquote> is a container for nested elements and I didn’t want to mess up that logic. Handily you can hijack the pre and post process call backs and with some regex you can search for your own hooks and add markup accordingly:

class StoryMarkdown::Render < Redcarpet::Render::HTML
  def cite(text)
    # allow attributes, starting with a --
    text.gsub! /(-- )(.*)\r/ do
      "<cite>#{$2}</cite>"
    end
    text
  end
  alias_method :preprocess, :cite

  def cite_html_clean(text)
    # we want attributions to be block level and not live inside paragraphs
    text.gsub! /(<cite>.*<\/cite>)(<\/p>)/ do
      "#{$2}#{$1}"
    end
    text
  end
  alias_method :postprocess, :cite_html_clean
end

On preprocess, it looks for strings starting with -- and ending with a carriage return and wraps them inside <cite> tags. This generates something like:

<blockquote>
  <p>This is my blockquote<cite>this is my attribution</cite></p>
</blockquote>

Markdown wraps text inside blockquotes in <p> tags automagically and I want the <cite> to sit outside these. To do this, on postprocess it looks for <cite> blocks that are next to a closing <p> tag and using regex capture groups, it moves the <cite> out of the <p> tag. This generates something like:

<blockquote>
  <p>This is my blockquote;</p>
  <cite>this is my attribution</cite>
</blockquote>

Et voilà.

Regex is awesome and I'm enjoying poking more into Ruby on Rails apps; I wrote my first rake task to re-process some images on a site without nuking the whole internet after updating this Markdown renderer. Although it took me 30 minutes of questioning myself before daring to run it on the staging server. And then another 30 minutes to work out how to run it on the staging server. As a BE dev, I have some way to go yet...