Rails 7 Transforms a Ruby Hash into HTML attributes for ERB Interpolation

Before Rails 7, we did stuff like this:

<img src="<%= pet[:image_url] %>", alt="<%= pet[:name] %>", data-breed="<%= pet[:breed] %>", data-color="<%= pet[:color] %>">

which gives:

<img src="http://bit.ly/lily-the-cat" alt="Lilyhammer" data-breed="Ragdoll" data-color="white">

HTML with interpolated ruby courtesy of ERB.

With Rails 7 you can do this:

Using tag helper

<%= tag.img src: pet[:image_url], alt: pet[:name],
  data: { breed: pet[:breed], color: pet[:color] } %>

Using content_tag

<%= content_tag(:img, "", src: pet[:image_url], alt: pet[:name],
  data: { breed: pet[:breed], color: pet[:color] }) %>

Is that better? Maybe better in that you don’t have to so much of the ERB interpolation syntax of opening and closing <%= %>. But we’re now abstracted from the intention of the code, which is to produce an image tag.

Now, the fact that we want an image tag is a dereferencing of the image tag img with tag.img then all that detail. But still all inside <%= %>.

Even worse, with the second example using the content_tag helper, the information that we have an image tag is now a symbol :img, a piece of data for the content_tag. The important fact of what tag we’re using is put on the same conceptual plane as all the other guff the image tag needs. The image tag is lost in the weeds of its own detail.

When reading this code, assume it would be among lots of other similar code, we need to be able to scan though and understand the structure. Image, inside a span, inside a div, etc. Now, with that detail hidden in the weeds, it’s no longer in plain sight.

I think a whole page of HTML rendered this was would be much harder to read than ERB HTML.

Is there a better way? There’s a clue above. It’s implied that one <%= %> is better than many. From there I will infer that zero <%= %> are better than many or even any.

What if we could write this?:

img src: pet[:image_url], alt: pet[:name], 'data-breed': pet[:breed], 'data-color': pet[:color]

Less punctuation, no more ERB interpolation and the image tag is back as a first class concept rather than piece of data! The intention is very clear.

Well… it can be written just like that. With Matestack

We could even model the structure of the data parts more closely resembling their semantics:

img src: pet[:image_url], alt: pet[:name], data: {breed: pet[:breed], color: pet[:color]}

Here’s a real code snippet using Matestack.

div class: 'card shadow-sm border-0 bg-light' do
  img path: image, class: 'w-100' if image.present?
  div class: 'card-body' do
    h5 title if title.present?
    paragraph body, class: 'card-text'

Look at how nice that is to read. And it can be packaged up as a component for later use like:

section class: 'some-section' do
  card image: image, title: title, body: body