I read an evocative post by Magnus Holm entitled Block Helpers in Rails 3, in which he points out that Rails 3 has bent the syntax of ERB too far. (In 'original' ERB, an ERB construct can only span a single statement. Rails 3 allows -- even requires -- spanning multiple statements.)
我读过Magnus Holm在Rails 3中题为“块助手”的一篇唤起人回忆的文章,他在文中指出,Rails 3对ERB的语法的影响太大了。(在“原始”ERB中,一个ERB构造只能跨一个语句。Rails 3允许——甚至需要——跨多个语句。
Which leads me to wonder: what are the real advantages to using ERB over Ruby's native string processing? To dig into this, I took the example listed in the ERB documentation and tried it both in ERB and native Ruby strings. It turns out that Ruby's rich library of string processing makes the translation really simple -- even intuitive.
这让我想知道:使用ERB优于Ruby的本地字符串处理的真正优势是什么?为了深入研究这个问题,我使用了ERB文档中列出的示例,并在ERB和本地Ruby字符串中进行了尝试。事实证明,Ruby丰富的字符串处理库使转换变得非常简单——甚至是直观。
Here's how it looks. Common to both (lifted directly from the ERB documentation):
这是它看起来如何。两者都有共同之处(直接取自ERB文件):
require "erb"
# Build template data class.
class Product
def initialize( code, name, desc, cost )
@code = code
@name = name
@desc = desc
@cost = cost
@features = [ ]
end
def add_feature( feature )
@features << feature
end
# Support templating of member data.
def get_binding
binding
end
end
Here's the template and the expansion written in ERB:
这是用ERB写的模板和扩展:
# ================================================================
# using ERB
erb_template = %{
<html>
<head><title>Ruby Toys -- <%= @name %></title></head>
<body>
<h1><%= @name %> (<%= @code %>)</h1>
<p><%= @desc %></p>
<ul>
<% @features.each do |f| %>
<li><b><%= f %></b></li>
<% end %>
</ul>
<p>
<% if @cost < 10 %>
<b>Only <%= @cost %>!!!</b>
<% else %>
Call for a price, today!
<% end %>
</p>
</body>
</html>
}.gsub(/^ /, '')
rhtml = ERB.new(erb_template)
# Produce results
@r1 = rhtml.result(toy.get_binding)
And here's the template written in pure Ruby:
这是用纯Ruby编写的模板:
# ================================================================
# using native Ruby strings
ruby_template = %q{"
<html>
<head><title>Ruby Toys -- #{ @name }</title></head>
<body>
<h1>#{ @name } (#{ @code })</h1>
<p>#{ @desc }</p>
<ul>
#{ @features.map do |f|
"<li><b>#{f}</b></li>\n"
end.join }
</ul>
<p>
#{ if @cost < 10
"<b>Only #{ @cost }!!!</b>"
else
"Call for a price, today!"
end
}
</p>
</body>
</html>
"}
# Produce results
@r2 = eval(ruby_template, toy.get_binding)
These produce the same results (modulo whitespace). Whether the ERB is simpler or more difficult is really a matter of taste and experience. Judging from the number of questions about ERB and <%= ... %> vs <% ... %> vs <%= ... -%>, it appears that many people might have an easier time sticking to straight Ruby.
它们产生相同的结果(模块化空白)。ERB是更简单还是更困难实际上是一个品味和经验的问题。从ERB和<%=…% >和< %……% >和< % =…-%>,看起来很多人可能更容易坚持使用纯Ruby。
At the risk of starting some sort of holy war, why bother with a ERB when the native Ruby does the same job? Do you think ERB is useful? Should Rails accept "native Ruby" templates as well?
冒着引发某种圣战的风险,当原生Ruby做同样的工作时,为什么还要使用ERB呢?你认为ERB有用吗?Rails也应该接受“原生Ruby”模板吗?
2 个解决方案
#1
4
Magnus Holm here; good to see that you enjoyed the article :-)
马格努斯河中沙洲;很高兴看到你喜欢这篇文章:
First let's have a look at templates (yes, interpolation is a template) as an evolution from constructing a string with regular code:
首先让我们来看看模板(是的,插值是一个模板),它是从用常规代码构造字符串演变而来的:
# "Regular" way of constructing a string:
str = "Hello World: "
@users.each do |user|
str << "How are you today, "
str << user.name
str << "? \n"
end
If we look at this code, it's evident that there are three patterns that repeat themselves:
如果我们看看这段代码,很明显有三种模式重复出现:
- We have plenty of static text which are appended:
str << "Static"
- 我们有大量的静态文本附加在:str < " static "
- We have some dynamic code which are appended:
str << expresstion
- 我们添加了一些动态代码:str < expresstion
- We have some blocks of code which change the control flow:
@users.each
- 我们有一些代码块来更改控制流:@users.each
The great thing about ERB is that it's a perfect isomorphism of these patterns, which means that the user can think of it in terms of constructing the string the regular way. It's simply a way to construct a string with more focus on the static parts (since they are common) than the dynamic parts.
ERB的优点在于它是这些模式的完美同构,这意味着用户可以把它想象成以常规的方式构造字符串。它只是一种构造字符串的方式,它更关注静态部分(因为它们是常见的),而不是动态部分。
As the "purist" you are, you've noticed that this is not the lowest level. That is completely true: As long as you can evaluate Turing complete code, your template engine becomes Turing complete and technically you don't need anything else. You don't need blocks when you can simply nest your template engine.
作为“纯粹主义者”,你已经注意到这并不是最低水平。这是完全正确的:只要您能够评估图灵完整的代码,您的模板引擎就会变成图灵完整,从技术上讲,您不需要任何其他东西。当您可以简单地嵌套模板引擎时,您不需要块。
Now we need think about the difference between low level and easy to understand. Yes, as you remove building parts you end up with a smaller core, but that doesn't necessarily make it easier to understand. You could draw an analogy to GOTOs: You can create any looping/conditionals using one single concept (GOTOs), but it turns out that it's easier to reason about code with if/while-statements. Why? Because a pattern appears! Instead of conceptually parse these patterns every time we look at them, it's easier to create abstractions which we can understand instantly.
现在我们需要考虑低水平和容易理解之间的区别。是的,当你移除建筑部件时,你最终会得到一个更小的核心,但这并不一定使理解变得更容易。您可以对GOTOs进行类比:您可以使用一个概念(GOTOs)创建任何循环/条件语句,但事实证明,使用if/while语句来解释代码更容易。为什么?因为模式出现!当我们每次看到这些模式时,都不会从概念上解析这些模式,而是更容易创建我们能够立即理解的抽象。
In your example, there's one pattern which I believe will be used in any template: @users.map { |f| code }.join
. This is exactly what ERB tries to abstract away so you can forget about the details and reason about your logic instead.
在您的示例中,有一个模式我相信将在任何模板中使用:@users。map {|f| code}.join。这正是ERB试图抽象出来的东西,这样您就可以忘记关于逻辑的细节和原因。
I also believe that if you're making a template engine simpler, but still make it Turing complete, you will abstract away these details anyway. You will realize that a pattern emerges and, as the DRY coder you are, you will start creating helpers and such. You're actually implementing your own little template engine on top of another. Now everything depends on the syntax flexibility of the base language whether or not you manage to abstract away the real details without too much other noise. For instance, there are Lisp flavours which supports static typing and pattern matching, but often it can't beat a syntax designed especially for the problem.
我还相信,如果您使模板引擎更简单,但仍然使图灵完整,那么无论如何您将抽象掉这些细节。您将会意识到一个模式出现了,并且作为一个干编码器,您将开始创建helper类。实际上,您是在另一个模板引擎之上实现您自己的小模板引擎。现在,一切都取决于基本语言的语法灵活性,无论您是否设法在没有太多其他干扰的情况下抽象出真实的细节。例如,有一些Lisp的味道支持静态类型和模式匹配,但是通常它不能打败专门为这个问题设计的语法。
So, what are the real advantages to using ERB over Ruby's native string processing? In one sentence: It gives you nice syntax for common patterns.
那么,与Ruby的本地字符串处理相比,使用ERB的真正好处是什么呢?一句话:它为常见的模式提供了很好的语法。
Do you think ERB is useful?
你认为ERB有用吗?
Yes and no. I think interpolation is definitely not the right way. You will see patterns in your templates and nothing beats a custom syntax when it comes to templates. I think ERB is quite useful, but it lacks in some ways: It doesn't have the concept that blocks are also expressions:
是的,没有。我认为插值绝对不是正确的方法。您将在模板中看到模式,当涉及到模板时,没有什么比自定义语法更好的了。我认为ERB非常有用,但它在某些方面缺乏:它没有块也是表达式的概念:
# This block is more like a statement (we don't care about the return value)
@users.each do |foo|
str << "Hello"
end
# This block is an expression
str << form_for(thing) do
another_string
end
I have no idea if you can fix this with syntax, but right now it's "fixed" in each framework and there's no way to write block helpers that works across several frameworks. I'd love to see a more "official" way to deal with this.
我不知道您是否可以用语法来解决这个问题,但是现在它在每个框架中都是“固定”的,并且没有办法编写跨多个框架的块助手。我希望看到一个更“正式”的方式来处理这个问题。
Should Rails accept "native Ruby" templates as well?
Rails也应该接受“原生Ruby”模板吗?
Tilt already does, and the best solution would be if Rails switched over to Tilt, but I don't think "native Ruby" is that useful after all. If it's a short template, you can of course just use an interpolated string right in your code. And if it's a big template and you're going to move it out of your Ruby file, why not use a syntax designed for templates?
Tilt已经这样做了,最好的解决方案应该是Rails切换到Tilt,但是我不认为“原生Ruby”有用。如果是一个短模板,当然可以在代码中使用一个内插字符串。如果它是一个大模板,并且你要把它从你的Ruby文件中移除,为什么不使用为模板设计的语法呢?
#2
1
Rails is opinionated software. It ships with a bunch of 'default' ways of doing things which I guess the core team agree is their preferred way of doing things - erb, Test::Unit, prototype, etc. but there's nothing to stop you changing these (more so with Rails 3).
Rails是固执己见的软件。它提供了一大堆“默认”的做事方式,我猜核心团队同意这是他们喜欢的做事方式——erb, Test: Unit, prototype等等,但是没有什么可以阻止你改变这些(Rails 3更是如此)。
In my opinion, erb in Rails, even though not quite like normal erb is far, far nicer than ruby string interpolation - especially when it comes to blocks. Having to break back into strings for the HTML within the block looks horrible to me.
在我看来,Rails中的erb,虽然不太像普通的erb,但比ruby字符串插补要好得多——尤其是在块方面。在我看来,要在块中为HTML分解成字符串是很可怕的。
But then again, haml looks horrible to me while others swear by it and hate erb. It's all down to your preference. That's why Rails makes it very easy for developers to create their own templating systems. I don't know if one exists, but there's nothing stopping devs from creating a templating system which uses solely ruby strings like that.
但话说回来,haml对我来说很可怕,而其他人却对它发誓,憎恨erb。这都取决于你的喜好。这就是为什么Rails让开发人员很容易创建他们自己的模板系统。我不知道是否存在这样一个模板系统,但是没有什么可以阻止devs创建一个只使用ruby字符串的模板系统。
So, in answer to "Should Rails accept "native Ruby" templates as well?", it already does. So long as someone else implements it ;)
因此,在回答“Rails是否也应该接受“本机Ruby”模板?”,它已经如此。只要别人实现了;)
#1
4
Magnus Holm here; good to see that you enjoyed the article :-)
马格努斯河中沙洲;很高兴看到你喜欢这篇文章:
First let's have a look at templates (yes, interpolation is a template) as an evolution from constructing a string with regular code:
首先让我们来看看模板(是的,插值是一个模板),它是从用常规代码构造字符串演变而来的:
# "Regular" way of constructing a string:
str = "Hello World: "
@users.each do |user|
str << "How are you today, "
str << user.name
str << "? \n"
end
If we look at this code, it's evident that there are three patterns that repeat themselves:
如果我们看看这段代码,很明显有三种模式重复出现:
- We have plenty of static text which are appended:
str << "Static"
- 我们有大量的静态文本附加在:str < " static "
- We have some dynamic code which are appended:
str << expresstion
- 我们添加了一些动态代码:str < expresstion
- We have some blocks of code which change the control flow:
@users.each
- 我们有一些代码块来更改控制流:@users.each
The great thing about ERB is that it's a perfect isomorphism of these patterns, which means that the user can think of it in terms of constructing the string the regular way. It's simply a way to construct a string with more focus on the static parts (since they are common) than the dynamic parts.
ERB的优点在于它是这些模式的完美同构,这意味着用户可以把它想象成以常规的方式构造字符串。它只是一种构造字符串的方式,它更关注静态部分(因为它们是常见的),而不是动态部分。
As the "purist" you are, you've noticed that this is not the lowest level. That is completely true: As long as you can evaluate Turing complete code, your template engine becomes Turing complete and technically you don't need anything else. You don't need blocks when you can simply nest your template engine.
作为“纯粹主义者”,你已经注意到这并不是最低水平。这是完全正确的:只要您能够评估图灵完整的代码,您的模板引擎就会变成图灵完整,从技术上讲,您不需要任何其他东西。当您可以简单地嵌套模板引擎时,您不需要块。
Now we need think about the difference between low level and easy to understand. Yes, as you remove building parts you end up with a smaller core, but that doesn't necessarily make it easier to understand. You could draw an analogy to GOTOs: You can create any looping/conditionals using one single concept (GOTOs), but it turns out that it's easier to reason about code with if/while-statements. Why? Because a pattern appears! Instead of conceptually parse these patterns every time we look at them, it's easier to create abstractions which we can understand instantly.
现在我们需要考虑低水平和容易理解之间的区别。是的,当你移除建筑部件时,你最终会得到一个更小的核心,但这并不一定使理解变得更容易。您可以对GOTOs进行类比:您可以使用一个概念(GOTOs)创建任何循环/条件语句,但事实证明,使用if/while语句来解释代码更容易。为什么?因为模式出现!当我们每次看到这些模式时,都不会从概念上解析这些模式,而是更容易创建我们能够立即理解的抽象。
In your example, there's one pattern which I believe will be used in any template: @users.map { |f| code }.join
. This is exactly what ERB tries to abstract away so you can forget about the details and reason about your logic instead.
在您的示例中,有一个模式我相信将在任何模板中使用:@users。map {|f| code}.join。这正是ERB试图抽象出来的东西,这样您就可以忘记关于逻辑的细节和原因。
I also believe that if you're making a template engine simpler, but still make it Turing complete, you will abstract away these details anyway. You will realize that a pattern emerges and, as the DRY coder you are, you will start creating helpers and such. You're actually implementing your own little template engine on top of another. Now everything depends on the syntax flexibility of the base language whether or not you manage to abstract away the real details without too much other noise. For instance, there are Lisp flavours which supports static typing and pattern matching, but often it can't beat a syntax designed especially for the problem.
我还相信,如果您使模板引擎更简单,但仍然使图灵完整,那么无论如何您将抽象掉这些细节。您将会意识到一个模式出现了,并且作为一个干编码器,您将开始创建helper类。实际上,您是在另一个模板引擎之上实现您自己的小模板引擎。现在,一切都取决于基本语言的语法灵活性,无论您是否设法在没有太多其他干扰的情况下抽象出真实的细节。例如,有一些Lisp的味道支持静态类型和模式匹配,但是通常它不能打败专门为这个问题设计的语法。
So, what are the real advantages to using ERB over Ruby's native string processing? In one sentence: It gives you nice syntax for common patterns.
那么,与Ruby的本地字符串处理相比,使用ERB的真正好处是什么呢?一句话:它为常见的模式提供了很好的语法。
Do you think ERB is useful?
你认为ERB有用吗?
Yes and no. I think interpolation is definitely not the right way. You will see patterns in your templates and nothing beats a custom syntax when it comes to templates. I think ERB is quite useful, but it lacks in some ways: It doesn't have the concept that blocks are also expressions:
是的,没有。我认为插值绝对不是正确的方法。您将在模板中看到模式,当涉及到模板时,没有什么比自定义语法更好的了。我认为ERB非常有用,但它在某些方面缺乏:它没有块也是表达式的概念:
# This block is more like a statement (we don't care about the return value)
@users.each do |foo|
str << "Hello"
end
# This block is an expression
str << form_for(thing) do
another_string
end
I have no idea if you can fix this with syntax, but right now it's "fixed" in each framework and there's no way to write block helpers that works across several frameworks. I'd love to see a more "official" way to deal with this.
我不知道您是否可以用语法来解决这个问题,但是现在它在每个框架中都是“固定”的,并且没有办法编写跨多个框架的块助手。我希望看到一个更“正式”的方式来处理这个问题。
Should Rails accept "native Ruby" templates as well?
Rails也应该接受“原生Ruby”模板吗?
Tilt already does, and the best solution would be if Rails switched over to Tilt, but I don't think "native Ruby" is that useful after all. If it's a short template, you can of course just use an interpolated string right in your code. And if it's a big template and you're going to move it out of your Ruby file, why not use a syntax designed for templates?
Tilt已经这样做了,最好的解决方案应该是Rails切换到Tilt,但是我不认为“原生Ruby”有用。如果是一个短模板,当然可以在代码中使用一个内插字符串。如果它是一个大模板,并且你要把它从你的Ruby文件中移除,为什么不使用为模板设计的语法呢?
#2
1
Rails is opinionated software. It ships with a bunch of 'default' ways of doing things which I guess the core team agree is their preferred way of doing things - erb, Test::Unit, prototype, etc. but there's nothing to stop you changing these (more so with Rails 3).
Rails是固执己见的软件。它提供了一大堆“默认”的做事方式,我猜核心团队同意这是他们喜欢的做事方式——erb, Test: Unit, prototype等等,但是没有什么可以阻止你改变这些(Rails 3更是如此)。
In my opinion, erb in Rails, even though not quite like normal erb is far, far nicer than ruby string interpolation - especially when it comes to blocks. Having to break back into strings for the HTML within the block looks horrible to me.
在我看来,Rails中的erb,虽然不太像普通的erb,但比ruby字符串插补要好得多——尤其是在块方面。在我看来,要在块中为HTML分解成字符串是很可怕的。
But then again, haml looks horrible to me while others swear by it and hate erb. It's all down to your preference. That's why Rails makes it very easy for developers to create their own templating systems. I don't know if one exists, but there's nothing stopping devs from creating a templating system which uses solely ruby strings like that.
但话说回来,haml对我来说很可怕,而其他人却对它发誓,憎恨erb。这都取决于你的喜好。这就是为什么Rails让开发人员很容易创建他们自己的模板系统。我不知道是否存在这样一个模板系统,但是没有什么可以阻止devs创建一个只使用ruby字符串的模板系统。
So, in answer to "Should Rails accept "native Ruby" templates as well?", it already does. So long as someone else implements it ;)
因此,在回答“Rails是否也应该接受“本机Ruby”模板?”,它已经如此。只要别人实现了;)