papercraft 1.4 → 2.14
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 +243 -619
- data/lib/papercraft/compiler/nodes.rb +223 -0
- data/lib/papercraft/compiler/tag_translator.rb +93 -0
- data/lib/papercraft/compiler.rb +674 -200
- data/lib/papercraft/proc_ext.rb +118 -0
- data/lib/papercraft/template.rb +15 -194
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft.rb +111 -87
- metadata +11 -60
- 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_children **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 { render_children **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
|
-
- [
|
86
|
-
- [Emitting a String with HTML Encoding](#emitting-a-string-with-html-encoding)
|
87
|
-
- [Emitting Markdown](#emitting-markdown)
|
88
|
-
- [Working with MIME Types](#working-with-mime-types)
|
102
|
+
- [Rendering Markdown](#rendering-markdown)
|
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,186 @@ 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
|
+
```
|
278
227
|
|
279
|
-
|
228
|
+
### `#raw` - emit raw HTML
|
280
229
|
|
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:
|
230
|
+
`#raw` is used for emitting raw HTML, i.e. without escaping. You can use this to
|
231
|
+
emit an HTML snippet:
|
286
232
|
|
287
233
|
```ruby
|
288
|
-
|
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
|
289
243
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
294
270
|
}
|
295
271
|
}
|
296
|
-
|
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>
|
297
281
|
```
|
298
282
|
|
299
|
-
|
300
|
-
|
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:
|
301
287
|
|
302
288
|
```ruby
|
303
|
-
|
304
|
-
|
305
|
-
|
289
|
+
partial = -> { p 'foo' }
|
290
|
+
-> {
|
291
|
+
div {
|
292
|
+
render partial
|
306
293
|
}
|
307
|
-
}.render #=>
|
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>
|
308
306
|
```
|
309
307
|
|
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:
|
308
|
+
### `#html5` - emit an HTML5 document type declaration and html tag
|
313
309
|
|
314
310
|
```ruby
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
td idx + 1
|
319
|
-
td item
|
311
|
+
-> {
|
312
|
+
html5 {
|
313
|
+
p 'hi'
|
320
314
|
}
|
321
|
-
}
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
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*'
|
333
327
|
}
|
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>'
|
328
|
+
}.render #=> <p>This is <em>markdown</em></p>
|
337
329
|
```
|
338
330
|
|
339
331
|
## Template Parameters
|
@@ -343,24 +335,24 @@ parameters are specified as block parameters, and are passed to the template on
|
|
343
335
|
rendering:
|
344
336
|
|
345
337
|
```ruby
|
346
|
-
greeting =
|
338
|
+
greeting = ->(name) { h1 "Hello, #{name}!" }
|
347
339
|
greeting.render('world') #=> "<h1>Hello, world!</h1>"
|
348
340
|
```
|
349
341
|
|
350
342
|
Templates can also accept named parameters:
|
351
343
|
|
352
344
|
```ruby
|
353
|
-
greeting =
|
345
|
+
greeting = ->(name:) { h1 "Hello, #{name}!" }
|
354
346
|
greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
|
355
347
|
```
|
356
348
|
|
357
349
|
## Template Logic
|
358
350
|
|
359
|
-
Since Papercraft templates are just a bunch of Ruby, you can easily embed your
|
360
|
-
|
351
|
+
Since Papercraft templates are just a bunch of Ruby, you can easily embed your view
|
352
|
+
logic right in the template:
|
361
353
|
|
362
354
|
```ruby
|
363
|
-
|
355
|
+
->(user = nil) {
|
364
356
|
if user
|
365
357
|
span "Hello, #{user.name}!"
|
366
358
|
else
|
@@ -371,12 +363,12 @@ Papercraft.html { |user = nil|
|
|
371
363
|
|
372
364
|
## Template Blocks
|
373
365
|
|
374
|
-
Templates can also accept and render blocks by using `
|
366
|
+
Templates can also accept and render blocks by using `render_children`:
|
375
367
|
|
376
368
|
```ruby
|
377
|
-
page =
|
369
|
+
page = -> {
|
378
370
|
html {
|
379
|
-
body {
|
371
|
+
body { render_children }
|
380
372
|
}
|
381
373
|
}
|
382
374
|
|
@@ -384,57 +376,10 @@ page = Papercraft.html {
|
|
384
376
|
page.render { h1 'hi' }
|
385
377
|
```
|
386
378
|
|
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
379
|
## Template Composition
|
434
380
|
|
435
|
-
Papercraft makes it easy to compose multiple templates into a whole HTML
|
436
|
-
|
437
|
-
example shows.
|
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.
|
438
383
|
|
439
384
|
```ruby
|
440
385
|
Title = ->(title) { h1 title }
|
@@ -454,7 +399,7 @@ ItemList = ->(items) {
|
|
454
399
|
}
|
455
400
|
}
|
456
401
|
|
457
|
-
page =
|
402
|
+
page = ->(title, items) {
|
458
403
|
html5 {
|
459
404
|
head { Title(title) }
|
460
405
|
body { ItemList(items) }
|
@@ -468,14 +413,14 @@ page.render('Hello from composed templates', [
|
|
468
413
|
```
|
469
414
|
|
470
415
|
In addition to using templates defined as constants, you can also use
|
471
|
-
non-constant templates by invoking the `#
|
416
|
+
non-constant templates by invoking the `#render` method:
|
472
417
|
|
473
418
|
```ruby
|
474
419
|
greeting = -> { span "Hello, world" }
|
475
420
|
|
476
|
-
|
421
|
+
-> {
|
477
422
|
div {
|
478
|
-
|
423
|
+
render greeting
|
479
424
|
}
|
480
425
|
}
|
481
426
|
```
|
@@ -486,17 +431,17 @@ Parameters and blocks can be applied to a template without it being rendered, by
|
|
486
431
|
using `#apply`. This mechanism is what allows template composition and the
|
487
432
|
creation of higher-order templates.
|
488
433
|
|
489
|
-
The `#apply` method returns a new template which applies the given parameters
|
490
|
-
or block to the original template:
|
434
|
+
The `#apply` method returns a new template which applies the given parameters
|
435
|
+
and or block to the original template:
|
491
436
|
|
492
437
|
```ruby
|
493
438
|
# parameter application
|
494
|
-
hello =
|
439
|
+
hello = -> { |name| h1 "Hello, #{name}!" }
|
495
440
|
hello_world = hello.apply('world')
|
496
441
|
hello_world.render #=> "<h1>Hello, world!</h1>"
|
497
442
|
|
498
443
|
# block application
|
499
|
-
div_wrap =
|
444
|
+
div_wrap = -> { div { render_children } }
|
500
445
|
wrapped_h1 = div_wrap.apply { h1 'hi' }
|
501
446
|
wrapped_h1.render #=> "<div><h1>hi</h1></div>"
|
502
447
|
|
@@ -507,16 +452,16 @@ wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
|
|
507
452
|
|
508
453
|
## Higher-Order Templates
|
509
454
|
|
510
|
-
Papercraft also lets you create higher-order templates, that is,
|
511
|
-
|
512
|
-
|
513
|
-
|
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.
|
514
459
|
|
515
460
|
Here is a higher-order template that takes a template as parameter:
|
516
461
|
|
517
462
|
```ruby
|
518
|
-
div_wrap =
|
519
|
-
greeter =
|
463
|
+
div_wrap = -> { |inner| div { render inner } }
|
464
|
+
greeter = -> { h1 'hi' }
|
520
465
|
wrapped_greeter = div_wrap.apply(greeter)
|
521
466
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
522
467
|
```
|
@@ -524,7 +469,7 @@ wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
|
524
469
|
The inner template can also be passed as a block, as shown above:
|
525
470
|
|
526
471
|
```ruby
|
527
|
-
div_wrap =
|
472
|
+
div_wrap = -> { div { render_children } }
|
528
473
|
wrapped_greeter = div_wrap.apply { h1 'hi' }
|
529
474
|
wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
|
530
475
|
```
|
@@ -538,13 +483,13 @@ this by creating a `default` page template that takes a block, then use `#apply`
|
|
538
483
|
to create the other templates:
|
539
484
|
|
540
485
|
```ruby
|
541
|
-
default_layout =
|
486
|
+
default_layout = -> { |**params|
|
542
487
|
html5 {
|
543
488
|
head {
|
544
489
|
title: params[:title]
|
545
490
|
}
|
546
491
|
body {
|
547
|
-
|
492
|
+
render_children(**params)
|
548
493
|
}
|
549
494
|
}
|
550
495
|
}
|
@@ -552,7 +497,7 @@ default_layout = Papercraft.html { |**params|
|
|
552
497
|
article_layout = default_layout.apply { |title:, body:|
|
553
498
|
article {
|
554
499
|
h1 title
|
555
|
-
|
500
|
+
markdown body
|
556
501
|
}
|
557
502
|
}
|
558
503
|
|
@@ -562,47 +507,27 @@ article_layout.render(
|
|
562
507
|
)
|
563
508
|
```
|
564
509
|
|
565
|
-
##
|
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
|
-
## Emitting Markdown
|
510
|
+
## Rendering Markdown
|
584
511
|
|
585
512
|
Markdown is rendered using the
|
586
513
|
[Kramdown](https://kramdown.gettalong.org/index.html) gem. To emit Markdown, use
|
587
|
-
`#
|
514
|
+
`#markdown`:
|
588
515
|
|
589
516
|
```ruby
|
590
|
-
template =
|
517
|
+
template = -> { |md| div { markdown md } }
|
591
518
|
template.render("Here's some *Markdown*") #=> "<div><p>Here's some <em>Markdown</em><p>\n</div>"
|
592
519
|
```
|
593
520
|
|
594
521
|
[Kramdown
|
595
522
|
options](https://kramdown.gettalong.org/options.html#available-options) can be
|
596
|
-
specified by adding them to the `#
|
523
|
+
specified by adding them to the `#markdown` call:
|
597
524
|
|
598
525
|
```ruby
|
599
|
-
template =
|
526
|
+
template = -> { |md| div { markdown md, auto_ids: false } }
|
600
527
|
template.render("# title") #=> "<div><h1>title</h1></div>"
|
601
528
|
```
|
602
529
|
|
603
|
-
|
604
|
-
render markdown in XML or JSON templates (usually for implementing RSS or JSON
|
605
|
-
feeds), you can use `Papercraft.markdown` directly:
|
530
|
+
You can also use `Papercraft.markdown` directly:
|
606
531
|
|
607
532
|
```ruby
|
608
533
|
Papercraft.markdown('# title') #=> "<h1>title</h1>"
|
@@ -615,7 +540,7 @@ The default Kramdown options are:
|
|
615
540
|
entity_output: :numeric,
|
616
541
|
syntax_highlighter: :rouge,
|
617
542
|
input: 'GFM',
|
618
|
-
hard_wrap: false
|
543
|
+
hard_wrap: false
|
619
544
|
}
|
620
545
|
```
|
621
546
|
|
@@ -626,22 +551,6 @@ The deafult options can be configured by accessing
|
|
626
551
|
Papercraft.default_kramdown_options[:auto_ids] = false
|
627
552
|
```
|
628
553
|
|
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
554
|
## Deferred Evaluation
|
646
555
|
|
647
556
|
Deferred evaluation allows deferring the rendering of parts of a template until
|
@@ -663,334 +572,49 @@ class that can collect JS and CSS dependencies from the different templates
|
|
663
572
|
integrated into the page, and adds them to the page's `<head>` element:
|
664
573
|
|
665
574
|
```ruby
|
666
|
-
|
667
|
-
|
575
|
+
deps = DependencyMananger.new
|
576
|
+
|
577
|
+
default_layout = -> { |**args|
|
668
578
|
head {
|
669
|
-
defer {
|
579
|
+
defer { render deps.head_markup }
|
670
580
|
}
|
671
|
-
body {
|
581
|
+
body { render_children **args }
|
672
582
|
}
|
673
583
|
|
674
584
|
button = proc { |text, onclick|
|
675
|
-
|
676
|
-
|
585
|
+
deps.js '/static/js/button.js'
|
586
|
+
deps.css '/static/css/button.css'
|
677
587
|
|
678
588
|
button text, onclick: onclick
|
679
589
|
}
|
680
590
|
|
681
591
|
heading = proc { |text|
|
682
|
-
|
683
|
-
|
592
|
+
deps.js '/static/js/heading.js'
|
593
|
+
deps.css '/static/css/heading.css'
|
684
594
|
|
685
595
|
h1 text
|
686
596
|
}
|
687
597
|
|
688
598
|
page = default_layout.apply {
|
689
|
-
|
599
|
+
render heading, "What's your favorite cheese?"
|
690
600
|
|
691
|
-
|
692
|
-
|
693
|
-
|
601
|
+
render button, 'Beaufort', 'eat_beaufort()'
|
602
|
+
render button, 'Mont d''or', 'eat_montdor()'
|
603
|
+
render button, 'Époisses', 'eat_epoisses()'
|
694
604
|
}
|
695
605
|
```
|
696
606
|
|
697
|
-
##
|
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)
|
607
|
+
## Cached Rendering
|
751
608
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
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`:
|
756
613
|
|
757
614
|
```ruby
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
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]}}"
|
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)
|
778
620
|
```
|
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
|
-
```
|
856
|
-
|
857
|
-
### Extending Specific Templates
|
858
|
-
|
859
|
-
Sometimes you wish to extend a specific template locally, without the extension
|
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'
|
873
|
-
}
|
874
|
-
```
|
875
|
-
|
876
|
-
The extension is in effect as long as the template is processing, so it is also
|
877
|
-
accessible to any sub templates that are emitted.
|
878
|
-
|
879
|
-
Local extensions can also be namespaced by passing `#extend` a hash mapping
|
880
|
-
namespaces to modules:
|
881
|
-
|
882
|
-
```ruby
|
883
|
-
Papercraft.html {
|
884
|
-
extend custom: CustomTags
|
885
|
-
|
886
|
-
custom.label 'foo'
|
887
|
-
}
|
888
|
-
```
|
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).
|