papercraft 1.3 → 2.13
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 +93 -0
- data/README.md +246 -617
- data/lib/papercraft/compiler/nodes.rb +223 -0
- data/lib/papercraft/compiler/tag_translator.rb +93 -0
- data/lib/papercraft/compiler.rb +657 -201
- data/lib/papercraft/proc_ext.rb +118 -0
- data/lib/papercraft/template.rb +16 -195
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +113 -89
- metadata +11 -64
- data/lib/papercraft/compiler_old.rb +0 -701
- data/lib/papercraft/extension_proxy.rb +0 -41
- data/lib/papercraft/extensions/soap.rb +0 -42
- data/lib/papercraft/html.rb +0 -173
- data/lib/papercraft/json.rb +0 -128
- data/lib/papercraft/renderer.rb +0 -190
- data/lib/papercraft/tags.rb +0 -408
- data/lib/papercraft/xml.rb +0 -47
- data/lib/tilt/papercraft.rb +0 -25
data/README.md
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
Papercraft
|
5
5
|
</h1>
|
6
6
|
|
7
|
-
<h4 align="center">
|
7
|
+
<h4 align="center">Functional HTML templating for Ruby</h4>
|
8
8
|
|
9
9
|
<p align="center">
|
10
10
|
<a href="http://rubygems.org/gems/papercraft">
|
11
11
|
<img src="https://badge.fury.io/rb/papercraft.svg" alt="Ruby gem">
|
12
12
|
</a>
|
13
|
-
<a href="https://github.com/digital-fabric/papercraft/actions
|
14
|
-
<img src="https://github.com/digital-fabric/papercraft/workflows/
|
13
|
+
<a href="https://github.com/digital-fabric/papercraft/actions/workflows/test.yml">
|
14
|
+
<img src="https://github.com/digital-fabric/papercraft/actions/workflows/test.yml/badge.svg" alt="Tests">
|
15
15
|
</a>
|
16
16
|
<a href="https://github.com/digital-fabric/papercraft/blob/master/LICENSE">
|
17
17
|
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
|
@@ -24,160 +24,112 @@
|
|
24
24
|
|
25
25
|
## What is Papercraft?
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
Papercraft templates are expressed in plain Ruby, leading to easier debugging,
|
30
|
-
better protection against HTML/XML injection attacks, and better code reuse.
|
27
|
+
```ruby
|
28
|
+
require 'papercraft'
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
page = ->(**props) {
|
31
|
+
html {
|
32
|
+
head { title 'My Title' }
|
33
|
+
body { render_yield **props }
|
34
|
+
}
|
35
|
+
}
|
36
|
+
page.render {
|
37
|
+
p 'foo'
|
38
|
+
}
|
39
|
+
#=> "<html><head><title>Title</title></head><body><p>foo</p></body></html>"
|
40
|
+
```
|
35
41
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
application.
|
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.
|
40
45
|
|
41
|
-
Papercraft
|
42
|
-
|
43
|
-
|
44
|
-
libraries.
|
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.
|
45
49
|
|
46
|
-
Papercraft
|
47
|
-
|
48
|
-
|
50
|
+
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
|
53
|
+
application.
|
49
54
|
|
50
55
|
```ruby
|
51
56
|
require 'papercraft'
|
52
57
|
|
53
|
-
page =
|
58
|
+
page = ->(**props) {
|
54
59
|
html {
|
55
|
-
head { title 'Title' }
|
56
|
-
body {
|
60
|
+
head { title 'My Title' }
|
61
|
+
body { yield **props }
|
57
62
|
}
|
58
63
|
}
|
59
|
-
page.render {
|
60
|
-
|
64
|
+
page.render {
|
65
|
+
p(class: 'big') 'foo'
|
66
|
+
}
|
67
|
+
#=> "<html><head><title>Title</title></head><body><p class="big">foo</p></body></html>"
|
61
68
|
|
62
|
-
|
63
|
-
|
69
|
+
hello_page = page.apply ->(name:, **) {
|
70
|
+
h1 "Hello, #{name}!"
|
71
|
+
}
|
72
|
+
hello.render(name: 'world')
|
64
73
|
#=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
|
65
74
|
```
|
66
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
|
+
|
67
90
|
## Table of Content
|
68
91
|
|
69
|
-
- [
|
70
|
-
- [
|
71
|
-
- [
|
72
|
-
- [Adding Tags](#adding-tags)
|
73
|
-
- [Tag and Attribute Formatting](#tag-and-attribute-formatting)
|
74
|
-
- [Escaping Content](#escaping-content)
|
75
|
-
- [Direct Iteration](#direct-iteration)
|
92
|
+
- [Getting Started](#getting-started)
|
93
|
+
- [Basic Markup](#basic-markup)
|
94
|
+
- [Builtin Methods](#builtin-methods)
|
76
95
|
- [Template Parameters](#template-parameters)
|
77
96
|
- [Template Logic](#template-logic)
|
78
97
|
- [Template Blocks](#template-blocks)
|
79
|
-
- [Template Fragments](#template-fragments)
|
80
|
-
- [Plain Procs as Templates](#plain-procs-as-templates)
|
81
98
|
- [Template Composition](#template-composition)
|
82
99
|
- [Parameter and Block Application](#parameter-and-block-application)
|
83
100
|
- [Higher-Order Templates](#higher-order-templates)
|
84
101
|
- [Layout Template Composition](#layout-template-composition)
|
85
|
-
- [Emitting Raw HTML](#emitting-raw-html)
|
86
|
-
- [Emitting a String with HTML Encoding](#emitting-a-string-with-html-encoding)
|
87
102
|
- [Emitting Markdown](#emitting-markdown)
|
88
|
-
- [Working with MIME Types](#working-with-mime-types)
|
89
103
|
- [Deferred Evaluation](#deferred-evaluation)
|
90
|
-
- [
|
91
|
-
- [XML Templates](#xml-templates)
|
92
|
-
- [JSON Templates](#json-templates)
|
93
|
-
- [Papercraft Extensions](#papercraft-extensions)
|
94
|
-
- [Extending Specific Templates](#extending-specific-templates)
|
95
|
-
- [Inline Helper Methods](#inline-helper-methods)
|
96
|
-
- [Bundled Extensions](#bundled-extensions)
|
97
|
-
- [API Reference](#api-reference)
|
104
|
+
- [Cached Rendering](#cached-rendering)
|
98
105
|
|
99
|
-
|
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
|
100
108
|
|
101
|
-
|
109
|
+
## Getting Started
|
102
110
|
|
103
|
-
|
111
|
+
In Papercraft, an HTML template is expressed as a proc:
|
104
112
|
|
105
113
|
```ruby
|
106
|
-
|
107
|
-
```
|
108
|
-
|
109
|
-
Or manually:
|
110
|
-
|
111
|
-
```bash
|
112
|
-
$ gem install papercraft
|
113
|
-
```
|
114
|
-
|
115
|
-
## Basic Usage
|
116
|
-
|
117
|
-
To create an HTML template use `Papercraft.html`:
|
118
|
-
|
119
|
-
```ruby
|
120
|
-
require 'papercraft'
|
121
|
-
|
122
|
-
html = Papercraft.html {
|
114
|
+
html = -> {
|
123
115
|
div(id: 'greeter') { p 'Hello!' }
|
124
116
|
}
|
125
117
|
```
|
126
118
|
|
127
|
-
|
128
|
-
templates, respectively.)
|
129
|
-
|
130
|
-
Rendering a template is done using `#render`:
|
119
|
+
Rendering a template is done using `Proc#render`:
|
131
120
|
|
132
121
|
```ruby
|
133
|
-
|
134
|
-
```
|
135
|
-
|
136
|
-
## Using with Tilt
|
137
|
-
|
138
|
-
Papercraft templates can also be rendered using [Tilt](https://github.com/jeremyevans/tilt):
|
139
|
-
|
140
|
-
```ruby
|
141
|
-
require 'tilt/papercraft'
|
142
|
-
|
143
|
-
# loading from a file (with a .papercraft extension)
|
144
|
-
template = Tilt.new('mytemplate.papercraft')
|
145
|
-
template.render
|
146
|
-
|
147
|
-
# defining a template inline:
|
148
|
-
template = Tilt['papercraft'].new { <<~RUBY
|
149
|
-
h1 'Hello'
|
150
|
-
RUBY
|
151
|
-
}
|
152
|
-
template.render
|
153
|
-
```
|
154
|
-
|
155
|
-
When rendering using Tilt, the following local variables are available to the
|
156
|
-
template: `scope`, `locals` and `block`:
|
157
|
-
|
158
|
-
```ruby
|
159
|
-
template = Tilt['papercraft'].new { <<~RUBY
|
160
|
-
title scope.title
|
161
|
-
|
162
|
-
h1 locals[:message]
|
163
|
-
|
164
|
-
emit block if block
|
165
|
-
RUBY
|
166
|
-
}
|
167
|
-
|
168
|
-
def title
|
169
|
-
'foo'
|
170
|
-
end
|
122
|
+
require 'papercraft'
|
171
123
|
|
172
|
-
|
124
|
+
html.render #=> "<div id="greeter"><p>Hello!</p></div>"
|
173
125
|
```
|
174
126
|
|
175
|
-
##
|
127
|
+
## Basic Markup
|
176
128
|
|
177
129
|
Tags are added using unqualified method calls, and can be nested using blocks:
|
178
130
|
|
179
131
|
```ruby
|
180
|
-
|
132
|
+
-> {
|
181
133
|
html {
|
182
134
|
head {
|
183
135
|
title 'page title'
|
@@ -194,146 +146,191 @@ Papercraft.html {
|
|
194
146
|
Tag methods accept a string argument, a block, or no argument at all:
|
195
147
|
|
196
148
|
```ruby
|
197
|
-
|
149
|
+
-> { p 'hello' }.render #=> "<p>hello</p>"
|
198
150
|
|
199
|
-
|
151
|
+
-> { p { span '1'; span '2' } }.render #=> "<p><span>1</span><span>2</span></p>"
|
200
152
|
|
201
|
-
|
153
|
+
-> { hr() }.render #=> "<hr/>"
|
202
154
|
```
|
203
155
|
|
204
156
|
Tag methods also accept tag attributes, given as a hash:
|
205
157
|
|
206
158
|
```ruby
|
207
|
-
|
159
|
+
-> { img src: '/my.gif' }.render #=> "<img src=\"/my.gif\"/>"
|
208
160
|
|
209
|
-
|
161
|
+
-> { p "foobar", class: 'important' }.render #=> "<p class=\"important\">foobar</p>"
|
210
162
|
```
|
211
163
|
|
212
164
|
A `true` attribute value will emit a valueless attribute. A `nil` or `false`
|
213
165
|
attribute value will emit nothing:
|
214
166
|
|
215
167
|
```ruby
|
216
|
-
|
217
|
-
|
168
|
+
-> { button disabled: nil }.render #=> "<button></button>"
|
169
|
+
-> { button disabled: true }.render #=> "<button disabled></button>"
|
218
170
|
```
|
219
171
|
|
220
172
|
An attribute value given as an array will be joined by space characters:
|
221
173
|
|
222
174
|
```ruby
|
223
|
-
|
175
|
+
-> { div class: [:foo, :bar] }.render #=> "<div class=\"foo bar\"></div>"
|
224
176
|
```
|
225
177
|
|
226
|
-
|
178
|
+
### Tag and Attribute Formatting
|
227
179
|
|
228
|
-
Papercraft does not make any assumption about what tags and attributes you can
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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:
|
233
185
|
|
234
186
|
- HTML: underscores are converted to dashes:
|
235
187
|
|
236
188
|
```ruby
|
237
|
-
|
189
|
+
-> {
|
238
190
|
foo_bar { p 'Hello', data_name: 'world' }
|
239
191
|
}.render #=> '<foo-bar><p data-name="world">Hello</p></foo-bar>'
|
240
192
|
```
|
241
193
|
|
242
|
-
- XML: underscores are converted to dashes, double underscores are converted to
|
243
|
-
colons:
|
244
|
-
|
245
|
-
```ruby
|
246
|
-
Papercraft.xml {
|
247
|
-
soap__Envelope(
|
248
|
-
xmlns__soap: 'http://schemas.xmlsoap.org/soap/envelope/',
|
249
|
-
) { }
|
250
|
-
}.render #=> '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Envelope>'
|
251
|
-
```
|
252
|
-
|
253
194
|
If you need more precise control over tag names, you can use the `#tag` method,
|
254
195
|
which takes the tag name as its first parameter, then the rest of the parameters
|
255
196
|
normally used for tags:
|
256
197
|
|
257
198
|
```ruby
|
258
|
-
|
199
|
+
-> {
|
259
200
|
tag 'cra_zy__:!tag', 'foo'
|
260
201
|
}.render #=> '<cra_zy__:!tag>foo</cra_zy__:!tag>'
|
261
202
|
```
|
262
203
|
|
263
|
-
|
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
|
264
211
|
|
265
|
-
|
266
|
-
specific escaping algorithm depends on the template type. For both HTML and XML
|
267
|
-
templates, Papercraft uses
|
268
|
-
[escape_utils](https://github.com/brianmario/escape_utils), specifically:
|
212
|
+
In addition to normal tags, Papercraft provides the following method calls for templates:
|
269
213
|
|
270
|
-
-
|
271
|
-
- XML: `escape_utils.escape_xml`
|
214
|
+
### `#text` - emit escaped text
|
272
215
|
|
273
|
-
|
274
|
-
|
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:
|
275
218
|
|
276
|
-
|
277
|
-
|
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
|
+
```
|
278
241
|
|
279
|
-
|
242
|
+
### `#render_yield` - emit given block
|
280
243
|
|
281
|
-
|
282
|
-
|
283
|
-
`#each` block, we can simply pass the data source *directly* to the tag using
|
284
|
-
the `_for` attribute. This is particularly useful when we need to create a set
|
285
|
-
of nested tags for each item. Consider the following example:
|
244
|
+
`#render_yield` is used to emit a given block. If no block is given, a
|
245
|
+
`LocalJumpError` exception is raised:
|
286
246
|
|
287
247
|
```ruby
|
288
|
-
|
248
|
+
Card = ->(**props) {
|
249
|
+
card { render_yield(**props) }
|
250
|
+
}
|
251
|
+
|
252
|
+
Card.render(foo: 'bar') { |foo|
|
253
|
+
h1 foo
|
254
|
+
} #=> <card><h1>bar</h1></card>
|
255
|
+
```
|
256
|
+
|
257
|
+
`render_yield` can be called with or without arguments, which are passed to the
|
258
|
+
given block.
|
259
|
+
|
260
|
+
### `#render_children` - emit given block
|
289
261
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
262
|
+
`#render_children` is used to emit a given block, but does not raise an
|
263
|
+
exception if no block is given.
|
264
|
+
|
265
|
+
### `#defer` - emit deferred HTML
|
266
|
+
|
267
|
+
`#defer` is used to emit HTML in a deferred fashion - the deferred part will be
|
268
|
+
evaluated only after processing the entire template:
|
269
|
+
|
270
|
+
```ruby
|
271
|
+
Layout = -> {
|
272
|
+
head {
|
273
|
+
defer {
|
274
|
+
title @title
|
294
275
|
}
|
295
276
|
}
|
296
|
-
|
277
|
+
body {
|
278
|
+
render_yield
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
Layout.render {
|
283
|
+
@title = 'Foobar'
|
284
|
+
h1 'hi'
|
285
|
+
} #=> <head><title>Foobar</title></head><body><h1>hi</h1></body>
|
297
286
|
```
|
298
287
|
|
299
|
-
|
300
|
-
|
288
|
+
### `#render` - render the given template inline
|
289
|
+
|
290
|
+
`#render` is used to emit the given template. This can be used to compose
|
291
|
+
templates:
|
301
292
|
|
302
293
|
```ruby
|
303
|
-
|
304
|
-
|
305
|
-
|
294
|
+
partial = -> { p 'foo' }
|
295
|
+
-> {
|
296
|
+
div {
|
297
|
+
render partial
|
306
298
|
}
|
307
|
-
}.render #=>
|
299
|
+
}.render #=> <div><p>foo</p></div>
|
300
|
+
```
|
301
|
+
|
302
|
+
Any argument following the given template is passed to the template for
|
303
|
+
rendering:
|
304
|
+
|
305
|
+
```ruby
|
306
|
+
large_button = ->(title) { button(title, class: 'large') }
|
307
|
+
|
308
|
+
-> {
|
309
|
+
render large_button, 'foo'
|
310
|
+
}.render #=> <button class="large">foo</button>
|
308
311
|
```
|
309
312
|
|
310
|
-
|
311
|
-
`Enumerator`. For example, you can use `#each_with_index` or iterate over a
|
312
|
-
hash. Papercraft will pass all yielded values to the given block:
|
313
|
+
### `#html5` - emit an HTML5 document type declaration and html tag
|
313
314
|
|
314
315
|
```ruby
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
td idx + 1
|
319
|
-
td item
|
316
|
+
-> {
|
317
|
+
html5 {
|
318
|
+
p 'hi'
|
320
319
|
}
|
321
|
-
}
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
320
|
+
} #=> <!DOCTYPE html><html><p>hi</p></html>
|
321
|
+
```
|
322
|
+
|
323
|
+
### `#markdown` emit markdown content
|
324
|
+
|
325
|
+
`#markdown` is used for rendering markdown content. The call converts the given
|
326
|
+
markdown to HTML and emits it into the rendered HTML:
|
327
|
+
|
328
|
+
```ruby
|
329
|
+
-> {
|
330
|
+
div {
|
331
|
+
markdown 'This is *markdown*'
|
333
332
|
}
|
334
|
-
}.render
|
335
|
-
#=> '<div class="row"><div><span>name</span><span>foo</span></div><div><span>age</span><span>16</span></div></div>'
|
336
|
-
#=> '<div class="row"><div><span>name</span><span>bar</span></div><div><span>age</span><span>32</span></div></div>'
|
333
|
+
}.render #=> <p>This is <em>markdown</em></p>
|
337
334
|
```
|
338
335
|
|
339
336
|
## Template Parameters
|
@@ -343,24 +340,24 @@ parameters are specified as block parameters, and are passed to the template on
|
|
343
340
|
rendering:
|
344
341
|
|
345
342
|
```ruby
|
346
|
-
greeting =
|
343
|
+
greeting = ->(name) { h1 "Hello, #{name}!" }
|
347
344
|
greeting.render('world') #=> "<h1>Hello, world!</h1>"
|
348
345
|
```
|
349
346
|
|
350
347
|
Templates can also accept named parameters:
|
351
348
|
|
352
349
|
```ruby
|
353
|
-
greeting =
|
350
|
+
greeting = ->(name:) { h1 "Hello, #{name}!" }
|
354
351
|
greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
|
355
352
|
```
|
356
353
|
|
357
354
|
## Template Logic
|
358
355
|
|
359
|
-
Since Papercraft templates are just a bunch of Ruby, you can easily embed your
|
360
|
-
|
356
|
+
Since Papercraft templates are just a bunch of Ruby, you can easily embed your view
|
357
|
+
logic right in the template:
|
361
358
|
|
362
359
|
```ruby
|
363
|
-
|
360
|
+
->(user = nil) {
|
364
361
|
if user
|
365
362
|
span "Hello, #{user.name}!"
|
366
363
|
else
|
@@ -371,12 +368,12 @@ Papercraft.html { |user = nil|
|
|
371
368
|
|
372
369
|
## Template Blocks
|
373
370
|
|
374
|
-
Templates can also accept and render blocks by using `
|
371
|
+
Templates can also accept and render blocks by using `render_yield`:
|
375
372
|
|
376
373
|
```ruby
|
377
|
-
page =
|
374
|
+
page = -> {
|
378
375
|
html {
|
379
|
-
body {
|
376
|
+
body { render_yield }
|
380
377
|
}
|
381
378
|
}
|
382
379
|
|
@@ -384,57 +381,10 @@ page = Papercraft.html {
|
|
384
381
|
page.render { h1 'hi' }
|
385
382
|
```
|
386
383
|
|
387
|
-
## Template Fragments
|
388
|
-
|
389
|
-
Template fragments allow rendering specific parts of a template, instead of the
|
390
|
-
entire template. For example, you can define a template for a web page that
|
391
|
-
includes a form, but may want to render just the form. Instead of extracting the
|
392
|
-
code and putting it into a separate template, you can use template fragments to
|
393
|
-
render just that part of that template:
|
394
|
-
|
395
|
-
```ruby
|
396
|
-
page = Papercraft.html {
|
397
|
-
div {
|
398
|
-
h1 'Page title'
|
399
|
-
p 'Some text'
|
400
|
-
}
|
401
|
-
div(id: 'my-form') {
|
402
|
-
fragment(:form) {
|
403
|
-
form {
|
404
|
-
input(name: 'email')
|
405
|
-
button 'OK'
|
406
|
-
}
|
407
|
-
}
|
408
|
-
}
|
409
|
-
}
|
410
|
-
page.render_fragment(:form)
|
411
|
-
```
|
412
|
-
|
413
|
-
For more information on how to use template fragments, see the [HTMX
|
414
|
-
article](https://htmx.org/essays/template-fragments/).
|
415
|
-
|
416
|
-
## Plain Procs as Templates
|
417
|
-
|
418
|
-
With Papercraft you can write a template as a plain Ruby proc, and later render
|
419
|
-
it by passing it as a block to `Papercraft.html`:
|
420
|
-
|
421
|
-
```ruby
|
422
|
-
greeting = proc { |name| h1 "Hello, #{name}!" }
|
423
|
-
Papercraft.html(&greeting).render('world')
|
424
|
-
```
|
425
|
-
|
426
|
-
Components can also be expressed using lambda notation:
|
427
|
-
|
428
|
-
```ruby
|
429
|
-
greeting = ->(name) { h1 "Hello, #{name}!" }
|
430
|
-
Papercraft.html(&greeting).render('world')
|
431
|
-
```
|
432
|
-
|
433
384
|
## Template Composition
|
434
385
|
|
435
|
-
Papercraft makes it easy to compose multiple templates into a whole HTML
|
436
|
-
|
437
|
-
example shows.
|
386
|
+
Papercraft makes it easy to compose multiple templates into a whole HTML document. A Papercraft
|
387
|
+
template can contain other templates, as the following example shows.
|
438
388
|
|
439
389
|
```ruby
|
440
390
|
Title = ->(title) { h1 title }
|
@@ -454,7 +404,7 @@ ItemList = ->(items) {
|
|
454
404
|
}
|
455
405
|
}
|
456
406
|
|
457
|
-
page =
|
407
|
+
page = ->(title, items) {
|
458
408
|
html5 {
|
459
409
|
head { Title(title) }
|
460
410
|
body { ItemList(items) }
|
@@ -468,14 +418,14 @@ page.render('Hello from composed templates', [
|
|
468
418
|
```
|
469
419
|
|
470
420
|
In addition to using templates defined as constants, you can also use
|
471
|
-
non-constant templates by invoking the `#
|
421
|
+
non-constant templates by invoking the `#render` method:
|
472
422
|
|
473
423
|
```ruby
|
474
424
|
greeting = -> { span "Hello, world" }
|
475
425
|
|
476
|
-
|
426
|
+
-> {
|
477
427
|
div {
|
478
|
-
|
428
|
+
render greeting
|
479
429
|
}
|
480
430
|
}
|
481
431
|
```
|
@@ -486,17 +436,17 @@ Parameters and blocks can be applied to a template without it being rendered, by
|
|
486
436
|
using `#apply`. This mechanism is what allows template composition and the
|
487
437
|
creation of higher-order templates.
|
488
438
|
|
489
|
-
The `#apply` method returns a new template which applies the given parameters
|
490
|
-
or block to the original template:
|
439
|
+
The `#apply` method returns a new template which applies the given parameters
|
440
|
+
and or block to the original template:
|
491
441
|
|
492
442
|
```ruby
|
493
443
|
# parameter application
|
494
|
-
hello =
|
444
|
+
hello = -> { |name| h1 "Hello, #{name}!" }
|
495
445
|
hello_world = hello.apply('world')
|
496
446
|
hello_world.render #=> "<h1>Hello, world!</h1>"
|
497
447
|
|
498
448
|
# block application
|
499
|
-
div_wrap =
|
449
|
+
div_wrap = -> { div { render_yield } }
|
500
450
|
wrapped_h1 = div_wrap.apply { h1 'hi' }
|
501
451
|
wrapped_h1.render #=> "<div><h1>hi</h1></div>"
|
502
452
|
|
@@ -507,16 +457,16 @@ wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
|
|
507
457
|
|
508
458
|
## Higher-Order Templates
|
509
459
|
|
510
|
-
Papercraft also lets you create higher-order templates, that is,
|
511
|
-
|
512
|
-
|
513
|
-
|
460
|
+
Papercraft also lets you create higher-order templates, that is, templates that take
|
461
|
+
other templates as parameters, or as blocks. Higher-order templates are handy
|
462
|
+
for creating layouts, wrapping templates in arbitrary markup, enhancing
|
463
|
+
templates or injecting template parameters.
|
514
464
|
|
515
465
|
Here is a higher-order template that takes a template as parameter:
|
516
466
|
|
517
467
|
```ruby
|
518
|
-
div_wrap =
|
519
|
-
greeter =
|
468
|
+
div_wrap = -> { |inner| div { render inner } }
|
469
|
+
greeter = -> { h1 'hi' }
|
520
470
|
wrapped_greeter = div_wrap.apply(greeter)
|
521
471
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
522
472
|
```
|
@@ -524,7 +474,7 @@ wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
|
524
474
|
The inner template can also be passed as a block, as shown above:
|
525
475
|
|
526
476
|
```ruby
|
527
|
-
div_wrap =
|
477
|
+
div_wrap = -> { div { render_yield } }
|
528
478
|
wrapped_greeter = div_wrap.apply { h1 'hi' }
|
529
479
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
530
480
|
```
|
@@ -538,13 +488,13 @@ this by creating a `default` page template that takes a block, then use `#apply`
|
|
538
488
|
to create the other templates:
|
539
489
|
|
540
490
|
```ruby
|
541
|
-
default_layout =
|
491
|
+
default_layout = -> { |**params|
|
542
492
|
html5 {
|
543
493
|
head {
|
544
494
|
title: params[:title]
|
545
495
|
}
|
546
496
|
body {
|
547
|
-
|
497
|
+
render_yield(**params)
|
548
498
|
}
|
549
499
|
}
|
550
500
|
}
|
@@ -552,7 +502,7 @@ default_layout = Papercraft.html { |**params|
|
|
552
502
|
article_layout = default_layout.apply { |title:, body:|
|
553
503
|
article {
|
554
504
|
h1 title
|
555
|
-
|
505
|
+
markdown body
|
556
506
|
}
|
557
507
|
}
|
558
508
|
|
@@ -562,47 +512,27 @@ article_layout.render(
|
|
562
512
|
)
|
563
513
|
```
|
564
514
|
|
565
|
-
## Emitting Raw HTML
|
566
|
-
|
567
|
-
Raw HTML can be emitted using `#emit`:
|
568
|
-
|
569
|
-
```ruby
|
570
|
-
wrapped = Papercraft.html { |html| div { emit html } }
|
571
|
-
wrapped.render("<h1>hi</h1>") #=> "<div><h1>hi</h1></div>"
|
572
|
-
```
|
573
|
-
|
574
|
-
## Emitting a String with HTML Encoding
|
575
|
-
|
576
|
-
To emit a string with proper HTML encoding, without wrapping it in an HTML
|
577
|
-
element, use `#text`:
|
578
|
-
|
579
|
-
```ruby
|
580
|
-
Papercraft.html { text 'hi&lo' }.render #=> "hi&lo"
|
581
|
-
```
|
582
|
-
|
583
515
|
## Emitting Markdown
|
584
516
|
|
585
517
|
Markdown is rendered using the
|
586
518
|
[Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
|
587
|
-
`#
|
519
|
+
`#markdown`:
|
588
520
|
|
589
521
|
```ruby
|
590
|
-
template =
|
522
|
+
template = -> { |md| div { markdown md } }
|
591
523
|
template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
|
592
524
|
```
|
593
525
|
|
594
526
|
[Kramdown
|
595
527
|
options](https://kramdown.gettalong.org/options.html#available-options) can be
|
596
|
-
specified by adding them to the `#
|
528
|
+
specified by adding them to the `#markdown` call:
|
597
529
|
|
598
530
|
```ruby
|
599
|
-
template =
|
531
|
+
template = -> { |md| div { markdown md, auto_ids: false } }
|
600
532
|
template.render("# title") #=> "<div><h1>title</h1></div>"
|
601
533
|
```
|
602
534
|
|
603
|
-
|
604
|
-
render markdown in XML or JSON templates (usually for implementing RSS or JSON
|
605
|
-
feeds), you can use `Papercraft.markdown` directly:
|
535
|
+
You can also use `Papercraft.markdown` directly:
|
606
536
|
|
607
537
|
```ruby
|
608
538
|
Papercraft.markdown('# title') #=> "<h1>title</h1>"
|
@@ -615,7 +545,7 @@ The default Kramdown options are:
|
|
615
545
|
entity_output: :numeric,
|
616
546
|
syntax_highlighter: :rouge,
|
617
547
|
input: 'GFM',
|
618
|
-
hard_wrap: false
|
548
|
+
hard_wrap: false
|
619
549
|
}
|
620
550
|
```
|
621
551
|
|
@@ -626,22 +556,6 @@ The deafult options can be configured by accessing
|
|
626
556
|
Papercraft.default_kramdown_options[:auto_ids] = false
|
627
557
|
```
|
628
558
|
|
629
|
-
## Working with MIME Types
|
630
|
-
|
631
|
-
Papercraft lets you set and interrogate a template's MIME type, in order to be
|
632
|
-
able to dynamically set the `Content-Type` HTTP response header. A template's
|
633
|
-
MIME type can be set when creating the template, e.g. `Papercraft.xml(mime_type:
|
634
|
-
'application/rss+xml')`. You can interrogate the template's MIME type using
|
635
|
-
`#mime_type`:
|
636
|
-
|
637
|
-
```ruby
|
638
|
-
# using Qeweney (https://github.com/digital-fabric/qeweney)
|
639
|
-
def serve_template(req, template)
|
640
|
-
body = template.render
|
641
|
-
respond(body, 'Content-Type' => template.mime_type)
|
642
|
-
end
|
643
|
-
```
|
644
|
-
|
645
559
|
## Deferred Evaluation
|
646
560
|
|
647
561
|
Deferred evaluation allows deferring the rendering of parts of a template until
|
@@ -663,334 +577,49 @@ class that can collect JS and CSS dependencies from the different templates
|
|
663
577
|
integrated into the page, and adds them to the page's `<head>` element:
|
664
578
|
|
665
579
|
```ruby
|
666
|
-
|
667
|
-
|
580
|
+
deps = DependencyMananger.new
|
581
|
+
|
582
|
+
default_layout = -> { |**args|
|
668
583
|
head {
|
669
|
-
defer {
|
584
|
+
defer { render deps.head_markup }
|
670
585
|
}
|
671
|
-
body {
|
586
|
+
body { render_yield **args }
|
672
587
|
}
|
673
588
|
|
674
589
|
button = proc { |text, onclick|
|
675
|
-
|
676
|
-
|
590
|
+
deps.js '/static/js/button.js'
|
591
|
+
deps.css '/static/css/button.css'
|
677
592
|
|
678
593
|
button text, onclick: onclick
|
679
594
|
}
|
680
595
|
|
681
596
|
heading = proc { |text|
|
682
|
-
|
683
|
-
|
597
|
+
deps.js '/static/js/heading.js'
|
598
|
+
deps.css '/static/css/heading.css'
|
684
599
|
|
685
600
|
h1 text
|
686
601
|
}
|
687
602
|
|
688
603
|
page = default_layout.apply {
|
689
|
-
|
690
|
-
|
691
|
-
emit button, 'Beaufort', 'eat_beaufort()'
|
692
|
-
emit button, 'Mont d''or', 'eat_montdor()'
|
693
|
-
emit button, 'Époisses', 'eat_epoisses()'
|
694
|
-
}
|
695
|
-
```
|
696
|
-
|
697
|
-
## HTML Templates
|
698
|
-
|
699
|
-
HTML templates include a few HTML-specific methods to facilitate writing modern
|
700
|
-
HTML:
|
701
|
-
|
702
|
-
- `html5 { ... }` - emits an HTML 5 DOCTYPE (`<!DOCTYPE html>`)
|
703
|
-
- `import_map(root_path, root_url)` - emits an import map including all files
|
704
|
-
matching `<root_path>/*.js`, based on the given `root_url`
|
705
|
-
- `js_module(js)` - emits a `<script type="module">` element
|
706
|
-
- `link_stylesheet(href, **attributes)` - emits a `<link rel="stylesheet" ...>`
|
707
|
-
element
|
708
|
-
- `script(js, **attributes)` - emits an inline `<script>` element
|
709
|
-
- `style(css, **attributes)` - emits an inline `<style>` element
|
710
|
-
- `versioned_file_href(href, root_path, root_url)` - calculates a versioned href
|
711
|
-
for the given file
|
712
|
-
|
713
|
-
[HTML docs](https://www.rubydoc.info/gems/papercraft/Papercraft/HTML)
|
714
|
-
|
715
|
-
## XML Templates
|
716
|
-
|
717
|
-
XML templates behave largely the same as HTML templates, with a few minor
|
718
|
-
differences. XML templates employ a different encoding algorithm, and lack some
|
719
|
-
specific HTML functionality, such as emitting Markdown.
|
720
|
-
|
721
|
-
Here's an example showing how to create an RSS feed:
|
722
|
-
|
723
|
-
```ruby
|
724
|
-
rss = Papercraft.xml(mime_type: 'text/xml; charset=utf-8') { |resource:, **props|
|
725
|
-
rss(version: '2.0', 'xmlns:atom' => 'http://www.w3.org/2005/Atom') {
|
726
|
-
channel {
|
727
|
-
title 'Noteflakes'
|
728
|
-
link 'https://noteflakes.com/'
|
729
|
-
description 'A website by Sharon Rosner'
|
730
|
-
language 'en-us'
|
731
|
-
pubDate Time.now.httpdate
|
732
|
-
emit '<atom:link href="https://noteflakes.com/feeds/rss" rel="self" type="application/rss+xml" />'
|
733
|
-
|
734
|
-
article_entries = resource.page_list('/articles').reverse
|
735
|
-
|
736
|
-
article_entries.each { |e|
|
737
|
-
item {
|
738
|
-
title e[:title]
|
739
|
-
link "https://noteflakes.com#{e[:url]}"
|
740
|
-
guid "https://noteflakes.com#{e[:url]}"
|
741
|
-
pubDate e[:date].to_time.httpdate
|
742
|
-
description e[:html_content]
|
743
|
-
}
|
744
|
-
}
|
745
|
-
}
|
746
|
-
}
|
747
|
-
}
|
748
|
-
```
|
749
|
-
|
750
|
-
[XML docs](https://www.rubydoc.info/gems/papercraft/Papercraft/XML)
|
751
|
-
|
752
|
-
## JSON Templates
|
753
|
-
|
754
|
-
JSON templates behave largely the same as HTML and XML templates. The only major
|
755
|
-
difference is that for adding array items you'll need to use the `#item` method:
|
756
|
-
|
757
|
-
```ruby
|
758
|
-
Papercraft.json {
|
759
|
-
item 1
|
760
|
-
item 2
|
761
|
-
item 3
|
762
|
-
}.render #=> "[1,2,3]"
|
763
|
-
```
|
764
|
-
|
765
|
-
Otherwise, you can create arbitrarily complex JSON structures by mixing hashes
|
766
|
-
and arrays:
|
767
|
-
|
768
|
-
```Ruby
|
769
|
-
Papercraft.json {
|
770
|
-
foo {
|
771
|
-
bar {
|
772
|
-
item nil
|
773
|
-
item true
|
774
|
-
item 123.456
|
775
|
-
}
|
776
|
-
}
|
777
|
-
}.render #=> "{\"foo\":{\"bar\":[null,true,123.456]}}"
|
778
|
-
```
|
779
|
-
|
780
|
-
Papercraft uses the [JSON gem](https://rubyapi.org/3.1/o/json) under the hood in
|
781
|
-
order to generate actual JSON.
|
782
|
-
|
783
|
-
[JSON docs](https://www.rubydoc.info/gems/papercraft/Papercraft/JSON)
|
784
|
-
|
785
|
-
## Papercraft Extensions
|
786
|
-
|
787
|
-
Papercraft extensions are modules that contain one or more methods that can be
|
788
|
-
used to render complex HTML components. Extension modules can be used by
|
789
|
-
installing them as a namespaced extension using `Papercraft::extension`.
|
790
|
-
Extensions are particularly useful when you work with CSS frameworks such as
|
791
|
-
[Bootstrap](https://getbootstrap.com/), [Tailwind](https://tailwindui.com/) or
|
792
|
-
[Primer](https://primer.style/).
|
793
|
-
|
794
|
-
For example, to create a Bootstrap card component, the following HTML markup is
|
795
|
-
needed (example taken from the [Bootstrap
|
796
|
-
docs](https://getbootstrap.com/docs/5.1/components/card/#titles-text-and-links)):
|
797
|
-
|
798
|
-
```html
|
799
|
-
<div class="card" style="width: 18rem;">
|
800
|
-
<div class="card-body">
|
801
|
-
<h5 class="card-title">Card title</h5>
|
802
|
-
<h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
|
803
|
-
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
|
804
|
-
<a href="#" class="card-link">Card link</a>
|
805
|
-
<a href="#" class="card-link">Another link</a>
|
806
|
-
</div>
|
807
|
-
</div>
|
808
|
-
```
|
809
|
-
|
810
|
-
With Papercraft, we could create a `Bootstrap` extension with a `#card` method
|
811
|
-
and other associated methods:
|
812
|
-
|
813
|
-
```ruby
|
814
|
-
module BootstrapComponents
|
815
|
-
def card(**props, &block)
|
816
|
-
div(class: 'card', **props) {
|
817
|
-
div(class: 'card-body', &block)
|
818
|
-
}
|
819
|
-
end
|
820
|
-
|
821
|
-
def card_title(title)
|
822
|
-
h4(title, class: 'card-title')
|
823
|
-
end
|
824
|
-
|
825
|
-
def card_subtitle(subtitle)
|
826
|
-
h5(subtitle, class: 'card-subtitle')
|
827
|
-
end
|
828
|
-
|
829
|
-
def card_text(text)
|
830
|
-
p(text, class: 'card-text')
|
831
|
-
end
|
832
|
-
|
833
|
-
def card_link(text, **opts)
|
834
|
-
a(text, class: 'card-link', **opts)
|
835
|
-
end
|
836
|
-
end
|
837
|
-
|
838
|
-
Papercraft.extension(bootstrap: BootstrapComponents)
|
839
|
-
```
|
840
|
-
|
841
|
-
The call to `Papercraft::extension` lets us access the different methods of
|
842
|
-
`BootstrapComponents` by calling `#bootstrap` inside a template. With this,
|
843
|
-
we'll be able to express the above markup as follows:
|
844
|
-
|
845
|
-
```ruby
|
846
|
-
Papercraft.html {
|
847
|
-
bootstrap.card(style: 'width: 18rem') {
|
848
|
-
bootstrap.card_title 'Card title'
|
849
|
-
bootstrap.card_subtitle 'Card subtitle'
|
850
|
-
bootstrap.card_text 'Some quick example text to build on the card title and make up the bulk of the card''s content.'
|
851
|
-
bootstrap.card_link 'Card link', href: '#foo'
|
852
|
-
bootstrap.card_link 'Another link', href: '#bar'
|
853
|
-
}
|
854
|
-
}
|
855
|
-
```
|
604
|
+
render heading, "What's your favorite cheese?"
|
856
605
|
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
API being available to other templates. To do this you can use `#extend`:
|
861
|
-
|
862
|
-
```ruby
|
863
|
-
module CustomTags
|
864
|
-
def label(text)
|
865
|
-
span text, class: 'label'
|
866
|
-
end
|
867
|
-
end
|
868
|
-
|
869
|
-
Papercraft.html {
|
870
|
-
extend CustomTags
|
871
|
-
|
872
|
-
label 'foo'
|
606
|
+
render button, 'Beaufort', 'eat_beaufort()'
|
607
|
+
render button, 'Mont d''or', 'eat_montdor()'
|
608
|
+
render button, 'Époisses', 'eat_epoisses()'
|
873
609
|
}
|
874
610
|
```
|
875
611
|
|
876
|
-
|
877
|
-
accessible to any sub templates that are emitted.
|
612
|
+
## Cached Rendering
|
878
613
|
|
879
|
-
|
880
|
-
|
614
|
+
Papercraft provides a simple API for caching the result of a rendering. The cache stores
|
615
|
+
renderings of a template respective to the given arguments. To automatically
|
616
|
+
retrieve the cached rendered HTML, or generate it for the first time, use
|
617
|
+
`Proc#render_cached`:
|
881
618
|
|
882
619
|
```ruby
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
620
|
+
template = ->(title) { div { h1 title } }
|
621
|
+
template.render_cached('foo') #=> <div><h1>foo</h1></div>
|
622
|
+
template.render_cached('foo') #=> <div><h1>foo</h1></div> (from cache)
|
623
|
+
template.render_cached('bar') #=> <div><h1>bar</h1></div>
|
624
|
+
template.render_cached('bar') #=> <div><h1>bar</h1></div> (from cache)
|
888
625
|
```
|
889
|
-
|
890
|
-
### Inline Helper Methods
|
891
|
-
|
892
|
-
In addition to proper extensions defined in modules, you can also define
|
893
|
-
individual extension methods inline in your Papercraft templates. You can do
|
894
|
-
this using any of the following techniques:
|
895
|
-
|
896
|
-
1. Define a method in the template body:
|
897
|
-
|
898
|
-
```ruby
|
899
|
-
Papercraft.html {
|
900
|
-
def label(text)
|
901
|
-
span text, class: 'label'
|
902
|
-
end
|
903
|
-
|
904
|
-
label 'foo'
|
905
|
-
label 'bar'
|
906
|
-
}
|
907
|
-
```
|
908
|
-
|
909
|
-
2. Use `def_tag` to define a custom tag:
|
910
|
-
|
911
|
-
```ruby
|
912
|
-
Papercraft.html {
|
913
|
-
def_tag(:label) { |text| span text, class: 'label' }
|
914
|
-
|
915
|
-
label 'foo'
|
916
|
-
label 'bar'
|
917
|
-
}
|
918
|
-
```
|
919
|
-
|
920
|
-
Note that using any of the above methods you can also create custom components
|
921
|
-
that take a block with inner HTML:
|
922
|
-
|
923
|
-
```ruby
|
924
|
-
# using def
|
925
|
-
def section(title, &inner)
|
926
|
-
div {
|
927
|
-
h1 title
|
928
|
-
emit inner
|
929
|
-
}
|
930
|
-
end
|
931
|
-
|
932
|
-
# using def_tag
|
933
|
-
def_tag(:section) do |title, &inner|
|
934
|
-
div {
|
935
|
-
h1 title
|
936
|
-
emit inner
|
937
|
-
}
|
938
|
-
end
|
939
|
-
```
|
940
|
-
|
941
|
-
### Bundled Extensions
|
942
|
-
|
943
|
-
Papercraft comes bundled with a few extensions that address common use cases.
|
944
|
-
All bundled extensions are namespaced under `Papercraft::Extensions`, and must
|
945
|
-
be specifically required in order to be available to templates.
|
946
|
-
|
947
|
-
For all bundled Papercraft extensions, there's no need to call
|
948
|
-
`Papercraft.extension`, requiring the extension is sufficient.
|
949
|
-
|
950
|
-
#### SOAP Extension
|
951
|
-
|
952
|
-
> The SOAP extension was contributed by [@aemadrid](https://github.com/aemadrid).
|
953
|
-
|
954
|
-
The SOAP extension provides common tags for building SOAP payloads. To load the
|
955
|
-
SOAP extensions, require `polyphony/extensions/soap`. The extension provides the
|
956
|
-
following methods:
|
957
|
-
|
958
|
-
- `soap.Body(...)` - emits a `soap:Body` tag.
|
959
|
-
- `soap.Envelope(...)` - emits a `soap:Envelope` tag.
|
960
|
-
- `soap.Fault(...)` - emits a `soap:Fault` tag.
|
961
|
-
- `soap.Header(...)` - emits a `soap:Header` tag.
|
962
|
-
|
963
|
-
As mentioned above, namespaced tags and attributes may be specified by using
|
964
|
-
double underscores for colons. Other tags that contain special characters may be
|
965
|
-
emitted using the `#tag` method:
|
966
|
-
|
967
|
-
```ruby
|
968
|
-
require 'polyphony/extensions/soap'
|
969
|
-
|
970
|
-
xml = Papercraft.xml {
|
971
|
-
soap.Envelope(
|
972
|
-
xmlns__xsd: 'http://www.w3.org/2001/XMLSchema',
|
973
|
-
xmlns__xsi: 'http://www.w3.org/2001/XMLSchema-instance'
|
974
|
-
) {
|
975
|
-
soap.Body {
|
976
|
-
PosRequest(xmlns: 'http://Some.Site') {
|
977
|
-
tag('Ver1.0') {
|
978
|
-
Header {
|
979
|
-
SecretAPIKey 'some_secret_key'
|
980
|
-
}
|
981
|
-
Transaction {
|
982
|
-
SomeData {}
|
983
|
-
}
|
984
|
-
}
|
985
|
-
}
|
986
|
-
}
|
987
|
-
}
|
988
|
-
}
|
989
|
-
```
|
990
|
-
|
991
|
-
[SOAP docs](https://www.rubydoc.info/gems/papercraft/Papercraft/Extensions/Soap)
|
992
|
-
|
993
|
-
## API Reference
|
994
|
-
|
995
|
-
The API reference for this library can be found
|
996
|
-
[here](https://www.rubydoc.info/gems/papercraft).
|