Using ERB in Markdown with Redcarpet - ruby-on-rails

I'm trying to get Markdown to play nicely with .erb. I'd like to use high_voltage to render markdown pages (or normal .html.erb files with markdown partials) that are parsed with Redcarpet and am struggling to get it to all work together.
At the moment I have an initializer called markdown_template_handler.rb that contains the following code:
class MarkdownTemplateHandler
def erb
#erb ||= ActionView::Template.registered_template_handler(:erb)
end
def call(template)
compiled_source = erb.call(template)
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
"#{markdown.render(compiled_source.source).inspect}.html_safe;"
end
end
ActionView::Template.register_template_handler(:md, MarkdownTemplateHandler.new)
However it is failing at line 7, compiled_source = erb.call(template) with the error code saying "wrong number of arguments (given 1, expected 2)"
I looked at the ERB Ruby documentation but from what I understood, the call method is a derivative of the new method, which only requires 1 argument, the text. However, when I tried to use it just in a quick rails console session, it also required two arguments.
When I remove the requirement to parse erb from the above code, everything works as expected, so I don't think it has anything to do with Redcarpet not working.
I am using Rails v6.0.0.rc1 & Ruby v2.5.3p105
Any help is appreciated.
Edit
Further research has led me to finding the Rails 6.0 ERB ActionView template handler. The call method of this handler does indeed require two arguments, the template and the source. That said, in Rails 5.2.3, the ERB Action View template handler call method only requires one argument, the template.
Could someone please point me in the direction of figuring out what source is in this context? There is no documentation for it that I can find.

In this case, source would be passed to the call by ActionView when the handler is called.
You would rewrite your call function like that:
def call(template, source)
compiled_source = erb.call(template, source)
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
"#{markdown.render(compiled_source).inspect}.html_safe;"
end
Before Rails 6, the source value was extracted from template.source, but it is now passed as a separate parameter.

This approach for rendering markdown with ERB in Rails 6 worked well for me. Thanks to Louis-Michel for pointing me in the right direction, including both parameters in the call.
require 'redcarpet'
class MarkdownTemplateHandler
def erb
#erb ||= ActionView::Template.registered_template_handler(:erb)
end
def call(template, source)
compiled_source = erb.call(template, source)
"Redcarpet::Markdown.new(Redcarpet::Render::HTML.new).render(begin;#{compiled_source};end).html_safe"
end
end
ActionView::Template.register_template_handler(:md, MarkdownTemplateHandler.new)

Related

How to use local or instance variable in ruby code in coffeescript in haml template

I'm newer of rails and facing complex issue trying to use variable in ruby helper method in coffeescript in haml template.
Here is my code in haml
:coffee
$('input#field').blur ->
input = $('input#field').val()
#{ ruby_method( input )}
ruby_helper.rb
def ruby_method (arg)
if arg
#model = Model.new(arg)
end
end
This lead a error as below
undefined local variable or method
Also tried instance variable
:coffee
$('input#field').blur ->
#input = $('input#field').val()
#{ ruby_method( #input )}
This can not pass it to helper method.
How can I get javascript variable in ruby helper method?
Thanks in advance!
Coffee Script and Rails engine Relation?
There is no relation between rails and coffee script and nothing in rails can be accessed in coffee script.
What can I do then ?
Just create def.js.erb file in your view folder and write your javascript code. in this file you can access all instance variable in method.
How does it calls method.js.erb ?
use this:
respond_to do |format|
format.js
end
this will call method.js.erb file instead of method.html.erb file.
Hope It helps
The short answer is, you can't. Ruby runs on your server, but any JavaScript (which CoffeeScript emits) you put in your template will run in the browser.
Anything you put inside a coffee: block must be valid CoffeeScript. As others have pointed out in comments, if you want Rails to do something via JS, you need Ajax.
For example, to create a Model with Ajax, set up a controller with a create action and post to it in JS.
For more details, see the excellent Working with JavaScript in Rails tutorial.

Testing a call to an external library in MiniTest

I am working on an application for a blog using Ruby on Rails. I have a model called Essay with a Draper Decorator. I am also using MiniTest::Spec for testing this application. Each Essay has a body which will be stored as Markdown. In the EssayDecorator, I have a method called body which renders the Markdown to html using RedCarpet.
In order to test this method, I wrote the following code:
describe '#body' do
it 'returns html from the markdown' do
essay = FactoryGirl.create(:essay)
#decorated_essay = essay.decorate
markdown = Minitest::Mock.new
#decorated_essay.stub :markdown, markdown do
markdown.expect :render, "<p>Test</p>", [essay.body]
#decorated_essay.send(:body)
markdown.verify
end
end
end
And inside the decorator I have two methods:
def body
markdown.render(model.body).html_safe
end
def markdown
Redcarpet::Markdown.new(Redcarpet::Render::HTML, :autolink => true, :space_after_headers => true)
end
This test passes, but seems weird to me. I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.
Is there a best practice for mocking out this kind of thing in MiniTest? I am pretty new to using Mocks and very new to using MiniTest.
Thanks, in advance.
IMO this code seems weird because it is not testing the behavior of your code, but it is testing the implementation. If the implementation changed (you stored the HTML in a cache instead of running it through Redcarpet) then this test would fail. This looks over-mocked to me.
I don't want to test that RedCarpet is doing its job, I just want to test that I call the render method.
This is implementation. Why are you running the body through markdown? Because you want hyperlinks to be created from URLs? I'd create a test for that. You want to ensure links have the no-follow attribute? I'd create a test for that. Create tests for why the code is doing something, not how it is doing it.
It is also entirely possible you are missing an abstraction in your application. Something that is responsible for formatting the plain text into HTML. Something that uses Redcarpet or RDiscount or any other library deemed important. The EssayDecorator probably shouldn't be responsible for ensuring that the text was formatted properly, but it may be responsible for telling the right object to apply the formatting.

