papercraft 2.23 → 2.24

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: 2f77a2ff47a98a713d1449c0ae27d4a250e478e5455257955fc5e7af34a8331b
4
- data.tar.gz: 6893f861f98361f48fdd0b0c2648c687d24149df217e0125da0ccc4795e40c69
3
+ metadata.gz: ea6b8d53d5110e44678302644fef523e5e9735b7f862f67b20a9f1bb4883c8b8
4
+ data.tar.gz: 3aacb8f0fb578c691230aefc5a1c0c16b1ddae30666004e977094f1f4a7793da
5
5
  SHA512:
6
- metadata.gz: b76597b671e62bdb67483c0cd30e4c716084655d0a80cad41c4a77405c97e8300712201f3cf2215584f979b7ae2d3c7e29770868bbda753b9faca88f633101a7
7
- data.tar.gz: 5bbb94d2ec5c13ab5da17c4474d0338d195abd5149dbddb75d1e0fbcb371c1e620c9666169233dad04cc4b8b6265bfc13d9d41c1115fe37a61e30549e0f06ea2
6
+ metadata.gz: 3014b7b8a22b5128f5aecb7fd7e7c181d6af5fd1f0727f71dafaa1994f16fda643c6f80aad04fcffc332042e392c11803d9f75d4b18ec8704edc94f374a927ab
7
+ data.tar.gz: e8d2017554d39c57d18ccd4ddf3a39f5fc4592d1c16584c47f5c31028a131c28379b43adad55557b70191df34d32f28d4e78989ed8d59700fc0f78bfd69dfcde
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 2.24 2025-10-14
2
+
3
+ - Update gem links
4
+ - Simplify `render_cache`, caller must provide cache key
5
+ - Reduce surface area of Proc extensions
6
+
1
7
  # 2.23 2025-10-12
2
8
 
3
9
  - Update ERB to version 5.1.1
data/README.md CHANGED
@@ -18,603 +18,29 @@
18
18
  </a>
19
19
  </p>
20
20
 
21
- <p align="center">
22
- <a href="https://www.rubydoc.info/gems/papercraft">API reference</a>
23
- </p>
24
-
25
- ## What is Papercraft?
26
-
27
21
  ```ruby
28
22
  require 'papercraft'
29
23
 
30
- page = ->(**props) {
31
- html {
32
- head { title 'My Title' }
33
- body { render_children **props }
34
- }
35
- }
36
- page.render {
37
- p 'foo'
38
- }
39
- #=> "<html><head><title>Title</title></head><body><p>foo</p></body></html>"
24
+ -> {
25
+ h1 "Hello from Papercraft!"
26
+ }.render
27
+ #=> "<h1>Hello from Papercraft</h1>"
40
28
  ```
41
29
 
42
- Papercraft is a templating engine for dynamically producing HTML in Ruby apps. Papercraft
43
- templates are expressed as Ruby procs, leading to easier debugging, better
44
- protection against HTML injection attacks, and better code reuse.
30
+ Papercraft is a templating engine for dynamically producing HTML in Ruby apps.
31
+ Papercraft templates are expressed as Ruby procs, leading to easier debugging,
32
+ better protection against HTML injection attacks, and better code reuse.
45
33
 
46
- Papercraft templates can be composed in a variety of ways, facilitating the usage of
47
- layout templates, and enabling a component-oriented approach to building web
48
- interfaces of arbitrary complexity.
34
+ Papercraft templates can be composed in a variety of ways, facilitating the
35
+ usage of layout templates, and enabling a component-oriented approach to
36
+ building web interfaces of arbitrary complexity.
49
37
 
50
38
  In Papercraft, dynamic data is passed explicitly to the template as block/lambda
51
- arguments, making the data flow easy to follow and understand. Papercraft also lets
52
- developers create derivative templates using full or partial parameter
39
+ arguments, making the data flow easy to follow and understand. Papercraft also
40
+ lets developers create derivative templates using full or partial parameter
53
41
  application.
54
42
 
