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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 729c7b1cf7482cb761b005fba8da8ae4c1325559daee15fedd2d695de21ee08d
|
4
|
+
data.tar.gz: 0546c527a6a9ac0570c9bd6057101b31591eba1f1ee549c272d9bda13a9f7a04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fdaa8d2f254744694ea3624f39871f4b411294a990f6e1bb4c57f56bf36049031b9403820cbadaa57ef6ac3e9021b6afa53da22588462450a6ca0863b6b88184
|
7
|
+
data.tar.gz: 4ea21235970af0cdef9ce09cd790742a94632e4f1f02d4a3d90a3e857741ec41952a53fe59b24016c00894dde7131dbbbc79d7616564f159f5646acf24f469f9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
## 0.22 2022-02-14
|
2
|
+
|
3
|
+
- Fix behaviour of call to `#p` in an extension (#10)
|
4
|
+
|
5
|
+
## 0.21 2022-02-13
|
6
|
+
|
7
|
+
- Refactor and improve documentation
|
8
|
+
|
9
|
+
## 0.20 2022-02-13
|
10
|
+
|
11
|
+
- Add support for XML namespaced tags and attributes (#9)
|
12
|
+
- Move and refactor HTML/XML common code to Tags module
|
13
|
+
|
14
|
+
## 0.19 2022-02-05
|
15
|
+
|
16
|
+
- Rename `Papercraft::Component` to `Papercraft::Template`
|
17
|
+
|
1
18
|
## 0.18 2022-02-04
|
2
19
|
|
3
20
|
- Cleanup and update examples
|
data/README.md
CHANGED
@@ -64,13 +64,14 @@ hello.render('world')
|
|
64
64
|
- [Installing papercraft](#installing-papercraft)
|
65
65
|
- [Basic usage](#basic-usage)
|
66
66
|
- [Adding tags](#adding-tags)
|
67
|
+
- [Tag and attribute formatting](#tag-and-attribute-formatting)
|
67
68
|
- [Template parameters](#template-parameters)
|
68
69
|
- [Template logic](#template-logic)
|
69
70
|
- [Template blocks](#template-blocks)
|
70
71
|
- [Plain procs as templates](#plain-procs-as-templates)
|
71
72
|
- [Template composition](#template-composition)
|
72
73
|
- [Parameter and block application](#parameter-and-block-application)
|
73
|
-
- [Higher-order
|
74
|
+
- [Higher-order templates](#higher-order-templates)
|
74
75
|
- [Layout template composition](#layout-template-composition)
|
75
76
|
- [Emitting raw HTML](#emitting-raw-html)
|
76
77
|
- [Emitting a string with HTML Encoding](#emitting-a-string-with-html-encoding)
|
@@ -154,6 +155,43 @@ Papercraft.html { img src: '/my.gif' }.render #=> "<img src="/my.gif"/>
|
|
154
155
|
Papercraft.html { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</p>"
|
155
156
|
```
|
156
157
|
|
158
|
+
## Tag and attribute formatting
|
159
|
+
|
160
|
+
Papercraft does not make any presumption about what tags and attributes you can
|
161
|
+
use. You can mix upper and lower case letters, and you can include arbitrary
|
162
|
+
characters in tag and attribute names. However, in order to best adhere to the
|
163
|
+
HTML and XML specs and common practices, tag names and attributes will be
|
164
|
+
formatted according to the following rules, depending on the template type:
|
165
|
+
|
166
|
+
- HTML: underscores are converted to dashes:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
Papercraft.html {
|
170
|
+
foo_bar { p 'Hello', data_name: 'world' }
|
171
|
+
}.render #=> '<foo-bar><p data-name="world">Hello</p></foo-bar>'
|
172
|
+
```
|
173
|
+
|
174
|
+
- XML: underscores are converted to dashes, double underscores are converted to
|
175
|
+
colons:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
Papercraft.xml {
|
179
|
+
soap__Envelope(
|
180
|
+
xmlns__soap: 'http://schemas.xmlsoap.org/soap/envelope/',
|
181
|
+
) { }
|
182
|
+
}.render #=> '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Envelope>'
|
183
|
+
```
|
184
|
+
|
185
|
+
If you need more precise control over tag names, you can use the `#tag` method,
|
186
|
+
which takes the tag name as its first parameter, then the rest of the parameters
|
187
|
+
normally used for tags:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
Papercraft.html {
|
191
|
+
tag 'cra_zy__:!tag', 'foo'
|
192
|
+
}.render #=> '<cra_zy__:!tag>foo</cra_zy__:!tag>'
|
193
|
+
```
|
194
|
+
|
157
195
|
## Template parameters
|
158
196
|
|
159
197
|
In Papercraft, parameters are always passed explicitly. This means that template
|
@@ -221,8 +259,8 @@ Papercraft.html(&greeting).render('world')
|
|
221
259
|
|
222
260
|
## Template composition
|
223
261
|
|
224
|
-
Papercraft makes it easy to compose multiple
|
225
|
-
document. A Papercraft
|
262
|
+
Papercraft makes it easy to compose multiple templates into a whole HTML
|
263
|
+
document. A Papercraft template can contain other templates, as the following
|
226
264
|
example shows.
|
227
265
|
|
228
266
|
```ruby
|
@@ -250,14 +288,14 @@ page = Papercraft.html { |title, items|
|
|
250
288
|
}
|
251
289
|
}
|
252
290
|
|
253
|
-
page.render('Hello from
|
291
|
+
page.render('Hello from composed templates', [
|
254
292
|
{ id: 1, text: 'foo', checked: false },
|
255
293
|
{ id: 2, text: 'bar', checked: true }
|
256
294
|
])
|
257
295
|
```
|
258
296
|
|
259
|
-
In addition to using
|
260
|
-
non-constant
|
297
|
+
In addition to using templates defined as constants, you can also use
|
298
|
+
non-constant templates by invoking the `#emit` method:
|
261
299
|
|
262
300
|
```ruby
|
263
301
|
greeting = -> { span "Hello, world" }
|
@@ -272,11 +310,11 @@ Papercraft.html {
|
|
272
310
|
## Parameter and block application
|
273
311
|
|
274
312
|
Parameters and blocks can be applied to a template without it being rendered, by
|
275
|
-
using `#apply`. This mechanism is what allows
|
276
|
-
creation of higher-order
|
313
|
+
using `#apply`. This mechanism is what allows template composition and the
|
314
|
+
creation of higher-order templates.
|
277
315
|
|
278
|
-
The `#apply` method returns a new
|
279
|
-
or block to the original
|
316
|
+
The `#apply` method returns a new template which applies the given parameters and
|
317
|
+
or block to the original template:
|
280
318
|
|
281
319
|
```ruby
|
282
320
|
# parameter application
|
@@ -289,19 +327,19 @@ div_wrap = Papercraft.html { div { emit_yield } }
|
|
289
327
|
wrapped_h1 = div_wrap.apply { h1 'hi' }
|
290
328
|
wrapped_h1.render #=> "<div><h1>hi</h1></div>"
|
291
329
|
|
292
|
-
# wrap a
|
330
|
+
# wrap a template
|
293
331
|
wrapped_hello_world = div_wrap.apply(&hello_world)
|
294
332
|
wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
|
295
333
|
```
|
296
334
|
|
297
|
-
## Higher-order
|
335
|
+
## Higher-order templates
|
298
336
|
|
299
|
-
Papercraft also lets you create higher-order
|
300
|
-
|
301
|
-
|
302
|
-
markup, enhancing
|
337
|
+
Papercraft also lets you create higher-order templates, that is,
|
338
|
+
templates that take other templates as parameters, or as blocks. Higher-order
|
339
|
+
templates are handy for creating layouts, wrapping templates in arbitrary
|
340
|
+
markup, enhancing templates or injecting template parameters.
|
303
341
|
|
304
|
-
Here is a
|
342
|
+
Here is a higher-order template that takes a template as parameter:
|
305
343
|
|
306
344
|
```ruby
|
307
345
|
div_wrap = Papercraft.html { |inner| div { emit inner } }
|
@@ -310,7 +348,7 @@ wrapped_greeter = div_wrap.apply(greeter)
|
|
310
348
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
311
349
|
```
|
312
350
|
|
313
|
-
The inner
|
351
|
+
The inner template can also be passed as a block, as shown above:
|
314
352
|
|
315
353
|
```ruby
|
316
354
|
div_wrap = Papercraft.html { div { emit_yield } }
|
@@ -320,7 +358,7 @@ wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
|
320
358
|
|
321
359
|
## Layout template composition
|
322
360
|
|
323
|
-
One of the principal uses of higher-order
|
361
|
+
One of the principal uses of higher-order templates is the creation of nested
|
324
362
|
layouts. Suppose we have a website with a number of different layouts, and we'd
|
325
363
|
like to avoid having to repeat the same code in the different layouts. We can do
|
326
364
|
this by creating a `default` page template that takes a block, then use `#apply`
|
@@ -434,8 +472,8 @@ end
|
|
434
472
|
## Deferred evaluation
|
435
473
|
|
436
474
|
Deferred evaluation allows deferring the rendering of parts of a template until
|
437
|
-
the last moment, thus allowing an inner
|
438
|
-
outer
|
475
|
+
the last moment, thus allowing an inner template to manipulate the state of the
|
476
|
+
outer template. To in order to defer a part of a template, use `#defer`, and
|
439
477
|
include any markup in the provided block. This technique, in in conjunction with
|
440
478
|
holding state in instance variables, is an alternative to passing parameters,
|
441
479
|
which can be limiting in some situations.
|
@@ -444,11 +482,11 @@ A few use cases for deferred evaulation come to mind:
|
|
444
482
|
|
445
483
|
- Setting the page title.
|
446
484
|
- Adding a flash message to a page.
|
447
|
-
- Using
|
485
|
+
- Using templates that dynamically add static dependencies (JS and CSS) to the
|
448
486
|
page.
|
449
487
|
|
450
488
|
The last use case is particularly interesting. Imagine a `DependencyMananger`
|
451
|
-
class that can collect JS and CSS dependencies from the different
|
489
|
+
class that can collect JS and CSS dependencies from the different templates
|
452
490
|
integrated into the page, and adds them to the page's `<head>` element:
|
453
491
|
|
454
492
|
```ruby
|
@@ -513,21 +551,27 @@ and other associated methods:
|
|
513
551
|
|
514
552
|
```ruby
|
515
553
|
module BootstrapComponents
|
516
|
-
|
517
|
-
|
518
|
-
def card(**props)
|
554
|
+
def card(**props, &block)
|
519
555
|
div(class: 'card', **props) {
|
520
|
-
div(class: 'card-body')
|
521
|
-
emit_yield
|
522
|
-
}
|
556
|
+
div(class: 'card-body', &block)
|
523
557
|
}
|
524
558
|
end
|
525
|
-
|
559
|
+
|
526
560
|
def card_title(title)
|
527
|
-
|
561
|
+
h4(title, class: 'card-title')
|
562
|
+
end
|
563
|
+
|
564
|
+
def card_subtitle(subtitle)
|
565
|
+
h5(subtitle, class: 'card-subtitle')
|
528
566
|
end
|
529
567
|
|
530
|
-
|
568
|
+
def card_text(text)
|
569
|
+
p(text, class: 'card-text')
|
570
|
+
end
|
571
|
+
|
572
|
+
def card_link(text, **opts)
|
573
|
+
a(text, class: 'card-link', **opts)
|
574
|
+
end
|
531
575
|
end
|
532
576
|
|
533
577
|
Papercraft.extension(bootstrap: BootstrapComponents)
|
@@ -543,14 +587,12 @@ Papercraft.html {
|
|
543
587
|
bootstrap.card_title 'Card title'
|
544
588
|
bootstrap.card_subtitle 'Card subtitle'
|
545
589
|
bootstrap.card_text 'Some quick example text to build on the card title and make up the bulk of the card''s content.'
|
546
|
-
bootstrap.card_link '
|
547
|
-
bootstrap.card_link '
|
590
|
+
bootstrap.card_link 'Card link', href: '#foo'
|
591
|
+
bootstrap.card_link 'Another link', href: '#bar'
|
548
592
|
}
|
549
593
|
}
|
550
594
|
```
|
551
595
|
|
552
|
-
|
553
|
-
|
554
596
|
## XML templates
|
555
597
|
|
556
598
|
XML templates behave largely the same as HTML templates, with a few minor
|
@@ -10,6 +10,7 @@ module Papercraft
|
|
10
10
|
class ExtensionProxy
|
11
11
|
|
12
12
|
# Initializes a new ExtensionProxy.
|
13
|
+
#
|
13
14
|
# @param renderer [Papercraft::Renderer] renderer to proxy to
|
14
15
|
# @param mod [Module] extension module
|
15
16
|
# @return [void]
|
@@ -18,13 +19,25 @@ module Papercraft
|
|
18
19
|
extend(mod)
|
19
20
|
end
|
20
21
|
|
21
|
-
# Proxies missing methods to the renderer
|
22
|
+
# Proxies missing methods to the renderer.
|
23
|
+
#
|
22
24
|
# @param sym [Symbol] method name
|
23
25
|
# @param *args [Array] arguments
|
26
|
+
# @param *props [Array] named arguments
|
24
27
|
# @param &block [Proc] block
|
25
28
|
# @return void
|
26
|
-
def method_missing(sym, *args, &block)
|
27
|
-
@renderer.send(sym, *args, &block)
|
29
|
+
def method_missing(sym, *args, **props, &block)
|
30
|
+
@renderer.send(sym, *args, **props, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Overrides the `Kernel#p` method to emit a p tag.
|
34
|
+
#
|
35
|
+
# @param *args [Array] arguments
|
36
|
+
# @param *props [Array] named arguments
|
37
|
+
# @param &block [Proc] block
|
38
|
+
# @return void
|
39
|
+
def p(text = nil, **props, &block)
|
40
|
+
@renderer.p(text, **props, &block)
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
data/lib/papercraft/html.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './tags'
|
4
|
+
|
3
5
|
module Papercraft
|
4
6
|
# HTML Markup extensions
|
5
7
|
module HTML
|
8
|
+
include Tags
|
9
|
+
|
6
10
|
# Emits the p tag (overrides Object#p)
|
7
11
|
#
|
8
12
|
# @param text [String] text content of tag
|
@@ -81,5 +85,33 @@ module Papercraft
|
|
81
85
|
def emit_markdown(markdown, **opts)
|
82
86
|
emit Papercraft.markdown(markdown, **opts)
|
83
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Escapes the given text using HTML entities.
|
92
|
+
#
|
93
|
+
# @param text [String] text
|
94
|
+
# @return [String] escaped text
|
95
|
+
def escape_text(text)
|
96
|
+
EscapeUtils.escape_html(text.to_s)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Converts a tag to its string representation. Underscores will be converted
|
100
|
+
# to dashes.
|
101
|
+
#
|
102
|
+
# @param tag [Symbol, String] tag
|
103
|
+
# @return [String] tag string
|
104
|
+
def tag_repr(tag)
|
105
|
+
tag.to_s.tr('_', '-')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Converts an attribute to its string representation. Underscores will be
|
109
|
+
# converted to dashes.
|
110
|
+
#
|
111
|
+
# @param att [Symbol, String] attribute
|
112
|
+
# @return [String] attribute string
|
113
|
+
def att_repr(att)
|
114
|
+
att.to_s.tr('_', '-')
|
115
|
+
end
|
84
116
|
end
|
85
117
|
end
|
data/lib/papercraft/json.rb
CHANGED
@@ -5,69 +5,122 @@ require 'json'
|
|
5
5
|
module Papercraft
|
6
6
|
# JSON renderer extensions
|
7
7
|
module JSON
|
8
|
-
|
9
|
-
|
8
|
+
# Initializes a JSON renderer, setting up an object stack.
|
9
|
+
def initialize(&template)
|
10
|
+
@object_stack = [nil]
|
11
|
+
super
|
10
12
|
end
|
11
13
|
|
14
|
+
# Adds an array item to the current object target. If a block is given, the
|
15
|
+
# block is evaulated against a new object target, then added to the current
|
16
|
+
# array.
|
17
|
+
#
|
18
|
+
# @param value [Object] item
|
19
|
+
# @param &block [Proc] template block
|
20
|
+
# @return [void]
|
21
|
+
def item(value = nil, &block)
|
22
|
+
verify_array_target
|
23
|
+
if block
|
24
|
+
value = enter_object(&block)
|
25
|
+
end
|
26
|
+
push_array_item(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds a key-value item to the current object target. If a block is given,
|
30
|
+
# the block is evaulated against a new object target, then used as the
|
31
|
+
# value.
|
32
|
+
#
|
33
|
+
# @param key [Object] key
|
34
|
+
# @param value [Object] value
|
35
|
+
# @param &block [Proc] template block
|
36
|
+
# @return [void]
|
37
|
+
def kv(key, value = nil, &block)
|
38
|
+
verify_hash_target
|
39
|
+
if block
|
40
|
+
value = enter_object(&block)
|
41
|
+
end
|
42
|
+
push_kv_item(key, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Intercepts method calls by adding key-value pairs to the current object
|
46
|
+
# target.
|
47
|
+
#
|
48
|
+
# @param key [Object] key
|
49
|
+
# @param value [Object] value
|
50
|
+
# @param &block [Proc] template block
|
51
|
+
# @return [void]
|
52
|
+
def method_missing(sym, value = nil, &block)
|
53
|
+
kv(sym, value, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts the root object target to JSON.
|
57
|
+
#
|
58
|
+
# @return [String] JSON template result
|
59
|
+
def to_s
|
60
|
+
@object_stack[0].to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Adds a new entry to the object stack and evaluates the given block.
|
66
|
+
#
|
67
|
+
# @param &block [Proc] template block
|
68
|
+
# @return [void]
|
12
69
|
def with_object(&block)
|
13
|
-
object_stack << nil
|
70
|
+
@object_stack << nil
|
14
71
|
instance_eval(&block)
|
15
72
|
end
|
16
73
|
|
74
|
+
# Verifies that the current object target is not a hash.
|
75
|
+
#
|
76
|
+
# @return [bool]
|
17
77
|
def verify_array_target
|
18
|
-
case object_stack[-1]
|
78
|
+
case @object_stack[-1]
|
19
79
|
when nil
|
20
|
-
object_stack[-1] = []
|
80
|
+
@object_stack[-1] = []
|
21
81
|
when Hash
|
22
82
|
raise "Mixing array and hash values"
|
23
83
|
end
|
24
84
|
end
|
25
85
|
|
86
|
+
# Verifies that the current object target is not an array.
|
87
|
+
#
|
88
|
+
# @return [bool]
|
26
89
|
def verify_hash_target
|
27
|
-
case object_stack[-1]
|
90
|
+
case @object_stack[-1]
|
28
91
|
when nil
|
29
|
-
object_stack[-1] = {}
|
92
|
+
@object_stack[-1] = {}
|
30
93
|
when Array
|
31
94
|
raise "Mixing array and hash values"
|
32
95
|
end
|
33
96
|
end
|
34
97
|
|
98
|
+
# Pushes an array item to the current object target.
|
99
|
+
#
|
100
|
+
# @param value [Object] item
|
101
|
+
# @return [void]
|
35
102
|
def push_array_item(value)
|
36
|
-
object_stack[-1] << value
|
103
|
+
@object_stack[-1] << value
|
37
104
|
end
|
38
105
|
|
106
|
+
# Pushes a key value into the current object target.
|
107
|
+
#
|
108
|
+
# @param key [Object] key
|
109
|
+
# @param value [Object] value
|
110
|
+
# @return [void]
|
39
111
|
def push_kv_item(key, value)
|
40
|
-
object_stack[-1][key] = value
|
112
|
+
@object_stack[-1][key] = value
|
41
113
|
end
|
42
114
|
|
115
|
+
# Adds a new object to the object stack, evaluates the given template block,
|
116
|
+
# then pops the object off the stack.
|
117
|
+
#
|
118
|
+
# @param &block [Proc] template block
|
119
|
+
# @return [void]
|
43
120
|
def enter_object(&block)
|
44
|
-
object_stack << nil
|
121
|
+
@object_stack << nil
|
45
122
|
instance_eval(&block)
|
46
|
-
object_stack.pop
|
47
|
-
end
|
48
|
-
|
49
|
-
def item(value = nil, &block)
|
50
|
-
verify_array_target
|
51
|
-
if block
|
52
|
-
value = enter_object(&block)
|
53
|
-
end
|
54
|
-
push_array_item(value)
|
55
|
-
end
|
56
|
-
|
57
|
-
def kv(key, value, &block)
|
58
|
-
verify_hash_target
|
59
|
-
if block
|
60
|
-
value = enter_object(&block)
|
61
|
-
end
|
62
|
-
push_kv_item(key, value)
|
63
|
-
end
|
64
|
-
|
65
|
-
def method_missing(sym, value = nil, &block)
|
66
|
-
kv(sym, value, &block)
|
67
|
-
end
|
68
|
-
|
69
|
-
def to_s
|
70
|
-
object_stack[0].to_json
|
71
|
-
end
|
123
|
+
@object_stack.pop
|
124
|
+
end
|
72
125
|
end
|
73
126
|
end
|