dom 0.5.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHART.html +429 -0
- data/MANUAL.html +414 -0
- data/lib/dom.rb +67 -49
- data/license +9 -0
- data/spec +387 -0
- metadata +15 -6
data/lib/dom.rb
CHANGED
@@ -1,8 +1,55 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2016 sawa
|
4
|
+
|
2
5
|
require "stringio"
|
3
6
|
require "strscan"
|
4
7
|
require "htmlentities"
|
5
8
|
|
9
|
+
class DomString < String; end
|
10
|
+
|
11
|
+
using (Module.new do
|
12
|
+
refine String do
|
13
|
+
AnsiColor = {
|
14
|
+
"1" => "bold",
|
15
|
+
"4" => "underline",
|
16
|
+
"30" => "black",
|
17
|
+
"31" => "red",
|
18
|
+
"32" => "green",
|
19
|
+
"33" => "yellow",
|
20
|
+
"34" => "blue",
|
21
|
+
"35" => "magenta",
|
22
|
+
"36" => "cyan",
|
23
|
+
"37" => "white",
|
24
|
+
"40" => "bg-black",
|
25
|
+
"41" => "bg-red",
|
26
|
+
"42" => "bg-green",
|
27
|
+
"43" => "bg-yellow",
|
28
|
+
"44" => "bg-blue",
|
29
|
+
"45" => "bg-magenta",
|
30
|
+
"46" => "bg-cyan",
|
31
|
+
"47" => "bg-white",
|
32
|
+
}
|
33
|
+
def dom_escape tag = nil
|
34
|
+
case tag; when :style, :script then self else Dom::Coder.encode(self) end
|
35
|
+
end
|
36
|
+
def _ansi2html
|
37
|
+
sc = StringScanner.new(self)
|
38
|
+
io = StringIO.new
|
39
|
+
io.print(
|
40
|
+
if sc.scan(/\e\[0?m/o) then '</span>'
|
41
|
+
elsif sc.scan(/\e\[0?(\d+)m/o) then '<span class="%s">' % AnsiColor[sc[1]]
|
42
|
+
end ||
|
43
|
+
sc.scan(/./mo)) until sc.eos?
|
44
|
+
io.string
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
refine DomString do
|
49
|
+
def dom_escape tag = nil; self end
|
50
|
+
end
|
51
|
+
end)
|
52
|
+
|
6
53
|
class File
|
7
54
|
def self.relativize f; f.sub(%r{\A/}o, "") end
|
8
55
|
end
|
@@ -10,6 +57,7 @@ end
|
|
10
57
|
module Dom
|
11
58
|
Coder = HTMLEntities.new
|
12
59
|
Indent = " "
|
60
|
+
private_constant :Indent
|
13
61
|
def self.compact; singleton_class.class_eval{alias join join_compact} end
|
14
62
|
def self.nested; singleton_class.class_eval{alias join join_nested} end
|
15
63
|
def self.pre; singleton_class.class_eval{alias join join_pre} end
|
@@ -66,60 +114,29 @@ end
|
|
66
114
|
class NilClass
|
67
115
|
public :dom
|
68
116
|
public :jsonml
|
117
|
+
def mounted; nil end
|
69
118
|
end
|
70
119
|
|
71
120
|
class String
|
72
|
-
def dom tag, mounted: nil, **attr
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
121
|
+
def dom tag = nil, mounted: nil, **attr
|
122
|
+
if tag
|
123
|
+
"<%s>%s</%s>".%(
|
124
|
+
Dom.format(tag, attr)
|
125
|
+
.insert(1, (block_given? ? yield(self) : self).dom_escape(tag)._ansi2html)
|
126
|
+
)
|
127
|
+
else
|
128
|
+
dom_escape
|
129
|
+
end
|
77
130
|
.dom_escaped.mounted_set(mounted)
|
78
131
|
end
|
79
|
-
def jsonml tag, attr = nil
|
80
|
-
[*Dom.json_format(tag, attr), self]
|
81
|
-
end
|
132
|
+
def jsonml tag, attr = nil; [*Dom.json_format(tag, attr), self] end
|
82
133
|
def mounted; nil end
|
83
|
-
def dom_escape tag = nil
|
84
|
-
case tag; when :style, :script then self else Dom::Coder.encode(self) end
|
85
|
-
end
|
86
134
|
def dom_escaped; DomString.new(self) end
|
87
|
-
AnsiColor = {
|
88
|
-
"1" => "bold",
|
89
|
-
"4" => "underline",
|
90
|
-
"30" => "black",
|
91
|
-
"31" => "red",
|
92
|
-
"32" => "green",
|
93
|
-
"33" => "yellow",
|
94
|
-
"34" => "blue",
|
95
|
-
"35" => "magenta",
|
96
|
-
"36" => "cyan",
|
97
|
-
"37" => "white",
|
98
|
-
"40" => "bg-black",
|
99
|
-
"41" => "bg-red",
|
100
|
-
"42" => "bg-green",
|
101
|
-
"43" => "bg-yellow",
|
102
|
-
"44" => "bg-blue",
|
103
|
-
"45" => "bg-magenta",
|
104
|
-
"46" => "bg-cyan",
|
105
|
-
"47" => "bg-white",
|
106
|
-
}
|
107
|
-
def _ansi2html
|
108
|
-
sc = StringScanner.new(self)
|
109
|
-
io = StringIO.new
|
110
|
-
io.print(
|
111
|
-
if sc.scan(/\e\[0?m/o) then '</span>'
|
112
|
-
elsif sc.scan(/\e\[0?(\d+)m/o) then '<span class="%s">' % AnsiColor[sc[1]]
|
113
|
-
end ||
|
114
|
-
sc.scan(/./mo)) until sc.eos?
|
115
|
-
io.string
|
116
|
-
end
|
117
135
|
def ansi2html; _ansi2html.dom_escaped end
|
118
136
|
end
|
119
137
|
|
120
138
|
class DomString < String
|
121
139
|
def to_s; self end
|
122
|
-
def dom_escape tag = nil; self end
|
123
140
|
def dom_escaped; self end
|
124
141
|
def mounted_set *mounted
|
125
142
|
mounted.compact!
|
@@ -138,14 +155,17 @@ class Array
|
|
138
155
|
a = self
|
139
156
|
a = block_given? ? map(&Proc.new) : flatten if recurse.empty?
|
140
157
|
if recurse.length <= 1
|
141
|
-
|
158
|
+
#!index: Using `index` instead of `find` to be able to detect `nil`.
|
159
|
+
# (Which turned out irrelevant.)
|
160
|
+
if i = a.index{|e| e.kind_of?(String).! and e.kind_of?(Array).! and e.nil?.!}
|
142
161
|
raise ArgumentError
|
143
|
-
.new("Expecting all array elements to be a string: `#{
|
162
|
+
.new("Expecting all array elements to be a string: `#{a[i].class}:#{a[i].inspect}'")
|
144
163
|
end
|
145
164
|
else
|
146
|
-
|
165
|
+
#!index:
|
166
|
+
if i = a.index{|e| e.kind_of?(Array).!}
|
147
167
|
raise ArgumentError
|
148
|
-
.new("Cannot apply tag `#{recurse[-2].inspect}' to `#{
|
168
|
+
.new("Cannot apply tag `#{recurse[-2].inspect}' to `#{a[i].class}:#{a[i].inspect}'")
|
149
169
|
end
|
150
170
|
end
|
151
171
|
a = a.map{|e| e.dom(*recurse, &(Proc.new if block_given?))} unless recurse.empty?
|
@@ -153,9 +173,7 @@ class Array
|
|
153
173
|
s = "<%s>%s</%s>" % Dom.format(tag, attr).insert(1, s) unless tag.nil?
|
154
174
|
s.dom_escaped.mounted_set(*a.map(&:mounted), mounted)
|
155
175
|
end
|
156
|
-
def jsonml tag, attr = nil
|
157
|
-
[*Dom.json_format(attr), *self]
|
158
|
-
end
|
176
|
+
def jsonml tag, attr = nil; [*Dom.json_format(attr), *self] end
|
159
177
|
end
|
160
178
|
|
161
179
|
Dom.compact
|
data/license
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013--2016 sawa
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/spec
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
#! frozen_string_literal: false
|
2
|
+
#!ruby
|
3
|
+
|
4
|
+
gemspec "dom.gemspec"
|
5
|
+
manage "lib/dom.rb"
|
6
|
+
|
7
|
+
spec "=License",
|
8
|
+
"The MIT License (MIT)",
|
9
|
+
"Copyright (c) 2013-2016 sawa",
|
10
|
+
"Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:",
|
11
|
+
"The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.",
|
12
|
+
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.",
|
13
|
+
coda
|
14
|
+
|
15
|
+
spec "=Overview",
|
16
|
+
"The `\"dom\"` gem is a library to generate HTML/XML code from Ruby commands. It replaces conventional approcaches using template engines, or certain kind of libraries with a similar purpose.",
|
17
|
+
"With conventional template engines, you need to write HTML/XML code in a dedicated language in an external file or here document, which is then parsed. With the `\"dom\"` gem, you can describe HTML/XML structures in Ruby language seemlessly with other parts of Ruby code.",
|
18
|
+
"Some conventional libraries define command names that represent the type of DOM node, which may expect an input like this:",
|
19
|
+
<<~'RUBY'.code,
|
20
|
+
h1{"Hello World!"} #=> "<h1>Hello World!</h1>"
|
21
|
+
div{"Hello World!"} #=> "<div>Hello World!</div>"
|
22
|
+
RUBY
|
23
|
+
"Such notation lacks flexibility. For every type of node, the corresponding command needs to be defined in advance. A user, would also not be able to use an original node type that is not predefined in the library.",
|
24
|
+
"Unlike such systems, `\"dom\"` gem makes use of a fixed method name `dom`, which takes a symbol argument representing the node type. The equivalent of the input above in `\"dom\"` is:",
|
25
|
+
<<~'RUBY'.code,
|
26
|
+
"Hello World!".dom(:h1) #=> "<h1>Hello World!</h1>"
|
27
|
+
"Hello World!".dom(:div) #=> "<div>Hello World!</div>"
|
28
|
+
RUBY
|
29
|
+
"Some conventional libraries take the content of a node as a block. Nested nodes would then require nested blocks:",
|
30
|
+
<<~'RUBY'.code,
|
31
|
+
div{p{span{"Hello World!"}}}
|
32
|
+
#=> "<div><p><span>Hello World!</span></p></div>"
|
33
|
+
RUBY
|
34
|
+
"As the structure becomes complicated, it becomes more difficult to keep the block opening paired with a closing.",
|
35
|
+
"In `\"dom\"`, the content of a node is the receiver of a method. Node embedding is described as method chaining, which avoids unnecessary nesting, and confirms to the Rubyistic coding style.",
|
36
|
+
<<~'RUBY'.code,
|
37
|
+
"Hello World!".dom(:span).dom(:p).dom(:div)
|
38
|
+
#=> "<div><p><span>Hello World!</span></p></div>"
|
39
|
+
RUBY
|
40
|
+
coda
|
41
|
+
|
42
|
+
spec "=Usage",
|
43
|
+
"Require dom (usually at the beginning of a file) before using its methods:",
|
44
|
+
<<~'RUBY'.code,
|
45
|
+
require "dom"
|
46
|
+
RUBY
|
47
|
+
"To surround a string with a tag, use the `dom` method. Pass the tag name as a symbol, with an optional hash, whose keys are symbols, and values that are either `nil`, `true`, `false`, or other kinds of objects that have the `to_s` methods inmplemented.",
|
48
|
+
<<~'RUBY'.code,
|
49
|
+
"foo".dom(:span, class: "bar") # => "<span class=\"bar\">foo</span>"
|
50
|
+
"foo".dom(:span, data_1: nil, data_2: 3) # => "<span data-2=\"3\">foo</span>"
|
51
|
+
RUBY
|
52
|
+
"The `dom` method can be chained to generate nested structures.",
|
53
|
+
<<~'RUBY'.code,
|
54
|
+
"Hello World!".dom(:span).dom(:p).dom(:div)
|
55
|
+
#=> "<div><p><span>Hello World!</span></p></div>"
|
56
|
+
RUBY
|
57
|
+
"When `dom` applies to an array, its elements are joined.",
|
58
|
+
<<~'RUBY'.code,
|
59
|
+
["foo", "bar"].dom(:div, class: "bar")
|
60
|
+
# => "<div class=\"bar\">foobar</div>"
|
61
|
+
RUBY
|
62
|
+
"When multiple tags are passed to a single `dom` method call, the tags are distributed and are percolated into the nested arrays.",
|
63
|
+
<<~'RUBY'.code,
|
64
|
+
[["foo1", "foo2"], ["bar1", "bar2"]].dom(:td, :tr, :table)
|
65
|
+
# => "<table><tr><td>foo1</td><td>foo2</td></tr><tr><td>bar1</td><td>bar2</td></tr></table>"
|
66
|
+
RUBY
|
67
|
+
coda
|
68
|
+
|
69
|
+
module Dom
|
70
|
+
hide spec "::Coder",
|
71
|
+
coda
|
72
|
+
|
73
|
+
hide spec "::Indent",
|
74
|
+
"! Indentation internally used in `join` whem the mode is `nested` or `pre`.",
|
75
|
+
UT =~ /\s+/,
|
76
|
+
coda
|
77
|
+
|
78
|
+
spec ".compact",
|
79
|
+
{"()" => Class},
|
80
|
+
"Sets the output to compact mode. Usually the best for production use. It is compressed, and is not convenient for human reading.",
|
81
|
+
UT.succeed?,
|
82
|
+
"Dom.compact".setup,
|
83
|
+
<<~'RUBY'.code,
|
84
|
+
Dom.compact
|
85
|
+
RUBY
|
86
|
+
coda
|
87
|
+
|
88
|
+
spec ".nested",
|
89
|
+
{"()" => Class},
|
90
|
+
"Sets the output to nested mode. Usually the best for development use. It may not be optimized for production use.",
|
91
|
+
UT.succeed?,
|
92
|
+
"Dom.compact".setup,
|
93
|
+
<<~'RUBY'.code,
|
94
|
+
Dom.nested
|
95
|
+
RUBY
|
96
|
+
coda
|
97
|
+
|
98
|
+
spec ".pre",
|
99
|
+
{"()" => Class},
|
100
|
+
"Sets the output to a mode that preserves the line numbers of the original by inserting HTML's comment expressions. This is for special usage. Normally, you will not need to to set to this mode.",
|
101
|
+
UT.succeed?,
|
102
|
+
"Dom.compact".setup,
|
103
|
+
<<~'RUBY'.code,
|
104
|
+
Dom.pre
|
105
|
+
RUBY
|
106
|
+
coda
|
107
|
+
|
108
|
+
hide spec ".join_compact",
|
109
|
+
"! Internally used `join` method for `compact` mode.",
|
110
|
+
coda
|
111
|
+
|
112
|
+
hide spec ".join_nested",
|
113
|
+
"! Internally used `join` method for `nested` mode.",
|
114
|
+
coda
|
115
|
+
|
116
|
+
hide spec ".join_pre",
|
117
|
+
"! Internally used `join` method for `pre` mode.",
|
118
|
+
coda
|
119
|
+
|
120
|
+
hide spec ".format",
|
121
|
+
"! Converts a hash into an HTML attribute. An attribute `:foo` with value `true` and `false` will be converted to the string `foo=\"\"` and `foo=\"none\"`, respectively. Key-value pairs with the value `nil` will be excluded from the output. Other values will be applied `to_s`. The pairs will be joined by a space, and the whole string will be prepended by a space.",
|
122
|
+
UT(:script, foo: "abcabc") == ["script foo=\"abcabc\"", "script"],
|
123
|
+
UT(:div, foo: nil) == ["div", "div"],
|
124
|
+
UT(:div, foo: false) == ["div foo=\"none\"", "div"],
|
125
|
+
UT(:script, foo: true) == ["script foo=\"\"", "script"],
|
126
|
+
UT(:div, foo: 3) == ["div foo=\"3\"", "div"],
|
127
|
+
UT(:div, foo1: "abcabc", foo2: nil, foo3: false, foo4: true, foo5: 3) ==
|
128
|
+
["div foo1=\"abcabc\" foo3=\"none\" foo4=\"\" foo5=\"3\"", "div"],
|
129
|
+
coda
|
130
|
+
|
131
|
+
hide spec ".json_format",
|
132
|
+
"! Creates an array to be used in JSON.",
|
133
|
+
UT(:div, style: "foo") == ["div", {"style" => "foo"}],
|
134
|
+
coda
|
135
|
+
|
136
|
+
hide spec ".hyphenize",
|
137
|
+
"!Converts underscore into hyphen. Used in `json_format`.",
|
138
|
+
UT("FooBar_Baz-Foo") == "FooBar-Baz-Foo",
|
139
|
+
coda
|
140
|
+
|
141
|
+
hide spec ".join",
|
142
|
+
"! Joins strings in an array. Depending on the mode, either aliased to `join_compact`, `join_nested`, or `join_pre`.",
|
143
|
+
coda
|
144
|
+
end
|
145
|
+
|
146
|
+
class String
|
147
|
+
spec "#dom",
|
148
|
+
{"(tag, mounted: nil, **attr)" => DomString},
|
149
|
+
"When `dom` is called on a string, it will create a tag including that string as the content.",
|
150
|
+
<<~'RUBY'.code,
|
151
|
+
"foo".dom(:span, class: "bar") # => "<span class=\"bar\">foo</span>"
|
152
|
+
RUBY
|
153
|
+
"foo".UT(:span, class: "bar") == "<span class=\"bar\">foo</span>",
|
154
|
+
"Attributes are passed as a hash. A key `:foo` with value `true` or `false` will be converted to the string `foo=\"\"` or `foo=\"none\"`, respectively. Key-value pairs with the value `nil` will be excluded from the output. For other values, `to_s` will be applied. The pairs will be joined by a space, and the whole string will be prepended by a space.",
|
155
|
+
<<~'RUBY'.code,
|
156
|
+
"foo".dom(:span, data_1: nil, data_2: 3) # => "<span data-2=\"3\">foo</span>"
|
157
|
+
RUBY
|
158
|
+
"foo".UT(:span, data_1: nil, data_2: 3) == "<span data-2=\"3\">foo</span>",
|
159
|
+
"HTML escaping will be automatically applied unless the `tag` is `:script` or `:style`:",
|
160
|
+
<<~'RUBY'.code,
|
161
|
+
"<".dom(:span) # => "<span><</span>"
|
162
|
+
"if(3 < 5){alert('foo')};".dom(:script) # => "<script>if(3 < 5){alert('foo')};</script>"
|
163
|
+
RUBY
|
164
|
+
coda
|
165
|
+
|
166
|
+
hide spec "#mounted",
|
167
|
+
"! Gets the state of inner elements, namely whether they are mounted once. For `String`, this is `nil`.",
|
168
|
+
"fresh string".UT.nil?,
|
169
|
+
coda
|
170
|
+
|
171
|
+
# hide spec "#dom_escape",
|
172
|
+
# "<".UT == "<",
|
173
|
+
# "&".UT == "&",
|
174
|
+
# coda
|
175
|
+
|
176
|
+
spec "#dom_escaped",
|
177
|
+
{"()" => DomString},
|
178
|
+
"Marks `self` so that it will not be further HTML escaped. Internally, it is converted to an instance of `DomString` class, which is never further escaped.",
|
179
|
+
"When a string is already HTML escaped but has not been done so via the `dom` method, further applying `dom` to that string will incorrctly doublely apply HTML marking. The string must be marked with this method to avoid HTML escape. For example, without applying `dom_escaped`, an already-escaped string is doublely escaped, giving wrong results:",
|
180
|
+
<<~'RUBY'.code,
|
181
|
+
"3 < 5".dom(:span)
|
182
|
+
# => "<span>3 &lt; 5</span>"
|
183
|
+
["a".dom(:span, class: "bold"), "b"].join.dom
|
184
|
+
#=> "<span class="bold">a</span>b"
|
185
|
+
RUBY
|
186
|
+
"To get the correct results, `dom_escaped` must be applied before application of `dom`:",
|
187
|
+
<<~'RUBY'.code,
|
188
|
+
"3 < 5".dom_escaped.dom(:span)
|
189
|
+
# => "<span>3 < 5</span>"
|
190
|
+
["a".dom(:span, class: "bold"), "b"].join.dom_escaped.dom
|
191
|
+
#=> "<span class=\"bold\">a</span>b"
|
192
|
+
RUBY
|
193
|
+
" 3 < 5".UT.dom(:span) == " 3 < 5".dom_escaped.dom(:span),
|
194
|
+
["a".dom(:span, class: "bold"), "b"].join.UT.dom == "<span class=\"bold\">a</span>b",
|
195
|
+
"When the string is created by `dom` method, such consideration is unnecessary (although it will not harm if `dom_escaped` is redundantly applied).",
|
196
|
+
<<~'RUBY'.code,
|
197
|
+
["a".dom(:span, class: "bold"), "b"].dom.dom
|
198
|
+
# => "<span class=\"bold\">a</span>b"
|
199
|
+
RUBY
|
200
|
+
" 3 < 5".UT.dom(:span) == " 3 < 5".dom_escaped.dom(:span),
|
201
|
+
["a".dom(:span, class: "bold"), "b"].join.UT.dom == "<span class=\"bold\">a</span>b",
|
202
|
+
"foo".UT.instance_of?(DomString),
|
203
|
+
"a = \"foo\"".setup,
|
204
|
+
expr("a").UT == expr("a"),
|
205
|
+
expr("a").UT === expr("a"),
|
206
|
+
expr("a").UT.eql?(expr("a")),
|
207
|
+
coda
|
208
|
+
|
209
|
+
hide spec "#jsonml",
|
210
|
+
coda
|
211
|
+
|
212
|
+
hide spec "::AnsiColor",
|
213
|
+
coda
|
214
|
+
|
215
|
+
hide spec "#ansi2html",
|
216
|
+
"! Internally called by `ansi2html`.",
|
217
|
+
coda
|
218
|
+
end
|
219
|
+
|
220
|
+
hide spec "::DomString",
|
221
|
+
coda
|
222
|
+
|
223
|
+
class DomString
|
224
|
+
hide spec "#to_s",
|
225
|
+
"! A `DomString` instance behaves like a `String` except that dom escape does not modify it. This class is to mark that the instance has alrady gone under dom escaping so that further application of dom escaping does not have effect. It makes dom escaping idempotent.",
|
226
|
+
expr("DomString.new").UT.instance_of?(DomString),
|
227
|
+
"a = DomString.new(\"foo\")".setup,
|
228
|
+
expr("a").UT == expr("a"),
|
229
|
+
expr("a").UT === expr("a"),
|
230
|
+
expr("a").UT.eql?(expr("a")),
|
231
|
+
coda
|
232
|
+
|
233
|
+
# hide spec "#dom_escape",
|
234
|
+
# coda
|
235
|
+
|
236
|
+
spec "#dom",
|
237
|
+
"foo".dom_escaped.UT(:span, class: "bar") == "<span class=\"bar\">foo</span>",
|
238
|
+
coda
|
239
|
+
|
240
|
+
spec "#dom_escaped",
|
241
|
+
expr("DomString.new(\"foo\")").UT.instance_of?(DomString),
|
242
|
+
coda
|
243
|
+
|
244
|
+
hide spec "#mounted",
|
245
|
+
"! Gets the mounted state of `self`. This is a flag indicating whether the string-like object has once been mounted in the web browser.",
|
246
|
+
coda
|
247
|
+
|
248
|
+
hide spec "#mounted_set",
|
249
|
+
"! Sets the state of inner elements, namely whether they are mounted once.",
|
250
|
+
coda
|
251
|
+
end
|
252
|
+
|
253
|
+
module Kernel
|
254
|
+
spec "#dom",
|
255
|
+
{"(tag, mounted: nil, **attr)" => DomString},
|
256
|
+
"When `dom` is used without an explicit receiver, it will create a self-closing tag.",
|
257
|
+
<<~'RUBY'.code,
|
258
|
+
dom(:img, source: "/tmp/foo.png") # => "<img source=\"/tmp/foo.png\" />"
|
259
|
+
RUBY
|
260
|
+
"In the following example, `Kernel#dom` is called at the embedded level. See `Array#dom`.",
|
261
|
+
<<~'RUBY'.code,
|
262
|
+
Array.new(3).dom(:col, :colgroup) # => "<colgroup><col /><col /><col /></colgroup>"
|
263
|
+
RUBY
|
264
|
+
TOPLEVEL_BINDING.receiver.UT(:img, source: "/tmp/foo.png") == "<img source=\"/tmp/foo.png\" />",
|
265
|
+
nil.UT(:foo) == "<foo />",
|
266
|
+
coda
|
267
|
+
|
268
|
+
hide spec "#jsonml",
|
269
|
+
"! Generates a jsonml string.",
|
270
|
+
coda
|
271
|
+
end
|
272
|
+
|
273
|
+
hide spec "::NilClass",
|
274
|
+
coda
|
275
|
+
|
276
|
+
class NilClass
|
277
|
+
hide spec "#mounted",
|
278
|
+
"! Gets the state of inner elements, namely whether they are mounted once. For `NilClass`, this is `nil`.",
|
279
|
+
nil.UT.nil?,
|
280
|
+
coda
|
281
|
+
end
|
282
|
+
|
283
|
+
class Array
|
284
|
+
spec "#dom",
|
285
|
+
{"(tag, mounted: nil, **attr)" => DomString},
|
286
|
+
"Concatenates the elements and puts them inside the tag.",
|
287
|
+
<<~'RUBY'.code,
|
288
|
+
["foo", "bar"].dom(:div, class: "bar") # => "<div class=\"bar\">foobar</div>"
|
289
|
+
RUBY
|
290
|
+
["foo", "bar"].UT(:div, class: "bar") == "<div class=\"bar\">foobar</div>",
|
291
|
+
{"()" => DomString},
|
292
|
+
"When no argument is given or an explicit `nil` is given, it joins its elements.",
|
293
|
+
<<~'RUBY'.code,
|
294
|
+
["foo", "bar"].dom # => "foobar",
|
295
|
+
["foo", "bar"].dom(nil) # => "foobar",
|
296
|
+
RUBY
|
297
|
+
["foo", "bar"].UT == "foobar",
|
298
|
+
RETURN.is_a?(DomString),
|
299
|
+
["foo", "bar"].UT(nil) == "foobar",
|
300
|
+
RETURN.is_a?(DomString),
|
301
|
+
{"(*tags, mounted: nil, **attr)" => DomString},
|
302
|
+
"When more than one tag is provided, it applies `dom` to each of its elements with the tags passed except for the last tag (with no other parameters). The last tag and the other parameters are used to apply `dom` to the result.",
|
303
|
+
<<~'RUBY'.code,
|
304
|
+
[["foo1", "foo2"], ["bar1", "bar2"]].dom(:td, :tr, :table)
|
305
|
+
# => "<table><tr><td>foo1</td><td>foo2</td></tr><tr><td>bar1</td><td>bar2</td></tr></table>"
|
306
|
+
|
307
|
+
"Array.new(3)".dom(:col, :colgroup)
|
308
|
+
#=> "<colgroup><col /><col /><col /></colgroup>"
|
309
|
+
|
310
|
+
[["a", 1], ["b", "c"]].dom(:td, :tr)
|
311
|
+
# => ArgumentError
|
312
|
+
|
313
|
+
[[["a", "b"], "c"], "d"].dom(:bar, :foo)
|
314
|
+
# => "<foo><bar>abc</bar><bar>d</bar></foo>"
|
315
|
+
|
316
|
+
["x"].dom(:a, :b, :c, :d)
|
317
|
+
# => ArgumentError
|
318
|
+
RUBY
|
319
|
+
[["foo1", "foo2"], ["bar1", "bar2"]].UT(:td, :tr, :table) ==
|
320
|
+
"<table><tr><td>foo1</td><td>foo2</td></tr><tr><td>bar1</td><td>bar2</td></tr></table>",
|
321
|
+
RETURN.is_a?(DomString),
|
322
|
+
expr("Array.new(3)").UT(:col, :colgroup) == "<colgroup><col /><col /><col /></colgroup>",
|
323
|
+
# note "flatten", "This is necessary so that `mounted` can be applied to all its elements, which is only defined for `String` and `DomString`."
|
324
|
+
[["a", 1], ["b", "c"]].UT(:td, :tr).raise?(ArgumentError),
|
325
|
+
[[["a", "b"], "c"], "d"].UT(:bar, :foo) == "<foo><bar>abc</bar><bar>d</bar></foo>",
|
326
|
+
["x"].UT(:a, :b, :c, :d).raise?(ArgumentError, message: /Cannot apply tag/),
|
327
|
+
"Passing `nil` as the last argument in a sequence:",
|
328
|
+
<<~'RUBY'.code,
|
329
|
+
[["foo1", "foo2"], ["bar1", "bar2"]].dom(:td, :tr, nil)
|
330
|
+
# => "<tr><td>foo1</td><td>foo2</td></tr><tr><td>bar1</td><td>bar2</td></tr>"
|
331
|
+
|
332
|
+
[["a", "b"], ["c", "d"]].dom(:div, nil, nil)
|
333
|
+
# => "<div>a</div><div>b</div><div>c</div><div>d</div>"
|
334
|
+
|
335
|
+
[["a", "b"], ["c", "d"]].dom(:div, nil)
|
336
|
+
# => "<div>ab</div><div>cd</div>"
|
337
|
+
RUBY
|
338
|
+
[["foo1", "foo2"], ["bar1", "bar2"]].UT(:td, :tr, nil) ==
|
339
|
+
"<tr><td>foo1</td><td>foo2</td></tr><tr><td>bar1</td><td>bar2</td></tr>",
|
340
|
+
RETURN.is_a?(DomString),
|
341
|
+
[["a", "b"], ["c", "d"]].UT(:div, nil, nil) == "<div>a</div><div>b</div><div>c</div><div>d</div>",
|
342
|
+
[["a", "b"], ["c", "d"]].UT(:div, nil) == "<div>ab</div><div>cd</div>",
|
343
|
+
"Cf.",
|
344
|
+
<<~'RUBY'.code,
|
345
|
+
[["a", "b"], ["c", "d"]].dom(nil, :div)
|
346
|
+
# => "<div>abcd</div>"
|
347
|
+
RUBY
|
348
|
+
[["a", "b"], ["c", "d"]].UT(nil, :div) == "<div>abcd</div>",
|
349
|
+
{"(*tags, mounted: nil, **attr, &pr)" => DomString},
|
350
|
+
"When a block is passed, the array depth to which the block is applied depends on the class of the receiver that corresponds to the inner most tag.",
|
351
|
+
"When the receiver that corresponds to the inndermost tag is an array, the block is applied to each of its elements.",
|
352
|
+
<<~'RUBY'.code,
|
353
|
+
["a", "b"].dom(:foo){|e| e * 2}
|
354
|
+
# => "<foo>aabb</foo>"
|
355
|
+
|
356
|
+
[["a", "b"], ["c", "d"]].dom(:bar, :foo){|e| e * 2}
|
357
|
+
# => "<foo><bar>aabb</bar><bar>ccdd</bar></foo>"
|
358
|
+
RUBY
|
359
|
+
["foo", "bar"].UT(:div, class: "bar") == "<div class=\"bar\">foobar</div>",
|
360
|
+
RETURN.is_a?(DomString),
|
361
|
+
["a", "b"].UT(:foo){|e| e * 2} == "<foo>aabb</foo>",
|
362
|
+
[["a", "b"], ["c", "d"]].UT(:bar, :foo){|e| e * 2} == "<foo><bar>aabb</bar><bar>ccdd</bar></foo>",
|
363
|
+
|
364
|
+
"When the receiver that corresponds to the innermost tag is a string, the block is applied to it.",
|
365
|
+
<<~'RUBY'.code,
|
366
|
+
[["a", "b"], ["c", "d"]].dom(:td, :tr, :tbody){|e| e * 2}
|
367
|
+
# => "<tbody><tr><td>aa</td><td>bb</td></tr><tr><td>cc</td><td>dd</td></tr></tbody>"
|
368
|
+
RUBY
|
369
|
+
[["a", "b"], ["c", "d"]].UT(:td, :tr, :tbody){|e| e * 2} == "<tbody><tr><td>aa</td><td>bb</td></tr><tr><td>cc</td><td>dd</td></tr></tbody>",
|
370
|
+
coda
|
371
|
+
|
372
|
+
hide spec "#jsonml",
|
373
|
+
coda
|
374
|
+
end
|
375
|
+
|
376
|
+
class File
|
377
|
+
spec ".relativize",
|
378
|
+
{"(string)" => String},
|
379
|
+
"Removes the initial slash if an absolute path is given. If a relative path is given, it has no effect.",
|
380
|
+
<<~'RUBY'.code,
|
381
|
+
File.relativize("/foo/bar/baz") #=> "foo/bar/baz"
|
382
|
+
File.relativize("foo/bar/baz") #=> "foo/bar/baz"
|
383
|
+
RUBY
|
384
|
+
UT("/foo/bar/baz") == "foo/bar/baz",
|
385
|
+
UT("foo/bar/baz") == "foo/bar/baz",
|
386
|
+
coda
|
387
|
+
end
|