链式 html 标记
动机和问题
有几个使用 ruby 生成 html 标记字符串的库(erb、haml、builder、markaby、tagz...),但我对其中任何一个都不满意。原因是,除了erb之外,它们都采用嵌套风格而不是链式风格。而erb是一种将ruby嵌入到html中的方法,而不是用ruby生成html。
据我了解,Ruby 的优点之一在于鼓励使用链式风格:
receiver.method1(args1).method2(args2). ... method_n(args_n)
而不是采用嵌套风格:
method_n(...method2(method1(receiver, args1), args2), ... args_n)
但是上面提到的库(erb 除外)采用嵌套风格(有时借助块参数)。
我的想法
出于我自己的目的,我编写了一个方法dom
,以便我可以以链式样式进行html标记。当应用于字符串时,此示例
"This is a link to SO".dom(:a, href: "http://stackoverflow.com").dom(:body).dom(:html)
将生成:
<html><body><a href="http://stackoverflow.com";>This is a link to SO</a></body></html>
当应用于数组时,此:
[
["a".dom(:td), "b".dom(:td)].dom(:tr),
["c".dom(:td), "d".dom(:td)].dom(:tr)
].dom(:table, border: 1)
将生成
<table border="1";>
<tr>
<td>"a"</td>
<td>"b"</td>
</tr>
<tr>
<td>"c"</td>
<td>"d"</td>
</tr>
<table>
并且,当在没有显式接收器(字符串和数组域之外)的情况下应用时,
dom(:img, src: "picture.jpg", width: 48, height: 48)
将生成
<img src="picture.jpg";width="48";height="48";/>
请注意,所有操作都只需一个即可完成方法dom
。这比使用其他库简单得多。它的灵活性还在于它不受 html 标签库存变化的影响;您只需使用符号参数指定即可。在其他库中,每个标签都有类和/或方法。此外,与 erb 不同,它是纯红宝石。它不是需要转换的 DSL。
我的实现
实现如下:
class Hash
def attribute
map{|k, v| %Q{#{k}#{
case v
when TrueClass; ''
when Hash; %Q{="#{v.subattribute}"}
else %Q{="#{v}"}
end
;}}}.join
end
def subattribute
map{|k, v| %Q{#{k}:#{v};}}.join
end
end
class Array
def dom type, hash = {}
"<#{type} #{hash.attribute}>\n#{join("\n").gsub(/^/, " ")}\n</#{type}>"
end
end
class String
def dom type, hash = {}
"<#{type} #{hash.attribute}>#{self}</#{type}>"
end
end
class Object
def dom type, hash = {}
"<#{type} #{hash.attribute}/>"
end
end
问题
- 是否已经有稳定的库可以做类似的事情?
- 这种方法(特别是我的实施或以链式方法执行此操作)有哪些潜在问题?
- 有些属性采用布尔值,通常建议省略这些值。例如,
而不是
。在我目前的实现中,我可以通过传递
true
(最终不会使用)作为诸如dom(:input, type: "text", readonly: true)
,但这似乎是多余的,也是我在代码中使用case
语句的部分原因,导致速度变慢。有更好的方法吗? - 实施方面是否有任何可能的改进?
Motivation and Problem
There are several libraries for generating html markup strings using ruby (erb, haml, builder, markaby, tagz, ...), but I am not satisfied with any of them. The reason is that, except for erb, they take the nesting style rather than the chain style. And erb is a way to embed ruby within html rather than generating html with ruby.
To my understanding, one beauty of ruby lies in encouraging the use of chain style:
receiver.method1(args1).method2(args2). ... method_n(args_n)
instead of doing a nesting style:
method_n(...method2(method1(receiver, args1), args2), ... args_n)
But libraries mentioned above (except for erb) take the nesting style (sometimes with the help of block arguments).
My Idea
For my own purpose, I wrote a method dom
so that I can do html markup in a chain style. When applied to a string, this example
"This is a link to SO".dom(:a, href: "http://stackoverflow.com").dom(:body).dom(:html)
will generate:
<html><body><a href="http://stackoverflow.com";>This is a link to SO</a></body></html>
When applied to an array, this:
[
["a".dom(:td), "b".dom(:td)].dom(:tr),
["c".dom(:td), "d".dom(:td)].dom(:tr)
].dom(:table, border: 1)
will generate
<table border="1";>
<tr>
<td>"a"</td>
<td>"b"</td>
</tr>
<tr>
<td>"c"</td>
<td>"d"</td>
</tr>
<table>
And, when applied without an explicit receiver (outside of a domain of strings and arrays),
dom(:img, src: "picture.jpg", width: 48, height: 48)
will generate
<img src="picture.jpg";width="48";height="48";/>
Note that all is done with just one method dom
. This is much simpler than using other libraries. It is also flexible in that it is not affected by a change in the inventory of html tags; you just specify that with a symbol argument. In other libraries, there are classes and/or methods for each tag. Furthuremore, unlike erb, it is pure ruby. It is not a DSL that needs to be convertred.
My Implementation
The implementation is as follows:
class Hash
def attribute
map{|k, v| %Q{#{k}#{
case v
when TrueClass; ''
when Hash; %Q{="#{v.subattribute}"}
else %Q{="#{v}"}
end
;}}}.join
end
def subattribute
map{|k, v| %Q{#{k}:#{v};}}.join
end
end
class Array
def dom type, hash = {}
"<#{type} #{hash.attribute}>\n#{join("\n").gsub(/^/, " ")}\n</#{type}>"
end
end
class String
def dom type, hash = {}
"<#{type} #{hash.attribute}>#{self}</#{type}>"
end
end
class Object
def dom type, hash = {}
"<#{type} #{hash.attribute}/>"
end
end
Questions
- Are there already stable libraries that do a similar thing?
- What will be the potential problems to this approach (particularly to my implementation or to doing this in chain approach)?
- Some attributes take boolean values, which are often encouraged to be omitted. For example,
<input type="text";readonly>
instead of<input type="text";readonly="true">
. In my present implementation, I can do that by passingtrue
(which will not be used in the end) as the value for such attribute likedom(:input, type: "text", readonly: true)
, but that seems redundant and is also part of the reason that I havecase
statement in the code, making it slower. Is there a better way to do this? - Are there any possible improvements to the implementation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
haml 和大多数其他嵌套内容的主要原因是,基本上很容易查看 HTML 的嵌套方式。我假设是这样,但你实际上编写 HTML 代码还是做更多后端工作?原因是在嵌套样式中,您将看到元素是如何嵌套的(如果您也在编写样式,这对您来说很重要)
虽然编写起来很容易,
但设计人员很难看到 HTML 是如何嵌套的无需绘制地图并尝试在他的脑海中进行可视化即可流动,而:
只需一看就可以做到这一点。
不要举一个更长的例子:
如果客户或您需要向链接添加可点击的图像,对您来说会更容易吗?你会怎么做呢?在 haml 中,这很简单:
另外,恕我直言,为每个 html 标签编写
dom(:)
与为其编写结束标签一样乏味(HAML 和其他人已修复)同样,这些是只是我来自 XHTML/CSS 程序员的意见(而不是 ruby 程序员的角度)
最后,我还认为这可以变成社区 wiki 或其他东西,因为这不值得一个确切的答案,并且可能会产生一个像这样的主观因素有很多。
The main reason of haml and most other nested stuff is that it is basically easy to look how your HTML is nested. I am assuming this but do you actually code HTML or do you do more backend stuff? The reason for it is that in nested style, you will see how your elements are nested(which is important to you if you are also writing the styles)
While it is quite easier to write
it is difficult for a designer to see how the HTML flows without mapping and trying it to visualize in his head, whereas:
does that already with just one look.
Take not of a longer example:
Would it be easier for you if say the client or you needed to add a clickable image to the link? How would you have done it? In haml it is easy as:
Also, IMHO, writing
dom(:)
for each html tag is just as tedious as writing the closing tag for it(which HAML and the others fixed)Again, these are just my opinions from an XHTML/CSS programmer(as opposed to a ruby programmer perspective)
And lastly, I would also think that this can be turned into a community wiki or something as this doesn't merit an exact answer and will probably spawn a lot of subjective ones like this one.