Background: Due to how email clients (mis)handle styles, in a Rails mailer layout, all styles need to be inlined for each tag, so the layout is verbose.
So if you have for example a block of information consisting of 3 paragraphs (%p) in a row with the same style, that style must be applied to each of the five.
# example of hard_coded_mailer_layout.html.haml
...
%p{style: "some very very long style declaration"}
This sentence is about Foo.
%p{style: "the SAME REPEATED very very long style declaration"}
This sentence is about Bar.
%p{style: "yes, again, the SAME repeated long style declaration"}
This sentence is about FooBar.
...
So now take the case where the text is account-specific and comes from the view (instead of being hard-coded into the layout).
If it's a known maximum number of paragraphs (3 in the above example) the view can simply specify 3 content_for blocks (:foo, :bar, and :foobar) and the layout can have 3 corresponding yields (:foo, :bar, and :foobar) like this:
# example layout_yielding_3_blocks.html.haml
...
- if content_for?(:foo)
%p{style: "some very very long style declaration"}
= yield :foo
- if content_for?(:bar)
%p{style: "the SAME REPEATED very very long style declaration"}
= yield :bar
- if content_for?(:foobar)
%p{style: "yes, again, the SAME repeated long style declaration"}
= yield :foobar
...
# corresponding view
- content_for :foo do
Your account has 2 Foos.
- content_for :bar do
Your account has 8 Bars.
- content_for :foobar do
Your account has 0 FooBars.
...
Question: What about when you want to pass a variable number of paragraphs to the layout, but still have the layout apply the styling? Is there a way to have a view specify an array of N content_for elements, so the layout can simply iterate through them? Specifically something like this:
# desired view... is it possible?
- content_for :info[0] do
Your account has 2 Foos.
- content_for :info[1] do
Your account has 8 Bars.
- content_for info[2] do
Your account has 0 FooBars.
...
so that the layout might look like this:
# desired corresponding layout, can something like this be done?
...
- yield(:info).each_with_index do |para, i|
%p{style: "some very very long style declaration"}
= (yield(:info))[i]
The easy, but problematic way: What is easy to do is have in the view a single content_for that contains all N paragraphs WITH their identical styles repeated N times, like this:
# current way of doing it (bad for 2 reasons below)
- content_for :all_info do
%p{style: "some very very long style declaration"}
Your account has 2 Foos.
%p{style: "the SAME REPEATED very very long style declaration"}
Your account has 8 Bars.
%p{style: "yes, again, the SAME repeated long style declaration"}
Your account has 0 FooBars.
but that stinks (as in code-smelly) because (a) it's very non-DRY and even worse (b) now the inlined styles are spread between the single layout and potentially dozens of views that use that layout... if you change the "style" you need to change it lots of places, or define the style elsewhere as a string or constant named email_para_style_info elsewhere as a variable.
Build and use a view helper instead.
Or if you really don't want styles in a helper, which is a perfectly acceptable place for them to be in this case, you can just tap the styles in your view and use them for each instance:
Note that using tag helpers like
content_tagwill escape the content appropriately, which means you avoid issues with accidental quotation closing.