papercraft 2.24 → 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/CHANGELOG.md +10 -0
- data/README.md +6 -4
- data/lib/papercraft/compiler.rb +33 -13
- data/lib/papercraft/proc_ext.rb +12 -54
- data/lib/papercraft/template.rb +19 -6
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +92 -3
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ac74fb8004af29f1b15e8ca36de8039d3e47c6642105032fa1a51ec46bda100a
|
|
4
|
+
data.tar.gz: 2b5fcf6bc6d472c33033381577b62e0875594a56e3a8f9f1d769b64d4a6ec2ce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 390c81c371d17216134e931b26e1d6c9728c54e2c8db220b0d21761f9ed7d21b12f784b5642e7473e55c1f65f2bad4fe07f9afa09d78c04b7d54569ed7cc5ebf
|
|
7
|
+
data.tar.gz: 9801c61a0a9788a563396bed4064b0b0f4aeacb82e43677c52bfc83ed1d25d7b001af4915c3bdb86b6191b1f5681bd1b54460fbaaa626bfde32e471cb12a830f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
# 3.0.0 2025-10-19
|
|
2
|
+
|
|
3
|
+
- Improve implementation of `Papercraft.apply`
|
|
4
|
+
- Add support for rendering self-closing XML tags
|
|
5
|
+
- Streamline Papercraft API
|
|
6
|
+
- Add support for `Papercraft.render { ... }`
|
|
7
|
+
- Prefix internal Proc extensions with `__papercraft_`
|
|
8
|
+
- Change API to use `Papercraft.html` instead of `Proc#render`. Same for
|
|
9
|
+
`apple`, `render_xml` etc.
|
|
10
|
+
|
|
1
11
|
# 2.24 2025-10-14
|
|
2
12
|
|
|
3
13
|
- Update gem links
|
data/README.md
CHANGED
|
@@ -21,10 +21,12 @@
|
|
|
21
21
|
```ruby
|
|
22
22
|
require 'papercraft'
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
Papercraft.html {
|
|
25
|
+
div {
|
|
26
|
+
h1 "Hello from Papercraft!"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
#=> "<div><h1>Hello from Papercraft</h1></div>"
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
Papercraft is a templating engine for dynamically producing HTML in Ruby apps.
|
data/lib/papercraft/compiler.rb
CHANGED
|
@@ -164,12 +164,23 @@ module Papercraft
|
|
|
164
164
|
# adjust_whitespace(node.location)
|
|
165
165
|
is_void = is_void_element?(tag)
|
|
166
166
|
is_raw_inner_text = is_raw_inner_text_element?(tag)
|
|
167
|
+
is_empty = !node.block && !node.inner_text
|
|
167
168
|
|
|
168
|
-
if is_void &&
|
|
169
|
+
if is_void && !is_empty
|
|
169
170
|
raise Papercraft::Error, "Void element #{tag} cannot contain child nodes or inner text"
|
|
170
171
|
end
|
|
171
172
|
|
|
172
|
-
|
|
173
|
+
if @mode == :xml && is_empty
|
|
174
|
+
emit_html(
|
|
175
|
+
node.tag_location,
|
|
176
|
+
format_xml_tag_self_closing(node.tag_location, tag, node.attributes)
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
emit_html(
|
|
182
|
+
node.tag_location, format_html_tag_open(node.tag_location, tag, node.attributes)
|
|
183
|
+
)
|
|
173
184
|
return if is_void
|
|
174
185
|
|
|
175
186
|
case node.block
|
|
@@ -178,7 +189,7 @@ module Papercraft
|
|
|
178
189
|
when Prism::BlockArgumentNode
|
|
179
190
|
flush_html_parts!
|
|
180
191
|
adjust_whitespace(node.block)
|
|
181
|
-
emit("; #{format_code(node.block.expression)}.
|
|
192
|
+
emit("; #{format_code(node.block.expression)}.__papercraft_compiled_proc.(__buffer__)")
|
|
182
193
|
end
|
|
183
194
|
|
|
184
195
|
if node.inner_text
|
|
@@ -213,7 +224,7 @@ module Papercraft
|
|
|
213
224
|
emit(format_code(node.call_node.receiver))
|
|
214
225
|
emit('::')
|
|
215
226
|
end
|
|
216
|
-
emit("#{node.call_node.name}.
|
|
227
|
+
emit("#{node.call_node.name}.__papercraft_compiled_proc.(__buffer__")
|
|
217
228
|
if node.call_node.arguments
|
|
218
229
|
emit(', ')
|
|
219
230
|
visit(node.call_node.arguments)
|
|
@@ -229,17 +240,17 @@ module Papercraft
|
|
|
229
240
|
args = node.call_node.arguments.arguments
|
|
230
241
|
first_arg = args.first
|
|
231
242
|
|
|
232
|
-
block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.
|
|
243
|
+
block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.__papercraft_compiled!)"
|
|
233
244
|
block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
|
|
234
245
|
|
|
235
246
|
flush_html_parts!
|
|
236
247
|
adjust_whitespace(node.location)
|
|
237
248
|
|
|
238
249
|
if args.length == 1
|
|
239
|
-
emit("; #{format_code(first_arg)}.
|
|
250
|
+
emit("; #{format_code(first_arg)}.__papercraft_compiled_proc.(__buffer__#{block_embed})")
|
|
240
251
|
else
|
|
241
252
|
args_code = format_code_comma_separated_nodes(args[1..])
|
|
242
|
-
emit("; #{format_code(first_arg)}.
|
|
253
|
+
emit("; #{format_code(first_arg)}.__papercraft_compiled_proc.(__buffer__, #{args_code}#{block_embed})")
|
|
243
254
|
end
|
|
244
255
|
end
|
|
245
256
|
|
|
@@ -336,7 +347,7 @@ module Papercraft
|
|
|
336
347
|
def visit_extension_tag_node(node)
|
|
337
348
|
flush_html_parts!
|
|
338
349
|
adjust_whitespace(node.location)
|
|
339
|
-
emit("; Papercraft::Extensions[#{node.tag.inspect}].
|
|
350
|
+
emit("; Papercraft::Extensions[#{node.tag.inspect}].__papercraft_compiled_proc.(__buffer__")
|
|
340
351
|
if node.call_node.arguments
|
|
341
352
|
emit(', ')
|
|
342
353
|
visit(node.call_node.arguments)
|
|
@@ -367,7 +378,7 @@ module Papercraft
|
|
|
367
378
|
end
|
|
368
379
|
block_params = block_params.empty? ? '' : ", #{block_params.join(', ')}"
|
|
369
380
|
|
|
370
|
-
emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).
|
|
381
|
+
emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).__papercraft_compiled!")
|
|
371
382
|
end
|
|
372
383
|
emit(")")
|
|
373
384
|
end
|
|
@@ -382,7 +393,7 @@ module Papercraft
|
|
|
382
393
|
guard = @render_yield_used ?
|
|
383
394
|
'' : "; raise(LocalJumpError, 'no block given (render_yield)') if !__block__"
|
|
384
395
|
@render_yield_used = true
|
|
385
|
-
emit("#{guard}; __block__.
|
|
396
|
+
emit("#{guard}; __block__.__papercraft_compiled_proc.(__buffer__")
|
|
386
397
|
if node.call_node.arguments
|
|
387
398
|
emit(', ')
|
|
388
399
|
visit(node.call_node.arguments)
|
|
@@ -398,7 +409,7 @@ module Papercraft
|
|
|
398
409
|
flush_html_parts!
|
|
399
410
|
adjust_whitespace(node.location)
|
|
400
411
|
@render_children_used = true
|
|
401
|
-
emit("; __block__&.
|
|
412
|
+
emit("; __block__&.__papercraft_compiled_proc&.(__buffer__")
|
|
402
413
|
if node.call_node.arguments
|
|
403
414
|
emit(', ')
|
|
404
415
|
visit(node.call_node.arguments)
|
|
@@ -410,7 +421,7 @@ module Papercraft
|
|
|
410
421
|
flush_html_parts!
|
|
411
422
|
adjust_whitespace(node.location)
|
|
412
423
|
|
|
413
|
-
emit("; #{node.call_node.receiver.name}.
|
|
424
|
+
emit("; #{node.call_node.receiver.name}.__papercraft_compiled_proc.(__buffer__")
|
|
414
425
|
if node.call_node.arguments
|
|
415
426
|
emit(', ')
|
|
416
427
|
visit(node.call_node.arguments)
|
|
@@ -418,7 +429,7 @@ module Papercraft
|
|
|
418
429
|
if node.call_node.block
|
|
419
430
|
emit(", &(->")
|
|
420
431
|
visit(node.call_node.block)
|
|
421
|
-
emit(").
|
|
432
|
+
emit(").__papercraft_compiled_proc")
|
|
422
433
|
end
|
|
423
434
|
emit(")")
|
|
424
435
|
end
|
|
@@ -489,6 +500,15 @@ module Papercraft
|
|
|
489
500
|
RAW_INNER_TEXT_TAGS.include?(tag.to_s)
|
|
490
501
|
end
|
|
491
502
|
|
|
503
|
+
def format_xml_tag_self_closing(loc, tag, attributes)
|
|
504
|
+
tag = convert_tag(tag)
|
|
505
|
+
if attributes && attributes&.elements.size > 0 || @@html_debug_attribute_injector
|
|
506
|
+
"<#{tag} #{format_html_attributes(loc, attributes)}/>"
|
|
507
|
+
else
|
|
508
|
+
"<#{tag}/>"
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
492
512
|
# Formats an open tag with optional attributes.
|
|
493
513
|
#
|
|
494
514
|
# @param loc [Prism::Location] tag location
|
data/lib/papercraft/proc_ext.rb
CHANGED
|
@@ -8,76 +8,34 @@ module Papercraft
|
|
|
8
8
|
# Returns true if proc is marked as compiled.
|
|
9
9
|
#
|
|
10
10
|
# @return [bool] is the proc marked as compiled
|
|
11
|
-
def
|
|
12
|
-
@
|
|
11
|
+
def __papercraft_compiled?
|
|
12
|
+
@__papercraft_compiled
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
# Marks the proc as compiled, i.e. can render directly and takes a string
|
|
16
16
|
# buffer as first argument.
|
|
17
17
|
#
|
|
18
18
|
# @return [self]
|
|
19
|
-
def
|
|
20
|
-
@
|
|
19
|
+
def __papercraft_compiled!
|
|
20
|
+
@__papercraft_compiled = true
|
|
21
21
|
self
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
# Returns the compiled proc for the
|
|
24
|
+
# Returns the compiled proc for the proc. If marked as compiled, returns
|
|
25
25
|
# self.
|
|
26
26
|
#
|
|
27
27
|
# @param mode [Symbol] compilation mode (:html, :xml)
|
|
28
28
|
# @return [Proc] compiled proc or self
|
|
29
|
-
def
|
|
30
|
-
@
|
|
29
|
+
def __papercraft_compiled_proc(mode: :html)
|
|
30
|
+
@__papercraft_compiled_proc ||= @__papercraft_compiled ?
|
|
31
|
+
self : Papercraft.compile(self, mode:)
|
|
31
32
|
end
|
|
32
33
|
|
|
33
|
-
#
|
|
34
|
+
# Returns the render cache for the proc.
|
|
34
35
|
#
|
|
35
|
-
# @return [
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
rescue Exception => e
|
|
39
|
-
e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Renders the proc to XML with the given arguments.
|
|
43
|
-
#
|
|
44
|
-
# @return [String] XML string
|
|
45
|
-
def render_xml(*a, **b, &c)
|
|
46
|
-
__compiled_proc__(mode: :xml).(+'', *a, **b, &c)
|
|
47
|
-
rescue Exception => e
|
|
48
|
-
e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Returns a proc that applies the given arguments to the original proc. The
|
|
52
|
-
# returned proc calls the *compiled* form of the proc, merging the
|
|
53
|
-
# positional and keywords parameters passed to `#apply` with parameters
|
|
54
|
-
# passed to the applied proc. If a block is given, it is wrapped in a proc
|
|
55
|
-
# that passed merged parameters to the block.
|
|
56
|
-
#
|
|
57
|
-
# @param *pos1 [Array<any>] applied positional parameters
|
|
58
|
-
# @param **kw1 [Hash<any, any] applied keyword parameters
|
|
59
|
-
# @return [Proc] applied proc
|
|
60
|
-
def apply(*pos1, **kw1, &block)
|
|
61
|
-
compiled = __compiled_proc__
|
|
62
|
-
c_compiled = block&.__compiled_proc__
|
|
63
|
-
|
|
64
|
-
->(__buffer__, *pos2, **kw2, &block2) {
|
|
65
|
-
c_proc = c_compiled && ->(__buffer__, *pos3, **kw3) {
|
|
66
|
-
c_compiled.(__buffer__, *pos3, **kw3, &block2)
|
|
67
|
-
}.__compiled__!
|
|
68
|
-
|
|
69
|
-
compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &c_proc)
|
|
70
|
-
}.__compiled__!
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Caches and returns the rendered HTML for the template with the given
|
|
74
|
-
# arguments.
|
|
75
|
-
#
|
|
76
|
-
# @param key [any] Cache key
|
|
77
|
-
# @return [String] HTML string
|
|
78
|
-
def render_cache(key, *args, **kargs, &block)
|
|
79
|
-
@render_cache ||= {}
|
|
80
|
-
@render_cache[key] ||= render(*args, **kargs, &block)
|
|
36
|
+
# @return [Hash] cache hash
|
|
37
|
+
def __papercraft_render_cache
|
|
38
|
+
@__papercraft_render_cache ||= {}
|
|
81
39
|
end
|
|
82
40
|
end
|
|
83
41
|
end
|
data/lib/papercraft/template.rb
CHANGED
|
@@ -8,21 +8,34 @@ module Papercraft
|
|
|
8
8
|
|
|
9
9
|
# @param proc [Proc] template proc
|
|
10
10
|
# @param mode [Symbol] mode (:html, :xml)
|
|
11
|
-
def initialize(proc, mode: :html)
|
|
12
|
-
@proc = proc
|
|
11
|
+
def initialize(proc = nil, mode: :html, &block)
|
|
12
|
+
@proc = proc || block
|
|
13
|
+
raise ArgumentError, "No template proc given" if !@proc
|
|
14
|
+
|
|
13
15
|
@mode = mode
|
|
14
16
|
end
|
|
15
17
|
|
|
18
|
+
# Renders the template.
|
|
19
|
+
#
|
|
20
|
+
# @return [String] generated HTML
|
|
16
21
|
def render(*, **, &)
|
|
17
|
-
(mode == :xml) ? @proc
|
|
22
|
+
(mode == :xml) ? Papercraft.xml(@proc, *, **, &) : Papercraft.html(@proc, *, **, &)
|
|
18
23
|
end
|
|
24
|
+
alias_method :call, :render
|
|
19
25
|
|
|
26
|
+
# Applies the given parameters and block to the template, returning an
|
|
27
|
+
# applied template.
|
|
28
|
+
#
|
|
29
|
+
# @return [Papercraft::Template] applied template
|
|
20
30
|
def apply(*, **, &)
|
|
21
|
-
Template.new(
|
|
31
|
+
Template.new(Papercraft.apply(@proc, *, **, &), mode: @mode)
|
|
22
32
|
end
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
# Returns the compiled proc for the template.
|
|
35
|
+
#
|
|
36
|
+
# @return [Proc] compiled proc
|
|
37
|
+
def __papercraft_compiled_proc
|
|
38
|
+
@proc.__papercraft_compiled_proc(mode: @mode)
|
|
26
39
|
end
|
|
27
40
|
end
|
|
28
41
|
end
|
data/lib/papercraft/version.rb
CHANGED
data/lib/papercraft.rb
CHANGED
|
@@ -114,10 +114,11 @@ module Papercraft
|
|
|
114
114
|
# @param opts [Hash] Kramdown option overrides
|
|
115
115
|
# @return [Kramdown::Document] Kramdown document
|
|
116
116
|
def markdown_doc(markdown, **opts)
|
|
117
|
-
@markdown_deps_loaded ||=
|
|
117
|
+
@markdown_deps_loaded ||= begin
|
|
118
118
|
require 'kramdown'
|
|
119
119
|
require 'rouge'
|
|
120
120
|
require 'kramdown-parser-gfm'
|
|
121
|
+
true
|
|
121
122
|
end
|
|
122
123
|
|
|
123
124
|
opts = default_kramdown_options.merge(opts)
|
|
@@ -168,7 +169,7 @@ module Papercraft
|
|
|
168
169
|
# @return [Array<String>] source map
|
|
169
170
|
def source_map(proc)
|
|
170
171
|
loc = proc.source_location
|
|
171
|
-
fn = proc.
|
|
172
|
+
fn = proc.__papercraft_compiled? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
|
|
172
173
|
Papercraft::Compiler.source_map_store[fn]
|
|
173
174
|
end
|
|
174
175
|
|
|
@@ -186,8 +187,96 @@ module Papercraft
|
|
|
186
187
|
# @param mode [Symbol] compilation mode (:html, :xml)
|
|
187
188
|
# @return [Proc] compiled proc
|
|
188
189
|
def compile(proc, mode: :html)
|
|
189
|
-
Papercraft::Compiler.compile(proc, mode:).
|
|
190
|
+
Papercraft::Compiler.compile(proc, mode:).__papercraft_compiled!
|
|
190
191
|
rescue Sirop::Error
|
|
191
192
|
raise Papercraft::Error, "Can't compile eval'd template"
|
|
192
193
|
end
|
|
194
|
+
|
|
195
|
+
# Renders the given template to HTML with the given arguments. The template
|
|
196
|
+
# can be passed either as the first parameter, or as a block, if no parameter
|
|
197
|
+
# is given.
|
|
198
|
+
#
|
|
199
|
+
# @param template [Proc] template proc
|
|
200
|
+
# @return [String] HTML string
|
|
201
|
+
def html(template = nil, *pos, **kw, &block)
|
|
202
|
+
if !template
|
|
203
|
+
template = block
|
|
204
|
+
elsif template.is_a?(Template)
|
|
205
|
+
template = template.proc
|
|
206
|
+
end
|
|
207
|
+
raise ArgumentError, "No template given" if !template
|
|
208
|
+
|
|
209
|
+
template.__papercraft_compiled_proc.(+'', *pos, **kw, &block)
|
|
210
|
+
rescue Exception => e
|
|
211
|
+
e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Renders the given template to XML with the given arguments. The template can
|
|
215
|
+
# be passed either as the first parameter, or as a block, if no parameter is
|
|
216
|
+
# given.
|
|
217
|
+
#
|
|
218
|
+
# @param template [Proc] template proc
|
|
219
|
+
# @return [String] XML string
|
|
220
|
+
def xml(template = nil, *pos, **kw, &block)
|
|
221
|
+
if !template
|
|
222
|
+
template = block
|
|
223
|
+
elsif template.is_a?(Template)
|
|
224
|
+
template = template.proc
|
|
225
|
+
end
|
|
226
|
+
raise ArgumentError, "No template given" if !template
|
|
227
|
+
|
|
228
|
+
template = template.proc if template.is_a?(Template)
|
|
229
|
+
template.__papercraft_compiled_proc(mode: :xml).(+'', *pos, **kw, &block)
|
|
230
|
+
rescue Exception => e
|
|
231
|
+
e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Returns a proc that applies the given arguments to the original proc. The
|
|
235
|
+
# returned proc calls the *compiled* form of the proc, merging the
|
|
236
|
+
# positional and keywords parameters passed to `#apply` with parameters
|
|
237
|
+
# passed to the applied proc. If a block is given, it is wrapped in a proc
|
|
238
|
+
# that passed merged parameters to the block.
|
|
239
|
+
#
|
|
240
|
+
# @param template [Proc] template proc
|
|
241
|
+
# @param *pos1 [Array<any>] applied positional parameters
|
|
242
|
+
# @param **kw1 [Hash<any, any] applied keyword parameters
|
|
243
|
+
# @return [Proc] applied proc
|
|
244
|
+
def apply(template, *pos1, **kw1, &block1)
|
|
245
|
+
template = template.proc if template.is_a?(Template)
|
|
246
|
+
compiled = template.__papercraft_compiled_proc
|
|
247
|
+
block1_compiled = block1&.__papercraft_compiled_proc
|
|
248
|
+
|
|
249
|
+
->(__buffer__, *pos2, **kw2, &block2) {
|
|
250
|
+
if block2
|
|
251
|
+
block2_compiled = block1_compiled ?
|
|
252
|
+
->(__buffer__, *pos3, **kw3) {
|
|
253
|
+
block1_compiled.(__buffer__, *pos3, **kw3, &block2)
|
|
254
|
+
}.__papercraft_compiled! :
|
|
255
|
+
block2.__papercraft_compiled_proc
|
|
256
|
+
compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &block2_compiled)
|
|
257
|
+
else
|
|
258
|
+
compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &block1_compiled)
|
|
259
|
+
end
|
|
260
|
+
}.__papercraft_compiled!
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Caches and returns the rendered HTML for the template with the given
|
|
264
|
+
# arguments.
|
|
265
|
+
#
|
|
266
|
+
# @param template [Proc] template proc
|
|
267
|
+
# @param key [any] Cache key
|
|
268
|
+
# @return [String] HTML string
|
|
269
|
+
def cache_html(template, key, *, **, &)
|
|
270
|
+
template.__papercraft_render_cache[key] ||= html(template, *, **, &)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Caches and returns the rendered XML for the template with the given
|
|
274
|
+
# arguments.
|
|
275
|
+
#
|
|
276
|
+
# @param template [Proc] template proc
|
|
277
|
+
# @param key [any] Cache key
|
|
278
|
+
# @return [String] XML string
|
|
279
|
+
def cache_xml(template, key, *, **, &)
|
|
280
|
+
template.__papercraft_render_cache[key] ||= xml(template, *, **, &)
|
|
281
|
+
end
|
|
193
282
|
end
|