40

I want to have some of my partials as markdown snippets. What is the easiest way to render them using the standard rails erb templating?

Ideally, I'd like to do something like this:

If I have a partial in app/views/_my_partial.md.erb:

My awesome view
===============

Look, I can **use** <%= language %>!

which I reference from a view like so:

<%= render "my_partial", :language => "Markdown!" %>

I want to get output that looks like this:

<h1>My awesome view</h1>
<p>Look, I can <strong>use</strong> Markdown!</p>
Jacob
  • 22,785
  • 8
  • 39
  • 55

8 Answers8

85

Turns out, the Right Way (tm) to do this is using ActionView::Template.register_template_handler:

lib/markdown_handler.rb:

require 'rdiscount'

module MarkdownHandler
  def self.erb
    @erb ||= ActionView::Template.registered_template_handler(:erb)
  end

  def self.call(template)
    compiled_source = erb.call(template)
    "RDiscount.new(begin;#{compiled_source};end).to_html"
  end
end

ActionView::Template.register_template_handler :md, MarkdownHandler

If you require 'markdown_handler' in your config/application.rb (or an initializer), then any view or partial can be rendered as Markdown with ERb interpolation using the extension .html.md:

app/views/home/index.html.md:

My awesome view
===============

Look, I can **use** <%= @language %>!

app/controllers/home_controller.rb:

class HomeController < ApplicationController
  def index
    @language = "Markdown"
  end
