1

In this fictive but representative example, we want to create a Rails 7.0 view helper that takes a block, feeds the block a magic word, takes the block's output and produces a link pointing to some path, displaying the block's output as the label.

Here's our non-working approach in the view helper:

def magic_link(&block)
  helpers.link_to('#somepath') do
    block.call('ABRACADABRA')
  end
end

And in the HAML view, we use it as follows:

= magic_link do |magic_word|
  %h1
    The magic word is:
    = magic_word
<a href="#somepath">
<h1>
The magic word is:
ABRACADABRA
</h1>
</a>

However, the actual result is:

<h1>
The magic word is:
ABRACADABRA
</h1>

<a href="#somepath">
<h1>
The magic word is:
ABRACADABRA
</h1>
</a>

As can be seen, the block is rendered twice, likely for the same reason as described in Why is a block from a view rendered twice?

However, due to the need of the link around the text, the solutions described in the stackoverflow question linked above will not work. Returning nil at the end of the block renders only the first occurrence, i.e. the block's result without the link.

A working but undesired solution would be to change the HAML to:

= magic_link do |magic_word|
  - capture do
    %h1
      The magic word is:
      = magic_word

This works because the - tells HAML to discard the result of the capture block, removing the unwanted duplicate. However, this is undesired because we want to keep the helper's interface clean; needing to call capture inside the view in order to use a helper properly is not an option.

How can the helper be rewritten to make the original HAML (without capture) work without producing the unwanted duplicate?

mu is too short
  • 426,620
  • 70
  • 833
  • 800
Kalsan
  • 822
  • 1
  • 8
  • 19

0 Answers0