【发布时间】:2011-06-10 07:43:58
【问题描述】:
动机和问题
有几个使用 ruby 生成 html 标记字符串的库(erb、haml、builder、markaby、tagz、...),但我对其中任何一个都不满意。原因是,除了erb之外,它们都采用嵌套样式而不是链样式。而erb是一种在html中嵌入ruby而不是用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
问题
- 是否已经有稳定的库可以做类似的事情?
- 这种方法(尤其是我的实现或在链中执行此方法)会出现哪些潜在问题?
- 某些属性采用布尔值,通常鼓励将其省略。例如,
<input type="text";readonly>而不是<input type="text";readonly="true">。在我目前的实现中,我可以通过传递true(最终不会使用)作为dom(:input, type: "text", readonly: true)等属性的值来做到这一点,但这似乎是多余的,也是我拥有@的部分原因代码中的 987654336@ 语句,使其变慢。有没有更好的方法来做到这一点? - 是否对实施有任何可能的改进?
【问题讨论】:
标签: html ruby templates chaining