Calling ERB without Rails: undefined method 'raw'

I am using the ERB engine to generate an offline HTML version of a page of my Rails website. The page shows great when shown by Rails, but I have trouble generating with ERB by myself (despite using the same ERB template).
First I was getting the error undefined method 't' and I solved it by replacing all <%=t(...)%> calls with <%=I18n.translate(...)%>.
Now I get undefined method 'raw'. Should I replace all <%=raw(...)%> calls with something else? If yes, what?
raw is defined as helper in actionpack/action_view library so that without rails you can't use it. But ERB templating shows its output without any escaping:
require 'erb'
#person_name = "<script>name</script>"
ERB.new("<%= #person_name %>").result # => "<script>name</script>"
And because of this for purpose of escaping there is ERB::Util#html_escape method
include ERB::Util
ERB.new("<%= h #person_name %>").result # => "<script>name</script>"
While #warhog 's answer will work, the include isn't necessary. It adds all the ERB::Util methods to the current class, which usually isn't desired and can cause unexpected side effects (if you had another h method for example). Instead just access the h method (or other helpers) using the ERB::Util class:
ERB.new("<%= ERB::Util.h #person_name %>").result

BlueCloth isn't working with Rails 3

Is BlueCloth compatible with Rails 3? I can't make it work, maybe someone uses it?
There is supposed to be a helper called 'markdown' available in the views after requiring 'bluecloth', but this doesn't seem to be available.
I'm upgrading an app to rails3 right now and it worked fine for me. I use a helper function called "format" in templates though the code below also provides a markdown function (in rails3 you'll have to use that with raw()). Here's the contents of my [project]/app/helpers/application_helper.rb
module ApplicationHelper
# Format text for display.
def format(text)
sanitize(markdown(text))
end
# Process text with Markdown.
def markdown(text)
BlueCloth::new(text).to_html
end
end
Like a previous poster said, you'll also need
gem 'bluecloth'
in your [project]/Gemfile. My template looks like:
<p><%= format #post.body %></p>
With the markdown function it would be:
<p><%= raw(markdown(#post.body)) %></p>
So I use the format function. Rename the functions however you want.
I've created a fresh Rails 3 app and in the Gemfile I added:
gem 'bluecloth', '>= 2.0.0'
Then opened the console:
ruby-1.8.7-p302 > BlueCloth.new('**hello**').to_html
=> "<p><strong>hello</strong></p>"
So it appears to be working, at least for me.
You could also try Rdiscount which I am not shure but I think is based on the same C library, or at least has similar benchmarks.
You should be more specific in how is it not working: Does it raises an error? Doesn't it renders html? etc...
What you could do, not saying it is pretty, is creating an initializer in your rails project and put the following in it:
require 'bluecloth'
class String
def markdown
BlueCloth.new(self).to_html
end
end
This should enable the markdown method on every string object.
I'd suggest switching to RDiscount over BlueCloth. It's a drop in replacement and is better on all counts.
http://github.com/rtomayko/rdiscount

Possible to embed markdown within erb?

If you use haml as rails view template, you can write portion of your page using markdown by using the ":markdown" filter.
Is is possible to do the same using erb?
It's pretty easy to write a method that does it, assuming you're using something like Rails which has #capture, #concat, and #markdown helpers. Here's an example, using Maruku:
def markdown_filter(&block)
concat(markdown(capture(&block)))
end
Then you can use this as so:
<% markdown_filter do %>
# Title
This is a *paragraph*.
This is **another paragraph**.
<% end %>
There are a few things to note here. First, it's important that all the text in the block have no indentation; you could get around this by figuring out the common indentation of the lines and removing it, but I didn't do that in the example helper above. Second, it uses Rails' #markdown helper, which could easily be implemented in other frameworks as so (replacing Maruku with your Markdown processor of choice):
def markdown(text)
Maruku.new(text).to_html
end
Rails 3 has removed the #markdown helper, so just add the above code in the appropriate helper, substituting the Markdown processor of your choice.
ERB does not have filtering like this built-in. You'll need to directly use a gem that handles it, like RDiscount or the venerable BlueCloth.

Resources