Include ERB delimiters inside of a string in an ERB block

后端 未结 1 1236
再見小時候
再見小時候 2021-01-13 16:59

I am working on a style guide which displays the code, as well as the output. It is currently structured so that the code only needs to be described once, and is displayed i

相关标签:
1条回答
  • 2021-01-13 17:24

    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.
    0 讨论(0)
提交回复
热议问题