papercraft 0.17 → 0.21
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 +19 -0
- data/README.md +135 -51
- data/lib/papercraft/extension_proxy.rb +3 -1
- data/lib/papercraft/html.rb +32 -0
- data/lib/papercraft/json.rb +89 -36
- data/lib/papercraft/renderer.rb +12 -240
- data/lib/papercraft/tags.rb +261 -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,18 +101,20 @@ 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]
|
226
|
-
def emit(o, *a, **b)
|
108
|
+
def emit(o, *a, **b, &block)
|
227
109
|
case o
|
228
110
|
when ::Proc
|
229
111
|
Renderer.verify_proc_parameters(o, a, b)
|
112
|
+
push_emit_yield_block(block) if block
|
230
113
|
instance_exec(*a, **b, &o)
|
231
114
|
when nil
|
115
|
+
# do nothing
|
232
116
|
else
|
233
|
-
|
117
|
+
emit_object(o)
|
234
118
|
end
|
235
119
|
end
|
236
120
|
alias_method :e, :emit
|
@@ -250,140 +134,28 @@ module Papercraft
|
|
250
134
|
|
251
135
|
instance_exec(*a, **b, &block)
|
252
136
|
end
|
253
|
-
|
254
|
-
# Defers the given block to be evaluated later. Deferred evaluation allows
|
255
|
-
# Papercraft components to inject state into sibling components, regardless
|
256
|
-
# of the component's order in the container component. For example, a nested
|
257
|
-
# component may set an instance variable used by another component. This is
|
258
|
-
# an elegant solution to the problem of setting the HTML page's title, or
|
259
|
-
# adding elements to the `<head>` section. Here's how a title can be
|
260
|
-
# controlled from a nested component:
|
261
|
-
#
|
262
|
-
# layout = Papercraft.html {
|
263
|
-
# html {
|
264
|
-
# head {
|
265
|
-
# defer { title @title }
|
266
|
-
# }
|
267
|
-
# body {
|
268
|
-
# emit_yield
|
269
|
-
# }
|
270
|
-
# }
|
271
|
-
# }
|
272
|
-
#
|
273
|
-
# html.render {
|
274
|
-
# @title = 'My super page'
|
275
|
-
# h1 'content'
|
276
|
-
# }
|
277
|
-
#
|
278
|
-
# @param &block [Proc] Deferred block to be emitted
|
279
|
-
# @return [void]
|
280
|
-
def defer(&block)
|
281
|
-
if !@parts
|
282
|
-
@parts = [@buffer, block]
|
283
|
-
else
|
284
|
-
@parts << @buffer unless @buffer.empty?
|
285
|
-
@parts << block
|
286
|
-
end
|
287
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
288
|
-
end
|
289
137
|
|
290
|
-
S_LT = '<'
|
291
|
-
S_GT = '>'
|
292
|
-
S_LT_SLASH = '</'
|
293
|
-
S_SPACE_LT_SLASH = ' </'
|
294
|
-
S_SLASH_GT = '/>'
|
295
|
-
S_SPACE = ' '
|
296
|
-
S_EQUAL_QUOTE = '="'
|
297
|
-
S_QUOTE = '"'
|
298
|
-
|
299
|
-
# Emits text into the rendering buffer, escaping any special characters to
|
300
|
-
# the respective HTML entities.
|
301
|
-
#
|
302
|
-
# @param data [String] text
|
303
|
-
# @return [void]
|
304
|
-
def text(data)
|
305
|
-
@buffer << escape_text(data)
|
306
|
-
end
|
307
|
-
|
308
138
|
private
|
309
139
|
|
310
|
-
# Escapes text. This method must be overriden in descendant classes.
|
311
|
-
#
|
312
|
-
# @param text [String] text to be escaped
|
313
|
-
def escape_text(text)
|
314
|
-
raise NotImplementedError
|
315
|
-
end
|
316
|
-
|
317
140
|
# Pushes the given block onto the emit_yield stack.
|
318
141
|
#
|
319
142
|
# @param block [Proc] block
|
320
143
|
def push_emit_yield_block(block)
|
321
144
|
(@emit_yield_stack ||= []) << block
|
322
145
|
end
|
323
|
-
|
324
|
-
# Emits tag attributes into the rendering buffer
|
325
|
-
# @param props [Hash] tag attributes
|
326
|
-
# @return [void]
|
327
|
-
def emit_props(props)
|
328
|
-
props.each { |k, v|
|
329
|
-
case k
|
330
|
-
when :src, :href
|
331
|
-
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
332
|
-
EscapeUtils.escape_uri(v) << S_QUOTE
|
333
|
-
else
|
334
|
-
case v
|
335
|
-
when true
|
336
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-')
|
337
|
-
when false, nil
|
338
|
-
# emit nothing
|
339
|
-
else
|
340
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-') <<
|
341
|
-
S_EQUAL_QUOTE << v << S_QUOTE
|
342
|
-
end
|
343
|
-
end
|
344
|
-
}
|
345
|
-
end
|
346
|
-
|
347
|
-
# Renders a deferred proc by evaluating it, then adding the rendered result
|
348
|
-
# to the buffer.
|
349
|
-
#
|
350
|
-
# @param &block [Proc] deferred proc
|
351
|
-
# @return [void]
|
352
|
-
def render_deferred_proc(&block)
|
353
|
-
old_buffer = @buffer
|
354
|
-
|
355
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
356
|
-
@parts = nil
|
357
|
-
|
358
|
-
instance_eval(&block)
|
359
|
-
|
360
|
-
old_buffer << to_s
|
361
|
-
@buffer = old_buffer
|
362
|
-
end
|
363
146
|
end
|
364
147
|
|
365
148
|
# Implements an HTML renderer
|
366
149
|
class HTMLRenderer < Renderer
|
367
150
|
include HTML
|
368
|
-
|
369
|
-
private
|
370
|
-
|
371
|
-
# Escapes the given text using HTML entities.
|
372
|
-
def escape_text(text)
|
373
|
-
EscapeUtils.escape_html(text.to_s)
|
374
|
-
end
|
375
151
|
end
|
376
152
|
|
377
153
|
# Implements an XML renderer
|
378
154
|
class XMLRenderer < Renderer
|
379
|
-
|
380
|
-
|
381
|
-
# Escapes the given text using XML entities.
|
382
|
-
def escape_text(text)
|
383
|
-
EscapeUtils.escape_xml(text.to_s)
|
384
|
-
end
|
155
|
+
include XML
|
385
156
|
end
|
386
157
|
|
158
|
+
# Implements a JSON renderer
|
387
159
|
class JSONRenderer < Renderer
|
388
160
|
include JSON
|
389
161
|
end
|
@@ -0,0 +1,261 @@
|
|
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
|
+
# p method_missing: sym, self: self
|
164
|
+
tag = sym.to_s
|
165
|
+
repr = tag_repr(tag)
|
166
|
+
code = S_TAG_METHOD % {
|
167
|
+
tag: tag,
|
168
|
+
TAG: tag.upcase,
|
169
|
+
tag_pre: "<#{repr}".inspect,
|
170
|
+
tag_close: "</#{repr}>".inspect
|
171
|
+
}
|
172
|
+
self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
|
173
|
+
send(sym, *args, **opts, &block)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Emits text into the rendering buffer, escaping any special characters to
|
177
|
+
# the respective XML entities.
|
178
|
+
#
|
179
|
+
# @param data [String] text
|
180
|
+
# @return [void]
|
181
|
+
def text(data)
|
182
|
+
@buffer << escape_text(data)
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
# Emits an arbitrary object by converting it to string, then adding it to
|
188
|
+
# the internal buffer. This method is called internally by `Renderer#emit`.
|
189
|
+
#
|
190
|
+
# @param obj [Object] emitted object
|
191
|
+
# @return [void]
|
192
|
+
def emit_object(obj)
|
193
|
+
@buffer << obj.to_s
|
194
|
+
end
|
195
|
+
|
196
|
+
# Renders a deferred proc by evaluating it, then adding the rendered result
|
197
|
+
# to the buffer.
|
198
|
+
#
|
199
|
+
# @param &block [Proc] deferred proc
|
200
|
+
# @return [void]
|
201
|
+
def render_deferred_proc(&block)
|
202
|
+
old_buffer = @buffer
|
203
|
+
|
204
|
+
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
205
|
+
@parts = nil
|
206
|
+
|
207
|
+
instance_eval(&block)
|
208
|
+
|
209
|
+
old_buffer << to_s
|
210
|
+
@buffer = old_buffer
|
211
|
+
end
|
212
|
+
|
213
|
+
# Escapes text. This method must be overriden in Renderers which include
|
214
|
+
# this module.
|
215
|
+
#
|
216
|
+
# @param text [String] text to be escaped
|
217
|
+
def escape_text(text)
|
218
|
+
raise NotImplementedError
|
219
|
+
end
|
220
|
+
|
221
|
+
# Converts a tag to its string representation. This method must be overriden
|
222
|
+
# in Renderers which include this module.
|
223
|
+
#
|
224
|
+
# @param tag [Symbol, String] tag
|
225
|
+
def tag_repr(tag)
|
226
|
+
raise NotImplementedError
|
227
|
+
end
|
228
|
+
|
229
|
+
# Converts an attribute to its string representation. This method must be
|
230
|
+
# overriden in Renderers which include this module.
|
231
|
+
#
|
232
|
+
# @param att [Symbol, String] attribute
|
233
|
+
def att_repr(att)
|
234
|
+
raise NotImplementedError
|
235
|
+
end
|
236
|
+
|
237
|
+
# Emits tag attributes into the rendering buffer.
|
238
|
+
#
|
239
|
+
# @param props [Hash] tag attributes
|
240
|
+
# @return [void]
|
241
|
+
def emit_props(props)
|
242
|
+
props.each { |k, v|
|
243
|
+
case k
|
244
|
+
when :src, :href
|
245
|
+
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
246
|
+
EscapeUtils.escape_uri(v) << S_QUOTE
|
247
|
+
else
|
248
|
+
case v
|
249
|
+
when true
|
250
|
+
@buffer << S_SPACE << att_repr(k)
|
251
|
+
when false, nil
|
252
|
+
# emit nothing
|
253
|
+
else
|
254
|
+
@buffer << S_SPACE << att_repr(k) <<
|
255
|
+
S_EQUAL_QUOTE << v << S_QUOTE
|
256
|
+
end
|
257
|
+
end
|
258
|
+
}
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|