Include Erb Delimiters Inside of a String in an Erb Block

Include ERB delimiters inside of a string in an ERB block

If I'm understanding you right, your real problem is that heredocs behave like double quotes as far as interpolation is concerned. So all you need is a quoting mechanism that behaves like single quotes. Ruby has lots of string quoting mechanisms, in particular we have %q{...}:

<% code = %q{
<div>
#{ image_tag 'image.png' }
</div>
} %>

You can use other delimiters if you'd like: %q|...|, %q(...), etc. There's still a change of course but at least you don't have to worry about interpolation problems.

If you really want to use a heredoc, you can specify the heredoc terminator with quotes and the corresponding quoting style will apply to the content:

<% code = <<'PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR'
<div>
#{ image_tag 'image.png' }
</div>
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%>

The single quotes in <<'PLACE...' specify that single quoting rules (i.e. no interpolation) apply to the heredoc's content.


Of course none of that stuff will work with embedded ERB like this:

<% code = %q{
<div>
<% ... %>
</div>
} %>

because the ERB parser will see the first %> as the closing delimiter for the outer <% code... part. Fear not, I think I have a plan that will work without involving gross hacks or too much work.

Some preliminaries:

  • Rails uses Erubis for ERB processing.
  • Erubis allows you to change the delimiters with the :pattern option to its constructor.
  • Rails uses Tilt and Sprockets to handle the template processing pipeline, these allow you to make the right things happen to pancakes.js.coffee.erb in the right order.

Using the above you can add your own template format that is ERB with a different delimiter and you can have Rails use this new format to handle your "special" sections before the normal ERB processing can make a mess of things.

First you need to hook up Tilt. If you have a look at lib/tilt/erb.rb in your Tilt installation, you'll see the Erubis stuff in Tilt::ErubisTemplate at the bottom. You should be able to subclass Tilt::ErubisTemplate and provide a prepare override that adds, say, a :pattern => '<!--% %-->' option and punts to the superclass. Then register this with Tilt and Sprockets in a Rails initializer with something like this:

Tilt.register(Your::Template::Subclass, 'klerb') # "kl" for "kludge" :)
Rails.application.assets.register_engine('.klerb', Your::Template::Subclass)

Now your application should be able to handle .klerb files with <!--% ... %--> as the template delimiters. And you can also chain your klerb with erb using names like pancakes.html.erb.klerb and the file will go through klerb before the ERB; this means that templates like this (in a file called whatever.html.erb.klerb):

<!--% code = <<PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
<div>
<% image_tag 'image.png' %>
</div>
PLACE_THE_EXAMPLE_CODE_BETWEEN_THESE_TWO_LINES_EXACTLY_AS_YOU_WANT_IT_TO_APPEAR
%-->
<!--%= "code = escape_the_erb_as_needed(%q{#{code}})" %-->
<% do_normal_erb_stuff %>

will do The Right Thing.

You'd need a helper to implement the escape_the_erb_as_needed functionality of course; a little experimentation should help you sort out what needs to be escape and in what way.

All that might look a bit complicated but it is really pretty straight forward. I've added custom template processing steps using Tilt and Sprockets and it turned out to be pretty simple in the end; figuring out which simple things to do took some work but I've already done that work for you:

  1. Tilt::Template subclass, you get this by piggy backing on Tilt::ErubisTemplate.
  2. Register with Tilt by calling Tilt.register.
  3. Register with Sprockets by calling Rails.application.assets.register_engine.
  4. ...
  5. Profit.

erb: Using each_with_index on a link_to id string

Just regular ruby interpolation

id: "flag_#{i}"

How do I yield from an ERB code block without rendering it?

ERB has an internal buffer, which makes using blocks a bit more complicated, as you can see in your code example.

Rails provides a capture method, which allows you to capture a string inside this buffer and return it from a block.

So your helper would become the following:

def make_backwards
capture do
yield.reverse
end
end

ERB String interpolation generating incorrect HTML syntax

This produces:

<div class="product" fav>

No, it doesn't. It produces <div class=product fav>. What you see (via something like "inspect element", correct?) is browser trying to interpret your broken markup as close to html spec as it can.

As noted in other answers, what you should do instead is something like this:

<div class="<%= cls %>">

Rendering ERB in models: no implicit conversion of ERB into String

The best and easiest way to do this is to use JQuery. First, put the ASCII map inside a div with id="ascii-map" in your template. Then switch to the front-end. Once the DOM is fully loaded, you can parse the ASCII map, look for the asterisk, and then wrap it in a span element that has red color defined for its font.

In your CSS:

.red-font {
color: red;
}

Then, some JQuery:

$(document).ready(function() {
var text = $('#ascii-map').html();
var textWithRed = text.replace("*", "<span class='red-font'>*</span>");
$('#ascii-map').html(textWithRed);
});

I test this and confirmed that it works.

Error compiling ERB code from string

“Normal” Eruby doesn’t allow for expressions using <%= to have blocks the way Rails uses them. Rails extends the Erubis Eruby handler to add support for them. Your test is just tring to use Erb directly, so this support isn’t available.

You need to make sure this support is loaded for this to work. I can get get it working with this:

ActionView::Template::Handlers::Erubis.new(template).evaluate(ActionView::Base.new)

I don’t know if this is the best way though. Check the RSpec docs, there may be a better way for testing parts of views like this.

Custom helper with block

Apparently, when using blocks, the equal sign has to be dropped. The following works:

<% wrap_me('span') do %>
Hello
<% end %>


Related Topics



Leave a reply



Submit