How to detect and replace YouTube links by extending the Redcarpet gem

originally shared here on

As I'll explain in a future post, I had an old Wordpress blog that I got sick of maintaining and updating, so I did what any red-blooded developer would do: build my own blog engine.

In doing so, however, I needed to import all my old posts from Wordpress over to my own Rails form. This worked nearly flawlessly, with the exception of my YouTube videos, which looked something like this (without the space inside the tag):

[youtube]nFZZxOHHZlo[/youtube ]

Because I am using the Redcarpet gem, I thought I'd extend its parsing capabilities to detect anything like this and wrap it in a responsive frame.

Here's how I did it (with extreme thanks to this SO post):

Step 1: Create a folder called app/services, and then create a file called custom_markdown.rb:

class CustomMarkdown < Redcarpet::Render::HTML
  def preprocess(text)
    format_youtube(text)
  end

  def format_youtube(text)
    text.gsub! /\[youtube](.*?)\[\/youtube]/ do
      "<div class='embed-container'><iframe src='https://www.youtube.com/embed/#{$1}' frameborder='0' allowfullscreen></iframe></div>"
    end
    text
  end

end

This code will perform a regular expression to find anything wrapped in [youtube] tags and replace it with a <div> that we can now style with CSS.

Step 2: Add the following SCSS somewhere in your project where it makes sense:

.embed-container { 
  position: relative; 
  padding-bottom: 56.25%; 
  height: 0; 
  overflow: hidden; 
  max-width: 100%; 
  iframe, object, embed { 
    position: absolute; 
    top: 0; 
    left: 0; 
    width: 100%; 
    height: 100%; 
  }
} 

Step 3: Use the custom parser when including Redcarpet in your project:

require './app/services/custom_markdown.rb' # This was necessary for me, might not be necessary for you
@blogs = Blog.all.order("published_at desc")
renderer = CustomMarkdown.new()
@markdown = Redcarpet::Markdown.new(renderer, extensions = {})

Now, anywhere you use those [youtube] tags, you'll get this fancy rendering instead: