Rails视图是否可以指定content_for数组,因此视图和布局都是DRY?

时间:2017-02-16 03:36:09

标签: ruby-on-rails dry 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个相应的产量(:foo,:bar和:foobar),如下所示:

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

问题:如果要将可变数量的段落传递给布局,但仍然使用布局应用样式,那该怎么办?有没有办法让视图指定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.

但是这很臭(如代码中的臭),因为(a)它非常非DRY甚至更糟糕(b)现在内联样式分布在单个布局和潜在的几十个使用它的视图之间布局...如果你改变"样式"你需要改变很多地方,或者在其他地方将样式定义为一个字符串或常量,在其他地方将email_para_style_info定义为变量。

1 个答案:

答案 0 :(得分:0)

构建并使用视图助手。

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之类的代码帮助程序会适当地转义内容,这意味着您可以避免意外引用关闭的问题。