On my rails3 application I want to use redcarpet to handle user's posts and the user comment section. As such I'd like to extend redcarpet to support turning @username into a link to a user on my site. I know redcarpet is written in C but is there anyway easy way to extend it in ruby? How hard would it be to write it in C? Should I just do this outside of redcarpet?
Also I'm intrested in some other extensions of redcarpet that would be shorthand for linking to other models in my app. I'm not sure the syntax yet but I'm guessing it would be similar to how github handles linking to issues.
I found it pretty easy to extend redcarpet's parser in Ruby for my rails 3 app. It wasn't scary at all.
First, start by deriving a class from Redcarpet's HTML renderer and override the preprocess method as recommended in the docs. In Rails 3.2 and Rails 4, this file can go anywhere and you don't need to require it. I use a 'services' folder to hold code like this.
# app/services/my_flavored_markdown.rb
class MyFlavoredMarkdown < Redcarpet::Render::HTML
def preprocess(text)
text
end
end
Next step is to add methods that do text substitutions you want. Here I use regex to wrap text that looks like "@mention" in an HTML span tag with a css class 'mention'.
# app/services/my_flavored_markdown.rb
class MyFlavoredMarkdown < Redcarpet::Render::HTML
def preprocess(text)
wrap_mentions(text)
end
def wrap_mentions(text)
text.gsub! /(^|\s)(@\w+)/ do
"#{$1}<span class='mention'>#{$2}</span>"
end
text
end
end
You could just as easily look up a user's profile page and wrap the @mention in an anchor tag instead. In my case, I also made methods for emoticons and hashtags that worked the same way and chained the methods together.
The last step is to add a helper that accepts some text, creates an instance of your Redcarpet-derived class, passes the text into that for processing, and returns the html result.
# app/helpers/application_helper.rb
def flavored_markdown_to_html(text)
renderer = MyFlavoredMarkdown.new()
# These options might be helpful but are not required
options = {
safe_links_only: true,
no_intra_emphasis: true,
autolink: true
}
Redcarpet::Markdown.new(renderer, options).render(text)
}
In your views you can call it like this:
<%= flavored_markdown_to_html("This is something worth @mentioning") %>
The output would then be:
This is something worth <span class='mention'>@mentioning</span>
.
I once tried to extend redcarpet
, but found it very difficult. If there are no other dependencies on redcarpet
I'd recommend you try rpeg-markdown
which is a (somewhat outdated) Ruby gem providing bindings to the excellent peg-markdown.
peg-markdown
is a markdown interpreter written as a formal grammar. This means that it is very easy to extend it with own syntax. I've successfully extended peg-markdown
for my own projects (see my fork here) and I found it to be much simpler than fiddling with redcarpet
's custom parser code.
I also found peg-markdown
to have fewer bugs.
The Ruby bindings may have to be made current by updating the git submodule. (I'm planning to submit a pull request to update rpeg-markdown
to the latest version of peg-markdown
.)