55
- ```ruby
56
- require 'papercraft'
57
-
58
- page = ->(**props) {
59
- html {
60
- head { title 'My Title' }
61
- body { render_children **props }
62
- }
63
- }
64
- page.render {
65
- p(class: 'big') 'foo'
66
- }
67
- #=> "<html><head><title>Title</title></head><body><p class="big">foo</p></body></html>"
68
-
69
- hello_page = page.apply ->(name:, **) {
70
- h1 "Hello, #{name}!"
71
- }
72
- hello.render(name: 'world')
73
- #=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
74
- ```
75
-
76
- Papercraft features:
77
-
78
- - Express HTML using plain Ruby procs.
79
- - Automatic compilation for super-fast execution (about as
80
- [fast](https://github.com/digital-fabric/papercraft/blob/master/examples/perf.rb) as
81
- compiled ERB/ERubi).
82
- - Deferred rendering using `defer`.
83
- - Simple and easy template composition (for uses such as layouts, or modular
84
- templates).
85
- - Markdown rendering using [Kramdown](https://github.com/gettalong/kramdown/).
86
- - Rudimentary support for generating XML.
87
- - Support for extensions.
88
- - Simple caching API for caching the rendering result.
89
-
90
- ## Table of Content
91
-
92
- - [Getting Started](#getting-started)
93
- - [Basic Markup](#basic-markup)
94
- - [Builtin Methods](#builtin-methods)
95
- - [Template Parameters](#template-parameters)
96
- - [Template Logic](#template-logic)
97
- - [Template Blocks](#template-blocks)
98
- - [Template Composition](#template-composition)
99
- - [Parameter and Block Application](#parameter-and-block-application)
100
- - [Higher-Order Templates](#higher-order-templates)
101
- - [Layout Template Composition](#layout-template-composition)
102
- - [Rendering Markdown](#rendering-markdown)
103
- - [Deferred Evaluation](#deferred-evaluation)
104
- - [Cached Rendering](#cached-rendering)
105
-
106
- A typical example for a dashboard-type app markup can be found here:
107
- https://github.com/digital-fabric/papercraft/blob/master/examples/dashboard.rb
108
-
109
- ## Getting Started
110
-
111
- In Papercraft, an HTML template is expressed as a proc:
112
-
113
- ```ruby
114
- html = -> {
115
- div(id: 'greeter') { p 'Hello!' }
116
- }
117
- ```
118
-
119
- Rendering a template is done using `Proc#render`:
120
-
121
- ```ruby
122
- require 'papercraft'
123
-
124
- html.render #=> "<div id="greeter"><p>Hello!</p></div>"
125
- ```
126
-
127
- ## Basic Markup
128
-
129
- Tags are added using unqualified method calls, and can be nested using blocks:
130
-
131
- ```ruby
132
- -> {
133
- html {
134
- head {
135
- title 'page title'
136
- }
137
- body {
138
- article {
139
- h1 'article title'
140
- }
141
- }
142
- }
143
- }
144
- ```
145
-
146
- Tag methods accept a string argument, a block, or no argument at all:
147
-
148
- ```ruby
149
- -> { p 'hello' }.render #=> "<p>hello</p>"
150
-
151
- -> { p { span '1'; span '2' } }.render #=> "<p><span>1</span><span>2</span></p>"
152
-
153
- -> { hr() }.render #=> "<hr/>"
154
- ```
155
-
156
- Tag methods also accept tag attributes, given as a hash:
157
-
158
- ```ruby
159
- -> { img src: '/my.gif' }.render #=> "<img src=\"/my.gif\"/>"
160
-
161
- -> { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</p>"
162
- ```
163
-
164
- A `true` attribute value will emit a valueless attribute. A `nil` or `false`
165
- attribute value will emit nothing:
166
-
167
- ```ruby
168
- -> { button disabled: nil }.render #=> "<button></button>"
169
- -> { button disabled: true }.render #=> "<button disabled></button>"
170
- ```
171
-
172
- An attribute value given as an array will be joined by space characters:
173
-
174
- ```ruby
175
- -> { div class: [:foo, :bar] }.render #=> "<div class=\"foo bar\"></div>"
176
- ```
177
-
178
- ### Tag and Attribute Formatting
179
-
180
- Papercraft does not make any assumption about what tags and attributes you can use. You
181
- can mix upper and lower case letters, and you can include arbitrary characters
182
- in tag and attribute names. However, in order to best adhere to the HTML specs
183
- and common practices, tag names and attributes will be formatted according to
184
- the following rules, depending on the template type:
185
-
186
- - HTML: underscores are converted to dashes:
187
-
188
- ```ruby
189
- -> {
190
- foo_bar { p 'Hello', data_name: 'world' }
191
- }.render #=> '<foo-bar><p data-name="world">Hello</p></foo-bar>'
192
- ```
193
-
194
- If you need more precise control over tag names, you can use the `#tag` method,
195
- which takes the tag name as its first parameter, then the rest of the parameters
196
- normally used for tags:
197
-
198
- ```ruby
199
- -> {
200
- tag 'cra_zy__:!tag', 'foo'
201
- }.render #=> '<cra_zy__:!tag>foo</cra_zy__:!tag>'
202
- ```
203
-
204
- ### Escaping Content
205
-
206
- Papercraft automatically escapes all text content emitted in a template. The specific
207
- escaping algorithm depends on the template type. To emit raw HTML, use the
208
- `#raw` method as [described below](#builtin-methods).
209
-
210
- ## Builtin Methods
211
-
212
- In addition to normal tags, Papercraft provides the following method calls for templates:
213
-
214
- ### `#text` - emit escaped text
215
-
216
- `#text` is used for emitting text that will be escaped. This method can be used
217
- to emit text not directly inside an enclosing tag:
218
-
219
- ```ruby
220
- -> {
221
- p {
222
- text 'The time is: '
223
- span(Time.now, id: 'clock')
224
- }
225
- }.render #=> <p>The time is: <span id="clock">XX:XX:XX</span></p>
226
- ```
227
-
228
- ### `#raw` - emit raw HTML
229
-
230
- `#raw` is used for emitting raw HTML, i.e. without escaping. You can use this to
231
- emit an HTML snippet:
232
-
233
- ```ruby
234
- TITLE_HTML = '<h1>hi</h1>'
235
- -> {
236
- div {
237
- raw TITLE_HTML
238
- }
239
- }.render #=> <div><h1>hi</h1></div>
240
- ```
241
-
242
- ### `#render_children` - render the given block
243
-
244
- `#render_children` is used to emit a given block. If no block is given, a
245
- `LocalJumpError` exception is raised:
246
-
247
- ```ruby
248
- Card = ->(**props) {
249
- card { render_children(**props) }
250
- }
251
-
252
- Card.render(foo: 'bar') { |foo|
253
- h1 foo
254
- } #=> <card><h1>bar</h1></card>
255
- ```
256
-
257
- `render_children` can be called with or without arguments, which are passed to the
258
- given block.
259
-
260
- ### `#defer` - emit deferred HTML
261
-
262
- `#defer` is used to emit HTML in a deferred fashion - the deferred part will be
263
- evaluated only after processing the entire template:
264
-
265
- ```ruby
266
- Layout = -> {
267
- head {
268
- defer {
269
- title @title
270
- }
271
- }
272
- body {
273
- render_children
274
- }
275
- }
276
-
277
- Layout.render {
278
- @title = 'Foobar'
279
- h1 'hi'
280
- } #=> <head><title>Foobar</title></head><body><h1>hi</h1></body>
281
- ```
282
-
283
- ### `#render` - render the given template inline
284
-
285
- `#render` is used to emit the given template. This can be used to compose
286
- templates:
287
-
288
- ```ruby
289
- partial = -> { p 'foo' }
290
- -> {
291
- div {
292
- render partial
293
- }
294
- }.render #=> <div><p>foo</p></div>
295
- ```
296
-
297
- Any argument following the given template is passed to the template for
298
- rendering:
299
-
300
- ```ruby
301
- large_button = ->(title) { button(title, class: 'large') }
302
-
303
- -> {
304
- render large_button, 'foo'
305
- }.render #=> <button class="large">foo</button>
306
- ```
307
-
308
- ### `#html`/`#html5` - emit an HTML5 document type declaration and html tag
309
-
310
- ```ruby
311
- -> {
312
- html5 {
313
- p 'hi'
314
- }
315
- } #=> <!DOCTYPE html><html><p>hi</p></html>
316
- ```
317
-
318
- ### `#markdown` emit markdown content
319
-
320
- `#markdown` is used for rendering markdown content. The call converts the given
321
- markdown to HTML and emits it into the rendered HTML:
322
-
323
- ```ruby
324
- -> {
325
- div {
326
- markdown 'This is *markdown*'
327
- }
328
- }.render #=> <p>This is <em>markdown</em></p>
329
- ```
330
-
331
- ## Template Parameters
332
-
333
- In Papercraft, parameters are always passed explicitly. This means that template
334
- parameters are specified as block parameters, and are passed to the template on
335
- rendering:
336
-
337
- ```ruby
338
- greeting = ->(name) { h1 "Hello, #{name}!" }
339
- greeting.render('world') #=> "<h1>Hello, world!</h1>"
340
- ```
341
-
342
- Templates can also accept named parameters:
343
-
344
- ```ruby
345
- greeting = ->(name:) { h1 "Hello, #{name}!" }
346
- greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
347
- ```
348
-
349
- ## Template Logic
350
-
351
- Since Papercraft templates are just a bunch of Ruby, you can easily embed your view
352
- logic right in the template:
353
-
354
- ```ruby
355
- ->(user = nil) {
356
- if user
357
- span "Hello, #{user.name}!"
358
- else
359
- span "Hello, guest!"
360
- end
361
- }
362
- ```
363
-
364
- ## Template Blocks
365
-
366
- Templates can also accept and render blocks by using `render_children`:
367
-
368
- ```ruby
369
- page = -> {
370
- html {
371
- body { render_children }
372
- }
373
- }
374
-
375
- # we pass the inner HTML
376
- page.render { h1 'hi' }
377
- ```
378
-
379
- ## Template Composition
380
-
381
- Papercraft makes it easy to compose multiple templates into a whole HTML document. A Papercraft
382
- template can contain other templates, as the following example shows.
383
-
384
- ```ruby
385
- Title = ->(title) { h1 title }
386
-
387
- Item = ->(id:, text:, checked:) {
388
- li {
389
- input name: id, type: 'checkbox', checked: checked
390
- label text, for: id
391
- }
392
- }
393
-
394
- ItemList = ->(items) {
395
- ul {
396
- items.each { |i|
397
- Item(**i)
398
- }
399
- }
400
- }
401
-
402
- page = ->(title, items) {
403
- html5 {
404
- head { Title(title) }
405
- body { ItemList(items) }
406
- }
407
- }
408
-
409
- page.render('Hello from composed templates', [
410
- { id: 1, text: 'foo', checked: false },
411
- { id: 2, text: 'bar', checked: true }
412
- ])
413
- ```
414
-
415
- In addition to using templates defined as constants, you can also use
416
- non-constant templates by invoking the `#render` method:
417
-
418
- ```ruby
419
- greeting = -> { span "Hello, world" }
420
-
421
- -> {
422
- div {
423
- render greeting
424
- }
425
- }
426
- ```
427
-
428
- ## Parameter and Block Application
429
-
430
- Parameters and blocks can be applied to a template without it being rendered, by
431
- using `#apply`. This mechanism is what allows template composition and the
432
- creation of higher-order templates.
433
-
434
- The `#apply` method returns a new template which applies the given parameters
435
- and or block to the original template:
436
-
437
- ```ruby
438
- # parameter application
439
- hello = -> { |name| h1 "Hello, #{name}!" }
440
- hello_world = hello.apply('world')
441
- hello_world.render #=> "<h1>Hello, world!</h1>"
442
-
443
- # block application
444
- div_wrap = -> { div { render_children } }
445
- wrapped_h1 = div_wrap.apply { h1 'hi' }
446
- wrapped_h1.render #=> "<div><h1>hi</h1></div>"
447
-
448
- # wrap a template
449
- wrapped_hello_world = div_wrap.apply(&hello_world)
450
- wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
451
- ```
452
-
453
- ## Higher-Order Templates
454
-
455
- Papercraft also lets you create higher-order templates, that is, templates that take
456
- other templates as parameters, or as blocks. Higher-order templates are handy
457
- for creating layouts, wrapping templates in arbitrary markup, enhancing
458
- templates or injecting template parameters.
459
-
460
- Here is a higher-order template that takes a template as parameter:
461
-
462
- ```ruby
463
- div_wrap = -> { |inner| div { render inner } }
464
- greeter = -> { h1 'hi' }
465
- wrapped_greeter = div_wrap.apply(greeter)
466
- wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
467
- ```
468
-
469
- The inner template can also be passed as a block, as shown above:
470
-
471
- ```ruby
472
- div_wrap = -> { div { render_children } }
473
- wrapped_greeter = div_wrap.apply { h1 'hi' }
474
- wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
475
- ```
476
-
477
- ## Layout Template Composition
478
-
479
- One of the principal uses of higher-order templates is the creation of nested
480
- layouts. Suppose we have a website with a number of different layouts, and we'd
481
- like to avoid having to repeat the same code in the different layouts. We can do
482
- this by creating a `default` page template that takes a block, then use `#apply`
483
- to create the other templates:
484
-
485
- ```ruby
486
- default_layout = -> { |**params|
487
- html5 {
488
- head {
489
- title: params[:title]
490
- }
491
- body {
492
- render_children(**params)
493
- }
494
- }
495
- }
496
-
497
- article_layout = default_layout.apply { |title:, body:|
498
- article {
499
- h1 title
500
- markdown body
501
- }
502
- }
503
-
504
- article_layout.render(
505
- title: 'This is a title',
506
- body: 'Hello from *markdown body*'
507
- )
508
- ```
509
-
510
- ## Rendering Markdown
511
-
512
- Markdown is rendered using the
513
- [Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
514
- `#markdown`:
515
-
516
- ```ruby
517
- template = -> { |md| div { markdown md } }
518
- template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
519
- ```
520
-
521
- [Kramdown
522
- options](https://kramdown.gettalong.org/options.html#available-options) can be
523
- specified by adding them to the `#markdown` call:
524
-
525
- ```ruby
526
- template = -> { |md| div { markdown md, auto_ids: false } }
527
- template.render("# title") #=> "<div><h1>title</h1></div>"
528
- ```
529
-
530
- You can also use `Papercraft.markdown` directly:
531
-
532
- ```ruby
533
- Papercraft.markdown('# title') #=> "<h1>title</h1>"
534
- ```
535
-
536
- The default Kramdown options are:
43
+ ## Documentation
537
44
 
538
- ```ruby
539
- {
540
- entity_output: :numeric,
541
- syntax_highlighter: :rouge,
542
- input: 'GFM',
543
- hard_wrap: false
544
- }
545
- ```
546
-
547
- The deafult options can be configured by accessing
548
- `Papercraft.default_kramdown_options`, e.g.:
549
-
550
- ```ruby
551
- Papercraft.default_kramdown_options[:auto_ids] = false
552
- ```
553
-
554
- ## Deferred Evaluation
555
-
556
- Deferred evaluation allows deferring the rendering of parts of a template until
557
- the last moment, thus allowing an inner template to manipulate the state of the
558
- outer template. To in order to defer a part of a template, use `#defer`, and
559
- include any markup in the provided block. This technique, in in conjunction with
560
- holding state in instance variables, is an alternative to passing parameters,
561
- which can be limiting in some situations.
562
-
563
- A few use cases for deferred evaulation come to mind:
564
-
565
- - Setting the page title.
566
- - Adding a flash message to a page.
567
- - Using templates that dynamically add static dependencies (JS and CSS) to the
568
- page.
569
-
570
- The last use case is particularly interesting. Imagine a `DependencyMananger`
571
- class that can collect JS and CSS dependencies from the different templates
572
- integrated into the page, and adds them to the page's `<head>` element:
573
-
574
- ```ruby
575
- deps = DependencyMananger.new
576
-
577
- default_layout = -> { |**args|
578
- head {
579
- defer { render deps.head_markup }
580
- }
581
- body { render_children **args }
582
- }
583
-
584
- button = proc { |text, onclick|
585
- deps.js '/static/js/button.js'
586
- deps.css '/static/css/button.css'
587
-
588
- button text, onclick: onclick
589
- }
590
-
591
- heading = proc { |text|
592
- deps.js '/static/js/heading.js'
593
- deps.css '/static/css/heading.css'
594
-
595
- h1 text
596
- }
597
-
598
- page = default_layout.apply {
599
- render heading, "What's your favorite cheese?"
600
-
601
- render button, 'Beaufort', 'eat_beaufort()'
602
- render button, 'Mont d''or', 'eat_montdor()'
603
- render button, 'Époisses', 'eat_epoisses()'
604
- }
605
- ```
606
-
607
- ## Cached Rendering
608
-
609
- Papercraft provides a simple API for caching the result of a rendering. The cache stores
610
- renderings of a template respective to the given arguments. To automatically
611
- retrieve the cached rendered HTML, or generate it for the first time, use
612
- `Proc#render_cached`:
613
-
614
- ```ruby
615
- template = ->(title) { div { h1 title } }
616
- template.render_cached('foo') #=> <div><h1>foo</h1></div>
617
- template.render_cached('foo') #=> <div><h1>foo</h1></div> (from cache)
618
- template.render_cached('bar') #=> <div><h1>bar</h1></div>
619
- template.render_cached('bar') #=> <div><h1>bar</h1></div> (from cache)
620
- ```
45
+ For more information, please consult the [Papercraft
46
+ website](https://papercraft.noteflakes.com/).
@@ -178,7 +178,7 @@ module Papercraft
178
178
  when Prism::BlockArgumentNode
179
179
  flush_html_parts!
180
180
  adjust_whitespace(node.block)
181
- emit("; #{format_code(node.block.expression)}.compiled_proc.(__buffer__)")
181
+ emit("; #{format_code(node.block.expression)}.__compiled_proc__.(__buffer__)")
182
182
  end
183
183
 
184
184
  if node.inner_text
@@ -213,7 +213,7 @@ module Papercraft
213
213
  emit(format_code(node.call_node.receiver))
214
214
  emit('::')
215
215
  end
216
- emit("#{node.call_node.name}.compiled_proc.(__buffer__")
216
+ emit("#{node.call_node.name}.__compiled_proc__.(__buffer__")
217
217
  if node.call_node.arguments
218
218
  emit(', ')
219
219
  visit(node.call_node.arguments)
@@ -229,17 +229,17 @@ module Papercraft
229
229
  args = node.call_node.arguments.arguments
230
230
  first_arg = args.first
231
231
 
232
- block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.compiled!)"
232
+ block_embed = node.block && "&(->(__buffer__) #{format_code(node.block)}.__compiled__!)"
233
233
  block_embed = ", #{block_embed}" if block_embed && node.call_node.arguments
234
234
 
235
235
  flush_html_parts!
236
236
  adjust_whitespace(node.location)
237
237
 
238
238
  if args.length == 1
239
- emit("; #{format_code(first_arg)}.compiled_proc.(__buffer__#{block_embed})")
239
+ emit("; #{format_code(first_arg)}.__compiled_proc__.(__buffer__#{block_embed})")
240
240
  else
241
241
  args_code = format_code_comma_separated_nodes(args[1..])
242
- emit("; #{format_code(first_arg)}.compiled_proc.(__buffer__, #{args_code}#{block_embed})")
242
+ emit("; #{format_code(first_arg)}.__compiled_proc__.(__buffer__, #{args_code}#{block_embed})")
243
243
  end
244
244
  end
245
245
 
@@ -336,7 +336,7 @@ module Papercraft
336
336
  def visit_extension_tag_node(node)
337
337
  flush_html_parts!
338
338
  adjust_whitespace(node.location)
339
- emit("; Papercraft::Extensions[#{node.tag.inspect}].compiled_proc.(__buffer__")
339
+ emit("; Papercraft::Extensions[#{node.tag.inspect}].__compiled_proc__.(__buffer__")
340
340
  if node.call_node.arguments
341
341
  emit(', ')
342
342
  visit(node.call_node.arguments)
@@ -367,7 +367,7 @@ module Papercraft
367
367
  end
368
368
  block_params = block_params.empty? ? '' : ", #{block_params.join(', ')}"
369
369
 
370
- emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).compiled!")
370
+ emit(", &(proc { |__buffer__#{block_params}| #{block_body} }).__compiled__!")
371
371
  end
372
372
  emit(")")
373
373
  end
@@ -382,7 +382,7 @@ module Papercraft
382
382
  guard = @render_yield_used ?
383
383
  '' : "; raise(LocalJumpError, 'no block given (render_yield)') if !__block__"
384
384
  @render_yield_used = true
385
- emit("#{guard}; __block__.compiled_proc.(__buffer__")
385
+ emit("#{guard}; __block__.__compiled_proc__.(__buffer__")
386
386
  if node.call_node.arguments
387
387
  emit(', ')
388
388
  visit(node.call_node.arguments)
@@ -398,7 +398,7 @@ module Papercraft
398
398
  flush_html_parts!
399
399
  adjust_whitespace(node.location)
400
400
  @render_children_used = true
401
- emit("; __block__&.compiled_proc&.(__buffer__")
401
+ emit("; __block__&.__compiled_proc__&.(__buffer__")
402
402
  if node.call_node.arguments
403
403
  emit(', ')
404
404
  visit(node.call_node.arguments)
@@ -410,7 +410,7 @@ module Papercraft
410
410
  flush_html_parts!
411
411
  adjust_whitespace(node.location)
412
412
 
413
- emit("; #{node.call_node.receiver.name}.compiled_proc.(__buffer__")
413
+ emit("; #{node.call_node.receiver.name}.__compiled_proc__.(__buffer__")
414
414
  if node.call_node.arguments
415
415
  emit(', ')
416
416
  visit(node.call_node.arguments)
@@ -418,7 +418,7 @@ module Papercraft
418
418
  if node.call_node.block
419
419
  emit(", &(->")
420
420
  visit(node.call_node.block)
421
- emit(").compiled_proc")
421
+ emit(").__compiled_proc__")
422
422
  end
423
423
  emit(")")
424
424
  end
@@ -5,42 +5,19 @@ require_relative './compiler'
5
5
  module Papercraft
6
6
  # Extensions to the Proc class.
7
7
  module ProcExtensions
8
- # Returns the compiled form code for the proc.
9
- #
10
- # @return [String] compiled proc code
11
- def compiled_code
12
- Papercraft::Compiler.compile_to_code(self).last
13
- end
14
-
15
- # Returns the source map for the compiled proc.
16
- #
17
- # @return [Array<String>] source map
18
- def source_map
19
- loc = source_location
20
- fn = compiled? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
21
- Papercraft::Compiler.source_map_store[fn]
22
- end
23
-
24
- # Returns the AST for the proc.
25
- #
26
- # @return [Prism::Node] AST root
27
- def ast
28
- Sirop.to_ast(self)
29
- end
30
-
31
8
  # Returns true if proc is marked as compiled.
32
9
  #
33
10
  # @return [bool] is the proc marked as compiled
34
- def compiled?
35
- @is_compiled
11
+ def __compiled__?
12
+ @__compiled__
36
13
  end
37
14
 
38
15
  # Marks the proc as compiled, i.e. can render directly and takes a string
39
16
  # buffer as first argument.
40
17
  #
41
18
  # @return [self]
42
- def compiled!
43
- @is_compiled = true
19
+ def __compiled__!
20
+ @__compiled__ = true
44
21
  self
45
22
  end
46
23
 
@@ -49,25 +26,15 @@ module Papercraft
49
26
  #
50
27
  # @param mode [Symbol] compilation mode (:html, :xml)
51
28
  # @return [Proc] compiled proc or self
52
- def compiled_proc(mode: :html)
53
- @compiled_proc ||= @is_compiled ? self : compile(mode:)
54
- end
55
-
56
- # Compiles the proc into the compiled form.
57
- #
58
- # @param mode [Symbol] compilation mode (:html, :xml)
59
- # @return [Proc] compiled proc
60
- def compile(mode: :html)
61
- Papercraft::Compiler.compile(self, mode:).compiled!
62
- rescue Sirop::Error
63
- raise Papercraft::Error, "Dynamically defined procs cannot be compiled"
29
+ def __compiled_proc__(mode: :html)
30
+ @__compiled_proc__ ||= @__compiled__ ? self : Papercraft.compile(self, mode:)
64
31
  end
65
32
 
66
33
  # Renders the proc to HTML with the given arguments.
67
34
  #
68
35
  # @return [String] HTML string
69
36
  def render(*a, **b, &c)
70
- compiled_proc.(+'', *a, **b, &c)
37
+ __compiled_proc__.(+'', *a, **b, &c)
71
38
  rescue Exception => e
72
39
  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
73
40
  end
@@ -76,21 +43,11 @@ module Papercraft
76
43
  #
77
44
  # @return [String] XML string
78
45
  def render_xml(*a, **b, &c)
79
- compiled_proc(mode: :xml).(+'', *a, **b, &c)
46
+ __compiled_proc__(mode: :xml).(+'', *a, **b, &c)
80
47
  rescue Exception => e
81
48
  e.is_a?(Papercraft::Error) ? raise : raise(Papercraft.translate_backtrace(e))
82
49
  end
83
50
 
84
- # Renders the proc to HTML with the given arguments into the given buffer.
85
- #
86
- # @param buf [String] buffer
87
- # @return [String] HTML string
88
- def render_to_buffer(buf, *a, **b, &c)
89
- compiled_proc.(buf, *a, **b, &c)
90
- rescue Exception => e
91
- raise Papercraft.translate_backtrace(e)
92
- end
93
-
94
51
  # Returns a proc that applies the given arguments to the original proc. The
95
52
  # returned proc calls the *compiled* form of the proc, merging the
96
53
  # positional and keywords parameters passed to `#apply` with parameters
@@ -101,25 +58,25 @@ module Papercraft
101
58
  # @param **kw1 [Hash<any, any] applied keyword parameters
102
59
  # @return [Proc] applied proc
103
60
  def apply(*pos1, **kw1, &block)
104
- compiled = compiled_proc
105
- c_compiled = block&.compiled_proc
61
+ compiled = __compiled_proc__
62
+ c_compiled = block&.__compiled_proc__
106
63
 
107
64
  ->(__buffer__, *pos2, **kw2, &block2) {
108
65
  c_proc = c_compiled && ->(__buffer__, *pos3, **kw3) {
109
66
  c_compiled.(__buffer__, *pos3, **kw3, &block2)
110
- }.compiled!
67
+ }.__compiled__!
111
68
 
112
69
  compiled.(__buffer__, *pos1, *pos2, **kw1, **kw2, &c_proc)
113
- }.compiled!
70
+ }.__compiled__!
114
71
  end
115
72
 
116
73
  # Caches and returns the rendered HTML for the template with the given
117
74
  # arguments.
118
75
  #
76
+ # @param key [any] Cache key
119
77
  # @return [String] HTML string
120
- def render_cached(*args, **kargs, &block)
78
+ def render_cache(key, *args, **kargs, &block)
121
79
  @render_cache ||= {}
122
- key = args.empty? && kargs.empty? && !block ? nil : [args, kargs, block&.source_location]
123
80
  @render_cache[key] ||= render(*args, **kargs, &block)
124
81
  end
125
82
  end
@@ -21,8 +21,8 @@ module Papercraft
21
21
  Template.new(@proc.apply(*, **, &), mode: @mode)
22
22
  end
23
23
 
24
- def compiled_proc
25
- @proc.compiled_proc(mode: @mode)
24
+ def __compiled_proc__
25
+ @proc.__compiled_proc__(mode: @mode)
26
26
  end
27
27
  end
28
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Papercraft
4
- VERSION = '2.23'
4
+ VERSION = '2.24'
5
5
  end
data/lib/papercraft.rb CHANGED
@@ -153,4 +153,41 @@ module Papercraft
153
153
  def default_kramdown_options=(opts)
154
154
  @default_kramdown_options = opts
155
155
  end
156
+
157
+ # Returns the compiled form code for the given proc.
158
+ #
159
+ # @param proc [Proc] template proc
160
+ # @return [String] compiled proc code
161
+ def compiled_code(proc)
162
+ Papercraft::Compiler.compile_to_code(proc).last
163
+ end
164
+
165
+ # Returns the source map for the given proc.
166
+ #
167
+ # @param proc [Proc] template proc
168
+ # @return [Array<String>] source map
169
+ def source_map(proc)
170
+ loc = proc.source_location
171
+ fn = proc.__compiled__? ? loc.first : Papercraft::Compiler.source_location_to_fn(loc)
172
+ Papercraft::Compiler.source_map_store[fn]
173
+ end
174
+
175
+ # Returns the AST for the given proc.
176
+ #
177
+ # @param proc [Proc] template proc
178
+ # @return [Prism::Node] AST root
179
+ def ast(proc)
180
+ Sirop.to_ast(proc)
181
+ end
182
+
183
+ # Compiles the given template.
184
+ #
185
+ # @param proc [Proc] template proc
186
+ # @param mode [Symbol] compilation mode (:html, :xml)
187
+ # @return [Proc] compiled proc
188
+ def compile(proc, mode: :html)
189
+ Papercraft::Compiler.compile(proc, mode:).__compiled__!
190
+ rescue Sirop::Error
191
+ raise Papercraft::Error, "Can't compile eval'd template"
192
+ end
156
193
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: papercraft
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.23'
4
+ version: '2.24'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
@@ -142,8 +142,8 @@ homepage: http://github.com/digital-fabric/papercraft
142
142
  licenses:
143
143
  - MIT
144
144
  metadata:
145
- homepage_uri: https://github.com/digital-fabric/papercraft
146
- documentation_uri: https://www.rubydoc.info/gems/papercraft
145
+ homepage_uri: https://papercraft.noteflakes.com/
146
+ source_code_uri: https://github.com/digital-fabric/papercraft
147
147
  changelog_uri: https://github.com/digital-fabric/papercraft/blob/master/CHANGELOG.md
148
148
  rdoc_options:
149
149
  - "--title"