fun_html 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fun_html/attribute.rb +96 -0
- data/lib/fun_html/{attribute_definitions.rb → spec_attributes.rb} +87 -33
- data/lib/fun_html/{node_definitions.rb → spec_elements.rb} +14 -17
- data/lib/fun_html/template.rb +104 -0
- data/lib/fun_html/version.rb +1 -1
- data/lib/fun_html/writer.rb +49 -0
- data/lib/fun_html.rb +4 -108
- data/rbi/attributes.rbx +183 -3
- data/rbi/{node_definitions.rbx → elements.rbx} +142 -142
- data/rbi/fun_html.rbi +396 -157
- data/rbi/fun_html.rbx +71 -12
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4fe70a0317730c02b34d0727a50c3cec42e8e0125a9da574ed0626b3e8f64432
|
4
|
+
data.tar.gz: 92a2bdc05ab5fdc34db3d6c284a34237f0218e6abc506d226351ac550805966f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf86a98a685339cbcad38e55c9f3ce8dfe05e109c1acd42c470038a0ffa2211e6008ad690abe014e7173ce89dfa085e0be1e1822ba5d9d1fc09c93b8c8e6d70d
|
7
|
+
data.tar.gz: f942f6395d3f5ef8f00db4a26c1a615d4c210502b20b08c62e9c4c1162a56e1da721df1d30178ae151cab8ea6e8a8055546ca0c70884254d9ea3dd16c995a715
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative 'spec_attributes'
|
5
|
+
require 'erb/escape'
|
6
|
+
|
7
|
+
module FunHtml
|
8
|
+
# Attribute generates HTML attributes. It is designed to prevent printing the
|
9
|
+
# same attribute twice. The Template elements expect an Attribute object.
|
10
|
+
#
|
11
|
+
# All attribute values are HTML escaped via ERB::Escape.
|
12
|
+
#
|
13
|
+
# It is typed to only allow the correct types to be passed to the attribute,
|
14
|
+
# if Sorbet is enabled.
|
15
|
+
#
|
16
|
+
# Attributes can be generated via a block, or chained.
|
17
|
+
#
|
18
|
+
# Block
|
19
|
+
# Attribute.new { _1.id "one" ; _1.klass "big" }
|
20
|
+
#
|
21
|
+
# Chained
|
22
|
+
# Attribute.new.id("one").klass("big")
|
23
|
+
#
|
24
|
+
# Boolean attributes
|
25
|
+
# Some attributes (async, autoplay, disabled) are boolean. When true is
|
26
|
+
# passed, the attribute prints, when false, the attribute will be blank.
|
27
|
+
#
|
28
|
+
# Attribute.new.disabled(true) -> " disabled"
|
29
|
+
# Attribute.new.disabled(false) -> " "
|
30
|
+
class Attribute
|
31
|
+
include FunHtml::SpecAttributes
|
32
|
+
|
33
|
+
# only allow nil or objects that respond to `safe_attribute`
|
34
|
+
def self.to_html(attr)
|
35
|
+
attr&.safe_attribute.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# create a new Attribute object to create reuseable attributes
|
39
|
+
def initialize(buffer = {}, &block)
|
40
|
+
@__buffer = buffer
|
41
|
+
return unless block
|
42
|
+
|
43
|
+
yield self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Custom data attributes
|
47
|
+
# The data attribute takes a suffix and the string value.
|
48
|
+
# Attribute.new.data('turbo','false') -> ' data-turbo="false"'
|
49
|
+
def data(suffix, value)
|
50
|
+
unless suffix.match?(/\A[a-z-]+\z/)
|
51
|
+
raise ArgumentError,
|
52
|
+
"suffix (#{suffix}) must be lowercase and only contain 'a' to 'z' or hyphens."
|
53
|
+
end
|
54
|
+
|
55
|
+
write(" data-#{suffix}=\"", value)
|
56
|
+
end
|
57
|
+
|
58
|
+
# CSS class name(s) for styling, the name changed to protect the Ruby.
|
59
|
+
def klass(value)
|
60
|
+
write(' class="', value)
|
61
|
+
end
|
62
|
+
|
63
|
+
# classes takes a hash of class names and boolean values
|
64
|
+
# only the names with a true value will be printed
|
65
|
+
# classes({'a' => true, 'b' => false, 'c' => true}) -> ' class="a c"'
|
66
|
+
def classes(list)
|
67
|
+
klass list.select { |_k, v| v == true }.keys.join(' ')
|
68
|
+
end
|
69
|
+
|
70
|
+
# alias so klass has classes
|
71
|
+
def klasses(list) = classes(list)
|
72
|
+
|
73
|
+
# Merge another Attribute to create a new, combined, Attribute.
|
74
|
+
def merge(other)
|
75
|
+
self.class.new(@__buffer.merge(other.instance_variable_get(:@__buffer)))
|
76
|
+
end
|
77
|
+
|
78
|
+
# output the attributes as string
|
79
|
+
def safe_attribute
|
80
|
+
@__buffer.values.join
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def write(name, value)
|
86
|
+
@__buffer[name] = "#{name}#{ERB::Escape.html_escape(value)}\""
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# for boolean attributes, determin if to print the attribute or not
|
91
|
+
def write_boolean(name, print)
|
92
|
+
@__buffer[name] = print ? name : ''
|
93
|
+
self
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module FunHtml
|
4
4
|
# HTML attributes autogenerated, do not edit
|
5
|
-
module
|
5
|
+
module SpecAttributes
|
6
6
|
# Specifies file types browser will accept
|
7
7
|
def accept(value) = write(' accept="', value)
|
8
8
|
# Character encodings used for form submission
|
@@ -16,13 +16,13 @@ module FunHtml
|
|
16
16
|
# Alternative text for images
|
17
17
|
def alt(value) = write(' alt="', value)
|
18
18
|
# Script should execute asynchronously
|
19
|
-
def async(value) =
|
19
|
+
def async(value) = write_boolean(' async', value)
|
20
20
|
# Form/input autocompletion
|
21
21
|
def autocomplete(value) = write(' autocomplete="', value)
|
22
22
|
# Element should be focused on page load
|
23
|
-
def autofocus(value) =
|
23
|
+
def autofocus(value) = write_boolean(' autofocus', value)
|
24
24
|
# Media will start playing automatically
|
25
|
-
def autoplay(value) =
|
25
|
+
def autoplay(value) = write_boolean(' autoplay', value)
|
26
26
|
# Background color of element
|
27
27
|
def bgcolor(value) = write(' bgcolor="', value)
|
28
28
|
# Border width in pixels
|
@@ -30,9 +30,7 @@ module FunHtml
|
|
30
30
|
# Character encoding of document
|
31
31
|
def charset(value) = write(' charset="', value)
|
32
32
|
# Whether checkbox/radio button is selected
|
33
|
-
def checked(value) =
|
34
|
-
# CSS class name(s) for styling
|
35
|
-
def klass(value) = write(' class="', value)
|
33
|
+
def checked(value) = write_boolean(' checked', value)
|
36
34
|
# Number of columns in textarea
|
37
35
|
def cols(value) = write(' cols="', value)
|
38
36
|
# Number of columns a cell spans
|
@@ -42,30 +40,19 @@ module FunHtml
|
|
42
40
|
# Whether content is editable
|
43
41
|
def contenteditable(value) = write(' contenteditable="', value)
|
44
42
|
# Show media playback controls
|
45
|
-
def controls(value) =
|
43
|
+
def controls(value) = write_boolean(' controls', value)
|
46
44
|
# Coordinates for image maps
|
47
45
|
def coords(value) = write(' coords="', value)
|
48
|
-
|
49
|
-
# Custom data attributes
|
50
|
-
def data(suffix, value)
|
51
|
-
unless suffix.match?(/\A[a-z-]+\z/)
|
52
|
-
raise ArgumentError,
|
53
|
-
"suffix (#{suffix}) must be lowercase and only contain 'a' to 'z' or hyphens."
|
54
|
-
end
|
55
|
-
|
56
|
-
write(" data-#{suffix}=\"", value)
|
57
|
-
end
|
58
|
-
|
59
46
|
# Date/time of element content
|
60
47
|
def datetime(value) = write(' datetime="', value)
|
61
48
|
# Default track for media
|
62
|
-
def default(value) =
|
49
|
+
def default(value) = write_boolean(' default', value)
|
63
50
|
# Script should execute after parsing
|
64
|
-
def defer(value) =
|
51
|
+
def defer(value) = write_boolean(' defer', value)
|
65
52
|
# Text direction
|
66
53
|
def dir(value) = write(' dir="', value)
|
67
54
|
# Element is disabled
|
68
|
-
def disabled(value) =
|
55
|
+
def disabled(value) = write_boolean(' disabled', value)
|
69
56
|
# Resource should be downloaded
|
70
57
|
def download(value) = write(' download="', value)
|
71
58
|
# Element can be dragged
|
@@ -83,7 +70,7 @@ module FunHtml
|
|
83
70
|
# Height of element
|
84
71
|
def height(value) = write(' height="', value)
|
85
72
|
# Element is not displayed
|
86
|
-
def hidden(value) =
|
73
|
+
def hidden(value) = write_boolean(' hidden', value)
|
87
74
|
# Upper range of meter
|
88
75
|
def high(value) = write(' high="', value)
|
89
76
|
# URL of linked resource
|
@@ -95,7 +82,7 @@ module FunHtml
|
|
95
82
|
# Subresource integrity hash
|
96
83
|
def integrity(value) = write(' integrity="', value)
|
97
84
|
# Image is server-side image map
|
98
|
-
def ismap(value) =
|
85
|
+
def ismap(value) = write_boolean(' ismap', value)
|
99
86
|
# Type of text track
|
100
87
|
def kind(value) = write(' kind="', value)
|
101
88
|
# Label for form control/option
|
@@ -105,7 +92,7 @@ module FunHtml
|
|
105
92
|
# Links input to datalist options
|
106
93
|
def list(value) = write(' list="', value)
|
107
94
|
# Media will replay when finished
|
108
|
-
def loop(value) =
|
95
|
+
def loop(value) = write_boolean(' loop', value)
|
109
96
|
# Lower range of meter
|
110
97
|
def low(value) = write(' low="', value)
|
111
98
|
# Maximum allowed value
|
@@ -119,15 +106,15 @@ module FunHtml
|
|
119
106
|
# Minimum allowed value
|
120
107
|
def min(value) = write(' min="', value)
|
121
108
|
# Multiple values can be selected
|
122
|
-
def multiple(value) =
|
109
|
+
def multiple(value) = write_boolean(' multiple', value)
|
123
110
|
# Media is muted by default
|
124
|
-
def muted(value) =
|
111
|
+
def muted(value) = write_boolean(' muted', value)
|
125
112
|
# Name of form control
|
126
113
|
def name(value) = write(' name="', value)
|
127
114
|
# Form validation is skipped
|
128
|
-
def novalidate(value) =
|
115
|
+
def novalidate(value) = write_boolean(' novalidate', value)
|
129
116
|
# Details element is expanded
|
130
|
-
def open(value) =
|
117
|
+
def open(value) = write_boolean(' open', value)
|
131
118
|
# Optimal value for meter
|
132
119
|
def optimum(value) = write(' optimum="', value)
|
133
120
|
# Regular expression pattern
|
@@ -139,23 +126,25 @@ module FunHtml
|
|
139
126
|
# How media should be loaded
|
140
127
|
def preload(value) = write(' preload="', value)
|
141
128
|
# Input field cannot be modified
|
142
|
-
def readonly(value) =
|
129
|
+
def readonly(value) = write_boolean(' readonly', value)
|
143
130
|
# Relationship of linked resource
|
144
131
|
def rel(value) = write(' rel="', value)
|
145
132
|
# Input must be filled out
|
146
|
-
def required(value) =
|
133
|
+
def required(value) = write_boolean(' required', value)
|
147
134
|
# List is numbered in reverse
|
148
|
-
def reversed(value) =
|
135
|
+
def reversed(value) = write_boolean(' reversed', value)
|
149
136
|
# Number of rows in textarea
|
150
137
|
def rows(value) = write(' rows="', value)
|
151
138
|
# Number of rows a cell spans
|
152
139
|
def rowspan(value) = write(' rowspan="', value)
|
153
140
|
# Security rules for iframe
|
154
141
|
def sandbox(value) = write(' sandbox="', value)
|
142
|
+
# Specifies the number of consecutive columns the <colgroup> element spans. The value must be a positive integer greater than zero. If not present, its default value is 1.
|
143
|
+
def span(value) = write(' span="', value)
|
155
144
|
# Cells header element relates to
|
156
145
|
def scope(value) = write(' scope="', value)
|
157
146
|
# Option is pre-selected
|
158
|
-
def selected(value) =
|
147
|
+
def selected(value) = write_boolean(' selected', value)
|
159
148
|
# Shape of image map area
|
160
149
|
def shape(value) = write(' shape="', value)
|
161
150
|
# Size of input/select control
|
@@ -196,5 +185,70 @@ module FunHtml
|
|
196
185
|
def width(value) = write(' width="', value)
|
197
186
|
# How text wraps in textarea
|
198
187
|
def wrap(value) = write(' wrap="', value)
|
188
|
+
def onabort(value) = write(' onabort="', value)
|
189
|
+
def onauxclick(value) = write(' onauxclick="', value)
|
190
|
+
def onbeforeinput(value) = write(' onbeforeinput="', value)
|
191
|
+
def onbeforematch(value) = write(' onbeforematch="', value)
|
192
|
+
def onbeforetoggle(value) = write(' onbeforetoggle="', value)
|
193
|
+
def oncancel(value) = write(' oncancel="', value)
|
194
|
+
def oncanplay(value) = write(' oncanplay="', value)
|
195
|
+
def oncanplaythrough(value) = write(' oncanplaythrough="', value)
|
196
|
+
def onchange(value) = write(' onchange="', value)
|
197
|
+
def onclick(value) = write(' onclick="', value)
|
198
|
+
def onclose(value) = write(' onclose="', value)
|
199
|
+
def oncontextlost(value) = write(' oncontextlost="', value)
|
200
|
+
def oncontextmenu(value) = write(' oncontextmenu="', value)
|
201
|
+
def oncontextrestored(value) = write(' oncontextrestored="', value)
|
202
|
+
def oncopy(value) = write(' oncopy="', value)
|
203
|
+
def oncuechange(value) = write(' oncuechange="', value)
|
204
|
+
def oncut(value) = write(' oncut="', value)
|
205
|
+
def ondblclick(value) = write(' ondblclick="', value)
|
206
|
+
def ondrag(value) = write(' ondrag="', value)
|
207
|
+
def ondragend(value) = write(' ondragend="', value)
|
208
|
+
def ondragenter(value) = write(' ondragenter="', value)
|
209
|
+
def ondragleave(value) = write(' ondragleave="', value)
|
210
|
+
def ondragover(value) = write(' ondragover="', value)
|
211
|
+
def ondragstart(value) = write(' ondragstart="', value)
|
212
|
+
def ondrop(value) = write(' ondrop="', value)
|
213
|
+
def ondurationchange(value) = write(' ondurationchange="', value)
|
214
|
+
def onemptied(value) = write(' onemptied="', value)
|
215
|
+
def onended(value) = write(' onended="', value)
|
216
|
+
def onformdata(value) = write(' onformdata="', value)
|
217
|
+
def oninput(value) = write(' oninput="', value)
|
218
|
+
def oninvalid(value) = write(' oninvalid="', value)
|
219
|
+
def onkeydown(value) = write(' onkeydown="', value)
|
220
|
+
def onkeypress(value) = write(' onkeypress="', value)
|
221
|
+
def onkeyup(value) = write(' onkeyup="', value)
|
222
|
+
def onloadeddata(value) = write(' onloadeddata="', value)
|
223
|
+
def onloadedmetadata(value) = write(' onloadedmetadata="', value)
|
224
|
+
def onloadstart(value) = write(' onloadstart="', value)
|
225
|
+
def onmousedown(value) = write(' onmousedown="', value)
|
226
|
+
def onmouseenter(value) = write(' onmouseenter="', value)
|
227
|
+
def onmouseleave(value) = write(' onmouseleave="', value)
|
228
|
+
def onmousemove(value) = write(' onmousemove="', value)
|
229
|
+
def onmouseout(value) = write(' onmouseout="', value)
|
230
|
+
def onmouseover(value) = write(' onmouseover="', value)
|
231
|
+
def onmouseup(value) = write(' onmouseup="', value)
|
232
|
+
def onpaste(value) = write(' onpaste="', value)
|
233
|
+
def onpause(value) = write(' onpause="', value)
|
234
|
+
def onplay(value) = write(' onplay="', value)
|
235
|
+
def onplaying(value) = write(' onplaying="', value)
|
236
|
+
def onprogress(value) = write(' onprogress="', value)
|
237
|
+
def onratechange(value) = write(' onratechange="', value)
|
238
|
+
def onreset(value) = write(' onreset="', value)
|
239
|
+
def onscrollend(value) = write(' onscrollend="', value)
|
240
|
+
def onsecuritypolicyviolation(value) = write(' onsecuritypolicyviolation="', value)
|
241
|
+
def onseeked(value) = write(' onseeked="', value)
|
242
|
+
def onseeking(value) = write(' onseeking="', value)
|
243
|
+
def onselect(value) = write(' onselect="', value)
|
244
|
+
def onslotchange(value) = write(' onslotchange="', value)
|
245
|
+
def onstalled(value) = write(' onstalled="', value)
|
246
|
+
def onsubmit(value) = write(' onsubmit="', value)
|
247
|
+
def onsuspend(value) = write(' onsuspend="', value)
|
248
|
+
def ontimeupdate(value) = write(' ontimeupdate="', value)
|
249
|
+
def ontoggle(value) = write(' ontoggle="', value)
|
250
|
+
def onvolumechange(value) = write(' onvolumechange="', value)
|
251
|
+
def onwaiting(value) = write(' onwaiting="', value)
|
252
|
+
def onwheel(value) = write(' onwheel="', value)
|
199
253
|
end
|
200
254
|
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module FunHtml
|
4
4
|
# HTML nodes autogenerated, do not edit
|
5
|
-
module
|
5
|
+
module SpecElements
|
6
6
|
module HTMLHtmlElement
|
7
7
|
def html(attributes = nil, &elements)
|
8
8
|
write('<html', '</html>', attributes, &elements)
|
@@ -24,21 +24,21 @@ module FunHtml
|
|
24
24
|
module HTMLBaseElement
|
25
25
|
def base(attributes = nil)
|
26
26
|
# no child elements allowed and no closing tag
|
27
|
-
|
27
|
+
write_void('<base', attributes)
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
module HTMLLinkElement
|
32
32
|
def link(attributes = nil)
|
33
33
|
# no child elements allowed and no closing tag
|
34
|
-
|
34
|
+
write_void('<link', attributes)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
38
|
module HTMLMetaElement
|
39
39
|
def meta(attributes = nil)
|
40
40
|
# no child elements allowed and no closing tag
|
41
|
-
|
41
|
+
write_void('<meta', attributes)
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -201,7 +201,7 @@ module FunHtml
|
|
201
201
|
|
202
202
|
def wbr(attributes = nil)
|
203
203
|
# no child elements allowed and no closing tag
|
204
|
-
|
204
|
+
write_void('<wbr', attributes)
|
205
205
|
end
|
206
206
|
|
207
207
|
def summary(attributes = nil, &elements)
|
@@ -300,7 +300,7 @@ module FunHtml
|
|
300
300
|
module HTMLHRElement
|
301
301
|
def hr(attributes = nil)
|
302
302
|
# no child elements allowed and no closing tag
|
303
|
-
|
303
|
+
write_void('<hr', attributes)
|
304
304
|
end
|
305
305
|
end
|
306
306
|
|
@@ -391,7 +391,7 @@ module FunHtml
|
|
391
391
|
module HTMLBRElement
|
392
392
|
def br(attributes = nil)
|
393
393
|
# no child elements allowed and no closing tag
|
394
|
-
|
394
|
+
write_void('<br', attributes)
|
395
395
|
end
|
396
396
|
end
|
397
397
|
|
@@ -414,14 +414,14 @@ module FunHtml
|
|
414
414
|
module HTMLSourceElement
|
415
415
|
def source(attributes = nil)
|
416
416
|
# no child elements allowed and no closing tag
|
417
|
-
|
417
|
+
write_void('<source', attributes)
|
418
418
|
end
|
419
419
|
end
|
420
420
|
|
421
421
|
module HTMLImageElement
|
422
422
|
def img(attributes = nil)
|
423
423
|
# no child elements allowed and no closing tag
|
424
|
-
|
424
|
+
write_void('<img', attributes)
|
425
425
|
end
|
426
426
|
end
|
427
427
|
|
@@ -434,7 +434,7 @@ module FunHtml
|
|
434
434
|
module HTMLEmbedElement
|
435
435
|
def embed(attributes = nil)
|
436
436
|
# no child elements allowed and no closing tag
|
437
|
-
|
437
|
+
write_void('<embed', attributes)
|
438
438
|
end
|
439
439
|
end
|
440
440
|
|
@@ -459,7 +459,7 @@ module FunHtml
|
|
459
459
|
module HTMLTrackElement
|
460
460
|
def track(attributes = nil)
|
461
461
|
# no child elements allowed and no closing tag
|
462
|
-
|
462
|
+
write_void('<track', attributes)
|
463
463
|
end
|
464
464
|
end
|
465
465
|
|
@@ -472,7 +472,7 @@ module FunHtml
|
|
472
472
|
module HTMLAreaElement
|
473
473
|
def area(attributes = nil)
|
474
474
|
# no child elements allowed and no closing tag
|
475
|
-
|
475
|
+
write_void('<area', attributes)
|
476
476
|
end
|
477
477
|
end
|
478
478
|
|
@@ -495,7 +495,7 @@ module FunHtml
|
|
495
495
|
|
496
496
|
def col(attributes = nil)
|
497
497
|
# no child elements allowed and no closing tag
|
498
|
-
|
498
|
+
write_void('<col', attributes)
|
499
499
|
end
|
500
500
|
end
|
501
501
|
|
@@ -544,7 +544,7 @@ module FunHtml
|
|
544
544
|
module HTMLInputElement
|
545
545
|
def input(attributes = nil)
|
546
546
|
# no child elements allowed and no closing tag
|
547
|
-
|
547
|
+
write_void('<input', attributes)
|
548
548
|
end
|
549
549
|
end
|
550
550
|
|
@@ -627,9 +627,6 @@ module FunHtml
|
|
627
627
|
end
|
628
628
|
|
629
629
|
module HTMLScriptElement
|
630
|
-
def script(attributes = nil, &elements)
|
631
|
-
write('<script', '</script>', attributes, &elements)
|
632
|
-
end
|
633
630
|
end
|
634
631
|
|
635
632
|
module HTMLTemplateElement
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
require_relative 'attribute'
|
4
|
+
require_relative 'writer'
|
5
|
+
require 'erb/escape'
|
6
|
+
|
7
|
+
module FunHtml
|
8
|
+
# FunHtml Template will generate typed HTML. Each tag and attribute has a
|
9
|
+
# match method that is typed via Sorbet (which is optional).
|
10
|
+
#
|
11
|
+
# The template is designed to allow subclassing or using `start` to generate
|
12
|
+
# templates without subclassing.
|
13
|
+
#
|
14
|
+
# When subclassing understand that `new` generatings a "buffer". Each time a
|
15
|
+
# tag(div, b, body, etc) is called it will be added to the buffer. Once
|
16
|
+
# `render` is called the buffer is is returned and then cleared.
|
17
|
+
#
|
18
|
+
# class Example < FunHtml::Template
|
19
|
+
# def initialize(name)
|
20
|
+
# super()
|
21
|
+
# @name = name
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def view
|
25
|
+
# doctype
|
26
|
+
# html do
|
27
|
+
# body do
|
28
|
+
# h1 { text @name }
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# puts Example.new('My Example').view.render
|
35
|
+
# <!DOCTYPE html><html><body><h1>My Example</h1></body></html>
|
36
|
+
#
|
37
|
+
# If you need to create custom tags, create a method that integrates with the
|
38
|
+
# Writer#write method.
|
39
|
+
class Template
|
40
|
+
include FunHtml::Writer
|
41
|
+
include FunHtml::SpecElements::HTMLAllElements
|
42
|
+
|
43
|
+
# To avoid subclassing a template, `start` can be used to yeild and return a Template.
|
44
|
+
#
|
45
|
+
# html = FunHtml::Template.start do |t|
|
46
|
+
# t.doctype
|
47
|
+
# t.html do
|
48
|
+
# t.body { t.h1 "Body" }
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
def self.start(&block)
|
53
|
+
obj = new
|
54
|
+
yield obj if block
|
55
|
+
obj
|
56
|
+
end
|
57
|
+
|
58
|
+
# join an array of other templates into this template.
|
59
|
+
def join(templates)
|
60
|
+
templates.each { @__buffer << _1.render }
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# text will generate the text node, this is the only way to insert strings into the template.
|
65
|
+
#
|
66
|
+
# Template.start do |t|
|
67
|
+
# t.div { t.text "Hello" }
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
def text(value)
|
71
|
+
@__buffer << ERB::Escape.html_escape(value)
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
# generate a comment block
|
76
|
+
def comment(comment_text = nil)
|
77
|
+
write('<!--', '-->', nil, closing_char: '') { text comment_text.to_s }
|
78
|
+
end
|
79
|
+
|
80
|
+
# generate the doctype
|
81
|
+
def doctype
|
82
|
+
@__buffer << '<!DOCTYPE html>'
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
# generate a script tag. The return the script code to the block.
|
87
|
+
# The code is not escaped.
|
88
|
+
def script(attributes = nil, &block) # rubocop:disable Lint/UnusedMethodArgument
|
89
|
+
body = yield
|
90
|
+
write('<script', '</script>', attributes) { send :unsafe_text, body }
|
91
|
+
end
|
92
|
+
|
93
|
+
# attr is short cut in the template to return a new Attribute
|
94
|
+
#
|
95
|
+
# Template.start { |t| t.div(t.attr.id('my-div'), t.text '...') }
|
96
|
+
def attr(&blk)
|
97
|
+
if blk
|
98
|
+
Attribute.new(&blk)
|
99
|
+
else
|
100
|
+
Attribute.new
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/fun_html/version.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative 'spec_elements'
|
5
|
+
require 'erb/escape'
|
6
|
+
|
7
|
+
module FunHtml
|
8
|
+
# Writer collects the rendered elements and attributes into a string.
|
9
|
+
module Writer
|
10
|
+
include Kernel
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@__buffer = +''
|
14
|
+
end
|
15
|
+
|
16
|
+
# Render produces the HTML string and clears the buffer.
|
17
|
+
def render
|
18
|
+
@__buffer
|
19
|
+
ensure
|
20
|
+
# empty the buffer to prevent double rendering
|
21
|
+
@__buffer = +''
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
# used when the text should not be escaped, see `script`
|
27
|
+
def unsafe_text(value)
|
28
|
+
@__buffer << value
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
CLOSE = '>'
|
33
|
+
CLOSE_VOID = '/>'
|
34
|
+
|
35
|
+
def write(open, close, attr = nil, closing_char: CLOSE, &block)
|
36
|
+
@__buffer << open << Attribute.to_html(attr)
|
37
|
+
|
38
|
+
@__buffer << closing_char
|
39
|
+
yield self if block
|
40
|
+
@__buffer << close
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_void(open, attr = nil)
|
46
|
+
@__buffer << open << Attribute.to_html(attr) << CLOSE_VOID
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|