papercraft 0.18 → 0.22
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/CHANGELOG.md +17 -0
- data/README.md +78 -36
- data/lib/papercraft/extension_proxy.rb +16 -3
- data/lib/papercraft/html.rb +32 -0
- data/lib/papercraft/json.rb +89 -36
- data/lib/papercraft/renderer.rb +10 -239
- data/lib/papercraft/tags.rb +260 -0
- data/lib/papercraft/{component.rb → template.rb} +29 -26
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft/xml.rb +39 -0
- data/lib/papercraft.rb +17 -17
- data/papercraft.png +0 -0
- metadata +7 -3
data/lib/papercraft/renderer.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'escape_utils'
|
4
|
-
|
5
3
|
require_relative './html'
|
4
|
+
require_relative './xml'
|
6
5
|
require_relative './json'
|
7
6
|
require_relative './extension_proxy'
|
8
7
|
|
9
8
|
module Papercraft
|
10
9
|
|
11
|
-
# A Renderer renders a Papercraft
|
10
|
+
# A Renderer renders a Papercraft template into a string
|
12
11
|
class Renderer
|
13
|
-
|
12
|
+
|
14
13
|
class << self
|
15
14
|
|
16
15
|
# Verifies that the given template proc can be called with the given
|
@@ -43,9 +42,9 @@ module Papercraft
|
|
43
42
|
#
|
44
43
|
# Installs the given extensions, passed in the form of a Ruby hash mapping
|
45
44
|
# methods to extension modules. The methods will be available to all
|
46
|
-
# Papercraft
|
45
|
+
# Papercraft templates. Extension methods are executed in the context of
|
47
46
|
# the the renderer instance, so they can look just like normal proc
|
48
|
-
# components. In cases where method names in the module clash with
|
47
|
+
# components. In cases where method names in the module clash with XML
|
49
48
|
# tag names, you can use the `#tag` method to emit the relevant tag.
|
50
49
|
#
|
51
50
|
# module ComponentLibrary
|
@@ -81,131 +80,14 @@ module Papercraft
|
|
81
80
|
end
|
82
81
|
end
|
83
82
|
|
84
|
-
INITIAL_BUFFER_CAPACITY = 8192
|
85
|
-
|
86
83
|
# Initializes the renderer and evaulates the given template in the
|
87
84
|
# renderer's scope.
|
88
85
|
#
|
89
86
|
# @param &template [Proc] template block
|
90
87
|
def initialize(&template)
|
91
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
92
88
|
instance_eval(&template)
|
93
89
|
end
|
94
90
|
|
95
|
-
# Returns the rendered template.
|
96
|
-
#
|
97
|
-
# @return [String]
|
98
|
-
def to_s
|
99
|
-
if @parts
|
100
|
-
last = @buffer
|
101
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
102
|
-
parts = @parts
|
103
|
-
@parts = nil
|
104
|
-
parts.each do |p|
|
105
|
-
if Proc === p
|
106
|
-
render_deferred_proc(&p)
|
107
|
-
else
|
108
|
-
@buffer << p
|
109
|
-
end
|
110
|
-
end
|
111
|
-
@buffer << last unless last.empty?
|
112
|
-
end
|
113
|
-
@buffer
|
114
|
-
end
|
115
|
-
|
116
|
-
# The tag method template below is optimized for performance. Do not touch!
|
117
|
-
|
118
|
-
S_TAG_METHOD_LINE = __LINE__ + 2
|
119
|
-
S_TAG_METHOD = <<~EOF
|
120
|
-
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
121
|
-
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
122
|
-
|
123
|
-
def %<tag>s(text = nil, **props, &block)
|
124
|
-
if text.is_a?(Hash) && props.empty?
|
125
|
-
props = text
|
126
|
-
text = nil
|
127
|
-
end
|
128
|
-
|
129
|
-
@buffer << S_TAG_%<TAG>s_PRE
|
130
|
-
emit_props(props) unless props.empty?
|
131
|
-
|
132
|
-
if block
|
133
|
-
@buffer << S_GT
|
134
|
-
instance_eval(&block)
|
135
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
136
|
-
elsif Proc === text
|
137
|
-
@buffer << S_GT
|
138
|
-
emit(text)
|
139
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
140
|
-
elsif text
|
141
|
-
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
142
|
-
else
|
143
|
-
@buffer << S_SLASH_GT
|
144
|
-
end
|
145
|
-
end
|
146
|
-
EOF
|
147
|
-
|
148
|
-
# Emits an HTML tag with the given content, properties and optional block.
|
149
|
-
# This method is an alternative to emitting HTML tags using dynamically
|
150
|
-
# created methods. This is particularly useful when using extensions that
|
151
|
-
# have method names that clash with HTML tags, such as `button` or `a`, or
|
152
|
-
# when you need to override the behaviour of a particular HTML tag.
|
153
|
-
#
|
154
|
-
# The following two method calls have the same effect:
|
155
|
-
#
|
156
|
-
# button 'text', id: 'button1'
|
157
|
-
# tag :button, 'text', id: 'button1'
|
158
|
-
#
|
159
|
-
# @param sym [Symbol, String] HTML tag
|
160
|
-
# @param text [String, nil] tag content
|
161
|
-
# @param **props [Hash] tag attributes
|
162
|
-
# @param &block [Proc] optional inner HTML
|
163
|
-
# @return [void]
|
164
|
-
def tag(sym, text = nil, **props, &block)
|
165
|
-
if text.is_a?(Hash) && props.empty?
|
166
|
-
props = text
|
167
|
-
text = nil
|
168
|
-
end
|
169
|
-
|
170
|
-
tag = sym.to_s.tr('_', '-')
|
171
|
-
|
172
|
-
@buffer << S_LT << tag
|
173
|
-
emit_props(props) unless props.empty?
|
174
|
-
|
175
|
-
if block
|
176
|
-
@buffer << S_GT
|
177
|
-
instance_eval(&block)
|
178
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
179
|
-
elsif Proc === text
|
180
|
-
@buffer << S_GT
|
181
|
-
emit(text)
|
182
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
183
|
-
elsif text
|
184
|
-
@buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
|
185
|
-
else
|
186
|
-
@buffer << S_SLASH_GT
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
# Catches undefined tag method call and handles it by defining the method.
|
191
|
-
#
|
192
|
-
# @param sym [Symbol] HTML tag or component identifier
|
193
|
-
# @param args [Array] method arguments
|
194
|
-
# @param opts [Hash] named method arguments
|
195
|
-
# @param &block [Proc] block passed to method
|
196
|
-
# @return [void]
|
197
|
-
def method_missing(sym, *args, **opts, &block)
|
198
|
-
tag = sym.to_s
|
199
|
-
code = S_TAG_METHOD % {
|
200
|
-
tag: tag,
|
201
|
-
TAG: tag.upcase,
|
202
|
-
tag_pre: "<#{tag.tr('_', '-')}".inspect,
|
203
|
-
tag_close: "</#{tag.tr('_', '-')}>".inspect
|
204
|
-
}
|
205
|
-
self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
|
206
|
-
send(sym, *args, **opts, &block)
|
207
|
-
end
|
208
|
-
|
209
91
|
# Emits the given object into the rendering buffer. If the given object is a
|
210
92
|
# proc or a component, `emit` will passes any additional arguments and named
|
211
93
|
# arguments to the object when rendering it. If the given object is nil,
|
@@ -219,7 +101,7 @@ module Papercraft
|
|
219
101
|
#
|
220
102
|
# Papercraft.html { emit nil }.render #=> ""
|
221
103
|
#
|
222
|
-
# @param o [Proc, Papercraft::
|
104
|
+
# @param o [Proc, Papercraft::Template, String] emitted object
|
223
105
|
# @param *a [Array<any>] arguments to pass to a proc
|
224
106
|
# @param **b [Hash] named arguments to pass to a proc
|
225
107
|
# @return [void]
|
@@ -230,8 +112,9 @@ module Papercraft
|
|
230
112
|
push_emit_yield_block(block) if block
|
231
113
|
instance_exec(*a, **b, &o)
|
232
114
|
when nil
|
115
|
+
# do nothing
|
233
116
|
else
|
234
|
-
|
117
|
+
emit_object(o)
|
235
118
|
end
|
236
119
|
end
|
237
120
|
alias_method :e, :emit
|
@@ -251,140 +134,28 @@ module Papercraft
|
|
251
134
|
|
252
135
|
instance_exec(*a, **b, &block)
|
253
136
|
end
|
254
|
-
|
255
|
-
# Defers the given block to be evaluated later. Deferred evaluation allows
|
256
|
-
# Papercraft components to inject state into sibling components, regardless
|
257
|
-
# of the component's order in the container component. For example, a nested
|
258
|
-
# component may set an instance variable used by another component. This is
|
259
|
-
# an elegant solution to the problem of setting the HTML page's title, or
|
260
|
-
# adding elements to the `<head>` section. Here's how a title can be
|
261
|
-
# controlled from a nested component:
|
262
|
-
#
|
263
|
-
# layout = Papercraft.html {
|
264
|
-
# html {
|
265
|
-
# head {
|
266
|
-
# defer { title @title }
|
267
|
-
# }
|
268
|
-
# body {
|
269
|
-
# emit_yield
|
270
|
-
# }
|
271
|
-
# }
|
272
|
-
# }
|
273
|
-
#
|
274
|
-
# html.render {
|
275
|
-
# @title = 'My super page'
|
276
|
-
# h1 'content'
|
277
|
-
# }
|
278
|
-
#
|
279
|
-
# @param &block [Proc] Deferred block to be emitted
|
280
|
-
# @return [void]
|
281
|
-
def defer(&block)
|
282
|
-
if !@parts
|
283
|
-
@parts = [@buffer, block]
|
284
|
-
else
|
285
|
-
@parts << @buffer unless @buffer.empty?
|
286
|
-
@parts << block
|
287
|
-
end
|
288
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
289
|
-
end
|
290
137
|
|
291
|
-
S_LT = '<'
|
292
|
-
S_GT = '>'
|
293
|
-
S_LT_SLASH = '</'
|
294
|
-
S_SPACE_LT_SLASH = ' </'
|
295
|
-
S_SLASH_GT = '/>'
|
296
|
-
S_SPACE = ' '
|
297
|
-
S_EQUAL_QUOTE = '="'
|
298
|
-
S_QUOTE = '"'
|
299
|
-
|
300
|
-
# Emits text into the rendering buffer, escaping any special characters to
|
301
|
-
# the respective HTML entities.
|
302
|
-
#
|
303
|
-
# @param data [String] text
|
304
|
-
# @return [void]
|
305
|
-
def text(data)
|
306
|
-
@buffer << escape_text(data)
|
307
|
-
end
|
308
|
-
|
309
138
|
private
|
310
139
|
|
311
|
-
# Escapes text. This method must be overriden in descendant classes.
|
312
|
-
#
|
313
|
-
# @param text [String] text to be escaped
|
314
|
-
def escape_text(text)
|
315
|
-
raise NotImplementedError
|
316
|
-
end
|
317
|
-
|
318
140
|
# Pushes the given block onto the emit_yield stack.
|
319
141
|
#
|
320
142
|
# @param block [Proc] block
|
321
143
|
def push_emit_yield_block(block)
|
322
144
|
(@emit_yield_stack ||= []) << block
|
323
145
|
end
|
324
|
-
|
325
|
-
# Emits tag attributes into the rendering buffer
|
326
|
-
# @param props [Hash] tag attributes
|
327
|
-
# @return [void]
|
328
|
-
def emit_props(props)
|
329
|
-
props.each { |k, v|
|
330
|
-
case k
|
331
|
-
when :src, :href
|
332
|
-
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
333
|
-
EscapeUtils.escape_uri(v) << S_QUOTE
|
334
|
-
else
|
335
|
-
case v
|
336
|
-
when true
|
337
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-')
|
338
|
-
when false, nil
|
339
|
-
# emit nothing
|
340
|
-
else
|
341
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-') <<
|
342
|
-
S_EQUAL_QUOTE << v << S_QUOTE
|
343
|
-
end
|
344
|
-
end
|
345
|
-
}
|
346
|
-
end
|
347
|
-
|
348
|
-
# Renders a deferred proc by evaluating it, then adding the rendered result
|
349
|
-
# to the buffer.
|
350
|
-
#
|
351
|
-
# @param &block [Proc] deferred proc
|
352
|
-
# @return [void]
|
353
|
-
def render_deferred_proc(&block)
|
354
|
-
old_buffer = @buffer
|
355
|
-
|
356
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
357
|
-
@parts = nil
|
358
|
-
|
359
|
-
instance_eval(&block)
|
360
|
-
|
361
|
-
old_buffer << to_s
|
362
|
-
@buffer = old_buffer
|
363
|
-
end
|
364
146
|
end
|
365
147
|
|
366
148
|
# Implements an HTML renderer
|
367
149
|
class HTMLRenderer < Renderer
|
368
150
|
include HTML
|
369
|
-
|
370
|
-
private
|
371
|
-
|
372
|
-
# Escapes the given text using HTML entities.
|
373
|
-
def escape_text(text)
|
374
|
-
EscapeUtils.escape_html(text.to_s)
|
375
|
-
end
|
376
151
|
end
|
377
152
|
|
378
153
|
# Implements an XML renderer
|
379
154
|
class XMLRenderer < Renderer
|
380
|
-
|
381
|
-
|
382
|
-
# Escapes the given text using XML entities.
|
383
|
-
def escape_text(text)
|
384
|
-
EscapeUtils.escape_xml(text.to_s)
|
385
|
-
end
|
155
|
+
include XML
|
386
156
|
end
|
387
157
|
|
158
|
+
# Implements a JSON renderer
|
388
159
|
class JSONRenderer < Renderer
|
389
160
|
include JSON
|
390
161
|
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Papercraft
|
4
|
+
# Markup (HTML/XML) extensions
|
5
|
+
module Tags
|
6
|
+
S_LT = '<'
|
7
|
+
S_GT = '>'
|
8
|
+
S_LT_SLASH = '</'
|
9
|
+
S_SPACE_LT_SLASH = ' </'
|
10
|
+
S_SLASH_GT = '/>'
|
11
|
+
S_SPACE = ' '
|
12
|
+
S_EQUAL_QUOTE = '="'
|
13
|
+
S_QUOTE = '"'
|
14
|
+
|
15
|
+
# The tag method template below is optimized for performance. Do not touch!
|
16
|
+
|
17
|
+
S_TAG_METHOD_LINE = __LINE__ + 2
|
18
|
+
S_TAG_METHOD = <<~EOF
|
19
|
+
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
20
|
+
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
21
|
+
|
22
|
+
def %<tag>s(text = nil, **props, &block)
|
23
|
+
if text.is_a?(Hash) && props.empty?
|
24
|
+
props = text
|
25
|
+
text = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
@buffer << S_TAG_%<TAG>s_PRE
|
29
|
+
emit_props(props) unless props.empty?
|
30
|
+
|
31
|
+
if block
|
32
|
+
@buffer << S_GT
|
33
|
+
instance_eval(&block)
|
34
|
+
@buffer << S_TAG_%<TAG>s_CLOSE
|
35
|
+
elsif Proc === text
|
36
|
+
@buffer << S_GT
|
37
|
+
emit(text)
|
38
|
+
@buffer << S_TAG_%<TAG>s_CLOSE
|
39
|
+
elsif text
|
40
|
+
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
41
|
+
else
|
42
|
+
@buffer << S_SLASH_GT
|
43
|
+
end
|
44
|
+
end
|
45
|
+
EOF
|
46
|
+
|
47
|
+
INITIAL_BUFFER_CAPACITY = 8192
|
48
|
+
|
49
|
+
# Initializes a tag renderer.
|
50
|
+
def initialize(&template)
|
51
|
+
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the rendered template.
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
def to_s
|
59
|
+
if @parts
|
60
|
+
last = @buffer
|
61
|
+
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
62
|
+
parts = @parts
|
63
|
+
@parts = nil
|
64
|
+
parts.each do |p|
|
65
|
+
if Proc === p
|
66
|
+
render_deferred_proc(&p)
|
67
|
+
else
|
68
|
+
@buffer << p
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@buffer << last unless last.empty?
|
72
|
+
end
|
73
|
+
@buffer
|
74
|
+
end
|
75
|
+
|
76
|
+
# Defers the given block to be evaluated later. Deferred evaluation allows
|
77
|
+
# Papercraft templates to inject state into sibling components, regardless
|
78
|
+
# of the component's order in the container component. For example, a nested
|
79
|
+
# component may set an instance variable used by another component. This is
|
80
|
+
# an elegant solution to the problem of setting the XML page's title, or
|
81
|
+
# adding elements to the `<head>` section. Here's how a title can be
|
82
|
+
# controlled from a nested component:
|
83
|
+
#
|
84
|
+
# layout = Papercraft.html {
|
85
|
+
# html {
|
86
|
+
# head {
|
87
|
+
# defer { title @title }
|
88
|
+
# }
|
89
|
+
# body {
|
90
|
+
# emit_yield
|
91
|
+
# }
|
92
|
+
# }
|
93
|
+
# }
|
94
|
+
#
|
95
|
+
# html.render {
|
96
|
+
# @title = 'My super page'
|
97
|
+
# h1 'content'
|
98
|
+
# }
|
99
|
+
#
|
100
|
+
# @param &block [Proc] Deferred block to be emitted
|
101
|
+
# @return [void]
|
102
|
+
def defer(&block)
|
103
|
+
if !@parts
|
104
|
+
@parts = [@buffer, block]
|
105
|
+
else
|
106
|
+
@parts << @buffer unless @buffer.empty?
|
107
|
+
@parts << block
|
108
|
+
end
|
109
|
+
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# Emits an XML tag with the given content, properties and optional block.
|
114
|
+
# This method is an alternative to emitting XML tags using dynamically
|
115
|
+
# created methods. This is particularly useful when using extensions that
|
116
|
+
# have method names that clash with XML tags, such as `button` or `a`, or
|
117
|
+
# when you need to override the behaviour of a particular XML tag.
|
118
|
+
#
|
119
|
+
# The following two method calls have the same effect:
|
120
|
+
#
|
121
|
+
# button 'text', id: 'button1'
|
122
|
+
# tag :button, 'text', id: 'button1'
|
123
|
+
#
|
124
|
+
# @param sym [Symbol, String] XML tag
|
125
|
+
# @param text [String, nil] tag content
|
126
|
+
# @param **props [Hash] tag attributes
|
127
|
+
# @param &block [Proc] optional inner XML
|
128
|
+
# @return [void]
|
129
|
+
def tag(sym, text = nil, **props, &block)
|
130
|
+
if text.is_a?(Hash) && props.empty?
|
131
|
+
props = text
|
132
|
+
text = nil
|
133
|
+
end
|
134
|
+
|
135
|
+
tag = tag_repr(sym)
|
136
|
+
|
137
|
+
@buffer << S_LT << tag
|
138
|
+
emit_props(props) unless props.empty?
|
139
|
+
|
140
|
+
if block
|
141
|
+
@buffer << S_GT
|
142
|
+
instance_eval(&block)
|
143
|
+
@buffer << S_LT_SLASH << tag << S_GT
|
144
|
+
elsif Proc === text
|
145
|
+
@buffer << S_GT
|
146
|
+
emit(text)
|
147
|
+
@buffer << S_LT_SLASH << tag << S_GT
|
148
|
+
elsif text
|
149
|
+
@buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
|
150
|
+
else
|
151
|
+
@buffer << S_SLASH_GT
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Catches undefined tag method call and handles it by defining the method.
|
156
|
+
#
|
157
|
+
# @param sym [Symbol] XML tag or component identifier
|
158
|
+
# @param args [Array] method arguments
|
159
|
+
# @param opts [Hash] named method arguments
|
160
|
+
# @param &block [Proc] block passed to method
|
161
|
+
# @return [void]
|
162
|
+
def method_missing(sym, *args, **opts, &block)
|
163
|
+
tag = sym.to_s
|
164
|
+
repr = tag_repr(tag)
|
165
|
+
code = S_TAG_METHOD % {
|
166
|
+
tag: tag,
|
167
|
+
TAG: tag.upcase,
|
168
|
+
tag_pre: "<#{repr}".inspect,
|
169
|
+
tag_close: "</#{repr}>".inspect
|
170
|
+
}
|
171
|
+
self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
|
172
|
+
send(sym, *args, **opts, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Emits text into the rendering buffer, escaping any special characters to
|
176
|
+
# the respective XML entities.
|
177
|
+
#
|
178
|
+
# @param data [String] text
|
179
|
+
# @return [void]
|
180
|
+
def text(data)
|
181
|
+
@buffer << escape_text(data)
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
# Emits an arbitrary object by converting it to string, then adding it to
|
187
|
+
# the internal buffer. This method is called internally by `Renderer#emit`.
|
188
|
+
#
|
189
|
+
# @param obj [Object] emitted object
|
190
|
+
# @return [void]
|
191
|
+
def emit_object(obj)
|
192
|
+
@buffer << obj.to_s
|
193
|
+
end
|
194
|
+
|
195
|
+
# Renders a deferred proc by evaluating it, then adding the rendered result
|
196
|
+
# to the buffer.
|
197
|
+
#
|
198
|
+
# @param &block [Proc] deferred proc
|
199
|
+
# @return [void]
|
200
|
+
def render_deferred_proc(&block)
|
201
|
+
old_buffer = @buffer
|
202
|
+
|
203
|
+
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
204
|
+
@parts = nil
|
205
|
+
|
206
|
+
instance_eval(&block)
|
207
|
+
|
208
|
+
old_buffer << to_s
|
209
|
+
@buffer = old_buffer
|
210
|
+
end
|
211
|
+
|
212
|
+
# Escapes text. This method must be overriden in Renderers which include
|
213
|
+
# this module.
|
214
|
+
#
|
215
|
+
# @param text [String] text to be escaped
|
216
|
+
def escape_text(text)
|
217
|
+
raise NotImplementedError
|
218
|
+
end
|
219
|
+
|
220
|
+
# Converts a tag to its string representation. This method must be overriden
|
221
|
+
# in Renderers which include this module.
|
222
|
+
#
|
223
|
+
# @param tag [Symbol, String] tag
|
224
|
+
def tag_repr(tag)
|
225
|
+
raise NotImplementedError
|
226
|
+
end
|
227
|
+
|
228
|
+
# Converts an attribute to its string representation. This method must be
|
229
|
+
# overriden in Renderers which include this module.
|
230
|
+
#
|
231
|
+
# @param att [Symbol, String] attribute
|
232
|
+
def att_repr(att)
|
233
|
+
raise NotImplementedError
|
234
|
+
end
|
235
|
+
|
236
|
+
# Emits tag attributes into the rendering buffer.
|
237
|
+
#
|
238
|
+
# @param props [Hash] tag attributes
|
239
|
+
# @return [void]
|
240
|
+
def emit_props(props)
|
241
|
+
props.each { |k, v|
|
242
|
+
case k
|
243
|
+
when :src, :href
|
244
|
+
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
245
|
+
EscapeUtils.escape_uri(v) << S_QUOTE
|
246
|
+
else
|
247
|
+
case v
|
248
|
+
when true
|
249
|
+
@buffer << S_SPACE << att_repr(k)
|
250
|
+
when false, nil
|
251
|
+
# emit nothing
|
252
|
+
else
|
253
|
+
@buffer << S_SPACE << att_repr(k) <<
|
254
|
+
S_EQUAL_QUOTE << v << S_QUOTE
|
255
|
+
end
|
256
|
+
end
|
257
|
+
}
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|