end
Jacob
  • 22,785
  • 8
  • 39
  • 55
  • 13
    Nice. Let me add, dont forget to add ```gem 'rdiscount'``` to your gemfile and that you can put the handler in the init directory instead and it will just be loaded always. ```config/initializers/markdown_handler.rb``` This means you can skip the change to ```config/applicaiton.rb``` – genkilabs Jul 11 '12 at 17:34
  • The nice thing about this solution is that it works with partials (even if layout is haml) by doing `= raw(render ('editable/intro'))` refering to `/views/editable/_intro.html.md`. Only the `raw` feels hackish, and maybe it raises security issues, but besides it's all good! – Augustin Riedinger Sep 02 '13 at 16:52
  • 2
    Interesting, I suspect this could be alleviated with the addition of a .html_safe to the generated source. The code returned from `erb.call(template)` is already escaped by the ERb rendered, and we're not doing any interpolation here, so it should be fine. – Jacob Oct 05 '13 at 13:38
  • Awesome! Thanks for sharing.. worked like a charm :') – FloatingRock Sep 01 '14 at 09:55
  • @Jacob One problem. I am using this gem: https://github.com/joliss/markdown-rails When I use your code, everything works, but its replaces the code blocks with code snippets. Basically it replaces "```" with "`" – nahtnam Sep 14 '14 at 23:51
  • 10
    If anyone is interested in using red carpet, the only line to change is `"Redcarpet::Markdown.new(Redcarpet::Render::HTML, no_intra_emphasis: true, autolink: true).render(begin;#{compiled_source};end).html_safe"` – elsurudo May 07 '15 at 09:31
  • 2
    This answer is actually undervalued: even the 3 gems *kinda* doing the same thing [maildown](https://github.com/schneems/maildown), [markdown-rails](https://github.com/joliss/markdown-rails), [markerb](https://github.com/plataformatec/markerb) don't achieve such great results. It should be in rails by default I reckon. – Augustin Riedinger Jul 15 '15 at 20:40
  • 1
    Just a heads up to anyone finding this thread and trying to get it to work with Rails 6.0: there was a change to `ActionView::Template.registered_template_handler` and it now requires two arguments, `template` and `source` (instead of just template). I'm working out a way to fix it and if I figure it out, will answer the question [under this question here](https://stackoverflow.com/questions/56715152/using-erb-in-markdown-with-redcarpet) – Rich Jun 24 '19 at 11:45
  • @Rich I came to your solution and it works just fine. However, the return of `erb.call` doesn't show correctly the embed values. For instance `<%= "Hello" %>` is shown as `'.freeze; "hello" @outputbuffer.safeappend='`. Did that happen for you too? – rwehresmann Dec 26 '19 at 02:18
20

Not a pure markdown solution but you can use HAML filters to render markdown, as well as other markup languages.

For example, in app/views/_my_partial.html.haml:

:markdown
  My awesome view
  ===============

  Look, I can **use** #{language}!
rib_ears
  • 3
  • 2
tjwallace
  • 5,528
  • 1
  • 26
  • 15
  • 5
    What I dislike about this approach is it forces an indentation of what should arguably be a pure markdown file. If I want a copy person to be able to control some content I want them to own the entire file and not have to remember to keep everything indented one level. – Chris Nicola Feb 12 '12 at 19:59
  • But then the good thing is that it doesn't load the same functionnalities two times, right? Is `gem 'rdiscount'` still necessary there? Maybe this answer is a good compromise? http://stackoverflow.com/a/8026947/1620081 – Augustin Riedinger Aug 26 '13 at 12:25
4

Piling on the solutions already presented, this is an interpolation-ary way in Rails 3 to render a pure Markdown file in a view from a partial without unnecessary indentation using Haml's :markdown filter and the RDiscount gem. The only catch is that your Markdown file is a Haml file, but that shouldn't matter for someone like a copy person.

In Gemfile:

gem 'rdiscount'

In app/views/my_page.html.haml

:markdown
  #{render 'my_partial', language: 'Markdown!'}

In app/views/_my_partial.html.haml

My awesome view
===============

Look, I can **use** #{language}!

If you didn't need the :language variable passed in to the markdown file, you could do away altogether with your Markdown being a Haml file:

In app/views/my_page.html.haml

:markdown
  #{render 'my_partial.md'}

In app/views/_my_partial.md

My awesome view
===============

Sorry, cannot **use** #{language} here!

Don't like those pesky underscores on your Markdown files?

In app/views/my_page.html.haml

:markdown
  #{render file: 'my_markdown.md'}

In app/views/my_markdown.md

My awesome view
===============

Sorry, cannot **use** #{language} here!
Paul Fioravanti
  • 16,423
  • 7
  • 71
  • 122
4

Have found way not to use haml in such situation.

in views/layouts/_markdown.html.erb

<%= m yield %>

in app/helpers/application_helper.rb

def m(string)
   RDiscount.new(string).to_html.html_safe
end  

in Gemfile

gem 'rdiscount'

So, in view you can call it like:

<%= render :partial => "contract.markdown", :layout => 'layouts/markdown.html.erb' %>

And contract.markdown will be formatted as markdown

Roman Golomidov
  • 191
  • 1
  • 8
4

I just released a markdown-rails gem, which handles .html.md views.

You cannot chain it with Erb though -- it's only for static views and partials. To embed Ruby code, you'd have to use tjwallace's solution with :markdown.

Jo Liss
  • 30,333
  • 19
  • 121
  • 170
2

Leveraged your answer to make a gem to render for GitHub Flavored Markdown in Rails (via HTML::Pipeline): https://github.com/afeld/html_pipeline_rails

Community
  • 1
  • 1
Aidan Feldman
  • 5,205
  • 36
  • 46
  • Thanks, this gem was the perfect solution for me (actually Rails 4). – Andy Waite Aug 03 '14 at 12:43
  • This gem was perfect for Rails 4. Allowed me to put all Markdown content into a single folder and include it where needed via partials. No need to have the entire view Markdown, just the content parts. – Marc Qualie Aug 21 '14 at 12:29
1

Here is a version similar to @Jacob's but using Redcarpet.

module MarkdownHandler
  def self.erb
    @erb ||= ActionView::Template.registered_template_handler(:erb)
  end

  def self.call(template)
    options = {
      fenced_code_blocks:           true,
      smartypants:                  true,
      disable_indented_code_blocks: true,
      prettify:                     true,
      tables:                       true,
      with_toc_data:                true,
      no_intra_emphasis:            true
    }
    @markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
    "#{@markdown.render(template.source).inspect}.html_safe"
  end
end
ActionView::Template.register_template_handler :md, MarkdownHandler

Full credit to lencioni who posted this in this gist.

And if you'd like to evaluate erb:

erb = ERB.new(template.source).result
@markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML, options)
"#{@markdown.render(erb).inspect}.html_safe"
davegson
  • 8,205
  • 4
  • 51
  • 71
wpp
  • 7,093
  • 4
  • 33
  • 65
  • per se, this is a nice addition, but please change your custom class `RedcarpetHeaderFix` to the default `Redcarpet::Render::HTML`. It gave me a headache ;) – davegson Dec 17 '18 at 13:09
0

You can use embedded Markdown in Rails 5. Embedded Markdown is based on the solution provided by Jacob above

  1. Add these to your application's Gemfile:
    gem 'coderay' #optional for Syntax Highlighting
    gem 'redcarpet'
    gem 'emd'
  1. bundle install.

  2. Then create a view app/view/home/changelog.html.md and paste your markdown in that .md file.

  3. Generate a home controller using the following command

    rails generate controller home

  4. Add the following line to your route.rb:

    get '/changelog', :to 'home#changelog'

  5. That's all. Visit http://localhost:3000/changelog to see your rendered markdown

Source: http://github.com/ytbryan/emd

Community
  • 1
  • 1
ytbryan
  • 2,644
  • 31
  • 49