Может ли представление Rails указать массив content_for, чтобы и представление, и макет были СУХИМИ?

Предыстория: из-за того, как почтовые клиенты (неправильно) обрабатывают стили, в макете почтовой программы Rails все стили должны быть встроены для каждого тега, поэтому макет получается многословным.

Итак, если у вас есть, например, блок информации, состоящий из 3 абзацев (%p) в строке с одинаковым стилем, этот стиль должен быть применен к каждому из пяти.

# 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.
...

Итак, теперь возьмем случай, когда текст зависит от учетной записи и исходит из представления (вместо того, чтобы быть жестко запрограммированным в макете).

Если это известное максимальное количество абзацев (3 в приведенном выше примере), представление может просто указать 3 блока content_for (:foo, :bar и :foobar), а макет может иметь 3 соответствующих yield ( :foo, :bar и :foobar) следующим образом:

# example layout_yielding
# 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.
...
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. ...

Вопрос. Что делать, если вы хотите передать макету переменное количество абзацев, но при этом макет применяет стиль? Есть ли способ, чтобы представление указывало массив из N элементов content_for, чтобы макет мог просто перебирать их? В частности, что-то вроде этого:

# 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.
...

так что макет может выглядеть так:

#  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]

Простой, но проблематичный способ: что легко сделать, так это иметь в представлении один content_for, содержащий все N абзацев С одинаковыми стилями, повторяющимися N раз, например:

# 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.

но это воняет (как вонючий код), потому что (а) это очень не СУХОЙ и даже хуже (б) теперь встроенные стили распределены между одним макетом и, возможно, десятками представлений, которые используют этот макет... если вы измените «стиль» вам нужно изменить во многих местах или определить стиль в другом месте как строку или константу с именем email_para_style_info в другом месте как переменную.


person jpw    schedule 16.02.2017    source источник


Ответы (1)


Вместо этого создайте и используйте помощник представления.

module EmailHtmlHelper
  def account_message(content = nil, &block)
    content_tag :p, content, style: account_message_styles, &block
  end

  def account_message_styles
    %{
      font-size: 1.2em;
      color: #c0ffee;
    }.tr("\n", '')
  end
end

<%= account_message 'Your account has 2 Foos.' %>

<%= account_message do %>
  A longer, 
  more complex
  <span>message</span>
  <%= link_to 'here', foo_url %>
<% end %>

Или, если вам действительно не нужны стили в помощнике, что является вполне приемлемым местом для них в этом случае, вы можете просто коснуться стилей в своем представлении и использовать их для каждого экземпляра:

<% "color: blue;".tap do |inline_styles| %>
  <%= content_tag :p, 'Status: Open', style: inline_styles %>
  <%= content_tag :p, 'Balance: $100', style: inline_styles %>
<% end %>

Обратите внимание, что использование помощников тегов, таких как content_tag, будет соответствующим образом экранировать содержимое, что означает, что вы избежите проблем со случайным закрытием цитаты.

person coreyward    schedule 16.02.2017
comment
Это надежный подход, но лучше, ИМО, было бы - ЕСЛИ возможно - полное разделение контента и стиля с макетом (только), представляющим таблицу стилей. Вот что я надеюсь, что кто-то может предложить в качестве ответа. Некоторая конструкция Iterable content_for была бы идеальной, если это возможно. - person jpw; 16.02.2017
comment
@jpwynn Я думаю, вы выступаете за меньшее разделение, если ваш первоначальный вопрос является каким-либо указанием. В этом случае я предоставил вам менее элегантный вариант в обновлении. - person coreyward; 16.02.2017