3

我有一个模板,用户可以上传生成报告。他们可以将特殊标签放入 html 模板中,它将替换为数据库中的数据。快速示例:

<div class="customer-info">
  <h1>{customer_name}</h1>
  <h2>{customer_address_line1}</h2>
  <h2>{customer_address_line2}</h2>
  <h2>{customer_address_city}, {customer_address_state} {customer_address_zip}</h2>
</div>

我有一个控制器来查找客户,然后解析模板并替换令牌。

现在我在控制器中有解析代码,创建了一个胖控制器。不好。

但是我应该把代码移到哪里?模型文件夹?创建一个 Util 文件夹并将其放在那里?

只是不确定 Rails Way 会是什么。

4

2 回答 2

1

我也对此感到好奇,并在这里找到了一个非常相似的讨论。老实说,我认为这取决于有多少解析代码。如果只有很少的几行,那么模型是一个安全的地方。如果它是一个大包,尤其是一个可重复使用的包,那么 /lib/ 文件夹可能是解析本身的更好选择。但是,您绝对应该按照您的建议将其从控制器中删除。

于 2013-07-12T02:48:13.013 回答
0

我同意逻辑不应该在控制器中,但让我们更具体地了解你将如何实现它。

首先,您将模板存储在数据库中的什么位置?它们应该存储在自己的模型中,让我们调用它 CustomerTemplate并赋予属性:Text 类型的模板。

所以现在我们有两种类型的对象,Customers 和 CustomerTemplates。如何在给定模板的情况下呈现客户?老实说,在 CustomerTemplate 模型中拥有一个接收客户并呈现它的函数并不可怕render,但它在你的应用程序中放置了一些严格来说不属于那里的逻辑。您应该将“客户特定的渲染逻辑”与“渲染我的简单自定义模板语言”分开。

因此,让我们为您的自定义语言创建一个简单的模板处理程序,我将给它取个昵称Curly。这个处理程序应该对客户一无所知。它所做的只是接受一个字符串并在 {} 中插入值。这样,如果您想在将来添加新的模板类型——比如说,渲染另一个模型,比如发票——你可以使用相同的模板类型。

Rails 中的模板是响应call并注册到ActionView::Template的类。最简单的例子是Builder

这是一个快速编写的模板处理程序,它呈现卷曲。该 call函数返回一个经过 eval 的字符串,因此该字符串必须是有效的 ruby​​ 代码。字符串 eval 在渲染调用的范围内,因此它可以访问通过{ locals: {} } 渲染选项传入的任何变量。

# In lib/curly_template_handler.rb
class CurlyTemplateHandler
  def self.call(template)
    src = template.source
    """
    r = '#{src}'.gsub(/{([^}]*)}/) { |s|
       local_assigns[$1.to_sym] || s
    }
    raw r
"""
  end
end

确保处理程序已初始化,让我们将其设置为侦听 :curly 类型。

# In config/initializers/register_curly_template.rb
ActionView::Template.register_template_handler(:curly, CurlyTemplateHandler)

我们需要将 lib/ 添加到 autoload_paths 以便加载类:

# config/application.rb
config.autoload_paths += %W(#{config.root}/lib)

最后,我们可以在视图中渲染我们的模板!我在这里嵌入了字符串,但你真的会从一个CustomerTemplate对象中得到它:

<%= render(inline: "<h2>{customer_name}</h2><p>{customer_address}</p>",
           type: :curly,
           locals: { customer_name: @customer.name,
           customer_address: @customer.address }) %>

不要在生产中使用我的示例代码!我遗漏了一堆你需要处理的极端情况,比如清理用户输入。

于 2013-07-12T05:20:26.010 回答