fun_html 0.1.4 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4eb896c9f7e8a1f225b04bd5b47b9280158ec76b20ce27dbea4237ef6fe833fb
4
- data.tar.gz: f56f5b4f9ad54fb6da2462bff7acfa49ec5f46fa016b88bc05746b3994dfc7b4
3
+ metadata.gz: 3e5e644d2ee9775409d54d3340ee692e557614bb1e85ef0c290317c0d00e9432
4
+ data.tar.gz: 4792b3bf1361a392a59221c43a937ce1922108d09b6f799a6c5a74466c41e760
5
5
  SHA512:
6
- metadata.gz: 3b20b2587d85eeb86fced3a578d0c3742f48d5cdda2d9f638ff53530d43647c509e10d7cbee6179d815a5eb31b49a44d828278e2a3095d8b19c80670a8730ab0
7
- data.tar.gz: 85dea75031b3cddeea68f23821c12e1c686b1d40bea8d1386f2ede461ce4db027ffc55a2ef38c34d0da9f4962dcc58df0530a2f38abbbb9e3cbf4f6db1568faf
6
+ metadata.gz: 808d8bb9950923c28e12b81c39d7be030aa2cf315d73e4b28efe157398aeaaacc349983bb785e167e59c6cad284656f6e310fff215d8022e15eebf89c1f51b81
7
+ data.tar.gz: a75a3249ea3253801467b1324b6da56c090ca919ea74309a900f80635d7a01dadfeab966844e98a20305a4b3cd0f96621ac929157309df861fddc3e511c424d4
@@ -0,0 +1,86 @@
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
+ # Merge another Attribute to create a new, combined, Attribute.
64
+ def merge(other)
65
+ self.class.new(@__buffer.merge(other.instance_variable_get(:@__buffer)))
66
+ end
67
+
68
+ # output the attributes as string
69
+ def safe_attribute
70
+ @__buffer.values.join
71
+ end
72
+
73
+ private
74
+
75
+ def write(name, value)
76
+ @__buffer[name] = "#{name}#{ERB::Escape.html_escape(value)}\""
77
+ self
78
+ end
79
+
80
+ # for boolean attributes, determin if to print the attribute or not
81
+ def write_boolean(name, print)
82
+ @__buffer[name] = print ? name : ''
83
+ self
84
+ end
85
+ end
86
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module FunHtml
4
4
  # HTML attributes autogenerated, do not edit
5
- module AttributeDefinitions
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) = write_empty(' 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) = write_empty(' autofocus', value)
23
+ def autofocus(value) = write_boolean(' autofocus', value)
24
24
  # Media will start playing automatically
25
- def autoplay(value) = write_empty(' 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) = write_empty(' 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) = write_empty(' 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) = write_empty(' default', value)
49
+ def default(value) = write_boolean(' default', value)
63
50
  # Script should execute after parsing
64
- def defer(value) = write_empty(' 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) = write_empty(' 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) = write_empty(' 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) = write_empty(' 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) = write_empty(' 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) = write_empty(' multiple', value)
109
+ def multiple(value) = write_boolean(' multiple', value)
123
110
  # Media is muted by default
124
- def muted(value) = write_empty(' 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) = write_empty(' novalidate', value)
115
+ def novalidate(value) = write_boolean(' novalidate', value)
129
116
  # Details element is expanded
130
- def open(value) = write_empty(' 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) = write_empty(' 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) = write_empty(' required', value)
133
+ def required(value) = write_boolean(' required', value)
147
134
  # List is numbered in reverse
148
- def reversed(value) = write_empty(' 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) = write_empty(' 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 NodeDefinitions
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
- write('<base', '>', attributes)
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
- write('<link', '>', attributes)
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
- write('<meta', '>', attributes)
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
- write('<wbr', '>', attributes)
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
- write('<hr', '>', attributes)
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
- write('<br', '>', attributes)
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
- write('<source', '>', attributes)
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
- write('<img', '>', attributes)
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
- write('<embed', '>', attributes)
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
- write('<track', '>', attributes)
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
- write('<area', '>', attributes)
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
- write('<col', '>', attributes)
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
- write('<input', '>', attributes)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FunHtml
4
- VERSION = '0.1.4'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -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