papercraft 1.0 → 1.1
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 +4 -0
- data/README.md +64 -2
- data/lib/papercraft/extension_proxy.rb +2 -2
- data/lib/papercraft/html.rb +5 -5
- data/lib/papercraft/json.rb +5 -3
- data/lib/papercraft/renderer.rb +6 -6
- data/lib/papercraft/tags.rb +13 -7
- data/lib/papercraft/template.rb +6 -6
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft/xml.rb +1 -1
- data/lib/papercraft.rb +7 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41c765213957fc2681e2f4cd7d94e7580e45394128d61a800349aa6e848c6b00
|
4
|
+
data.tar.gz: 5c2b0a91c3d4c744db20536d08ae741a07dab3a9cfc2c5fa790ea987224b5c7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf87f3ae1bcf718ad7b03e5b1b895cd8065e2aee72f7044cf0fb822db029477aa98297b8caf66d9e3db1ee664d9e42a954afb203f856fa6f190765defe5139e1
|
7
|
+
data.tar.gz: bfd2847daa9f2d9be7a0aa7e0adee170771e3fcdd6ca0ad407478f7b5bff2848b63639d21c26d1b9d040ac44b741d9032ac24742e839e19f1327d81ffd88e901
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -24,7 +24,8 @@
|
|
24
24
|
|
25
25
|
## What is Papercraft?
|
26
26
|
|
27
|
-
Papercraft is a templating engine for dynamically producing
|
27
|
+
Papercraft is a templating engine for dynamically producing
|
28
|
+
[HTML](#html-templates), [XML](#xml-templates) or [JSON](#json-templates).
|
28
29
|
Papercraft templates are expressed in plain Ruby, leading to easier debugging,
|
29
30
|
better protection against HTML/XML injection attacks, and better code reuse.
|
30
31
|
|
@@ -71,6 +72,7 @@ hello.render('world')
|
|
71
72
|
- [Adding Tags](#adding-tags)
|
72
73
|
- [Tag and Attribute Formatting](#tag-and-attribute-formatting)
|
73
74
|
- [Escaping Content](#escaping-content)
|
75
|
+
- [Direct Iteration][#direct-iteration]
|
74
76
|
- [Template Parameters](#template-parameters)
|
75
77
|
- [Template Logic](#template-logic)
|
76
78
|
- [Template Blocks](#template-blocks)
|
@@ -130,7 +132,7 @@ html.render #=> "<div id="greeter"><p>Hello!</p></div>"
|
|
130
132
|
|
131
133
|
## Using with Tilt
|
132
134
|
|
133
|
-
Papercraft templates can also be rendered using Tilt:
|
135
|
+
Papercraft templates can also be rendered using [Tilt](https://github.com/jeremyevans/tilt):
|
134
136
|
|
135
137
|
```ruby
|
136
138
|
require 'tilt/papercraft'
|
@@ -271,6 +273,66 @@ below](#emitting-raw-html).
|
|
271
273
|
JSON templates are rendered using the `json` gem bundled with Ruby, which takes
|
272
274
|
care of escaping text values.
|
273
275
|
|
276
|
+
## Direct Iteration
|
277
|
+
|
278
|
+
Papercraft enables iterating directly over any enumerable data source. Instead
|
279
|
+
of rendering each item in a given data container by wrapping it inside of an
|
280
|
+
`#each` block, we can simply pass the data source *directly* to the tag using
|
281
|
+
the `_for` attribute. This is particularly useful when we need to create a set
|
282
|
+
of nested tags for each item. Consider the following example:
|
283
|
+
|
284
|
+
```ruby
|
285
|
+
data = %w{foo bar}
|
286
|
+
|
287
|
+
Papercraft.html {
|
288
|
+
data.each { |item|
|
289
|
+
tr {
|
290
|
+
td item
|
291
|
+
}
|
292
|
+
}
|
293
|
+
}.render #=> '<tr><td>foo</td></tr><tr><td>bar</td></tr>'
|
294
|
+
```
|
295
|
+
|
296
|
+
Instead of using `data.each` to iterate over the list of data, we can directly
|
297
|
+
pass the data source to the `tr` tag using the special `_for` attribute:
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
Papercraft.html {
|
301
|
+
tr(_for: data) { |item|
|
302
|
+
td item
|
303
|
+
}
|
304
|
+
}.render #=> '<tr><td>foo</td></tr><tr><td>bar</td></tr>'
|
305
|
+
```
|
306
|
+
|
307
|
+
Note that this will work with any data source that is an `Enumerable` or an
|
308
|
+
`Enumerator`. For example, you can use `#each_with_index` or iterate over a
|
309
|
+
hash. Papercraft will pass all yielded values to the given block:
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
data = %{foo bar}
|
313
|
+
Papercraft.html {
|
314
|
+
tr(_for: data.each_with_index) { |item, idx|
|
315
|
+
td idx + 1
|
316
|
+
td item
|
317
|
+
}
|
318
|
+
}.render #=> '<tr><td>1</td><td>foo</td></tr><tr><td>2</td><td>bar</td></tr>'
|
319
|
+
|
320
|
+
data = [
|
321
|
+
{ name: 'foo', age: 16 },
|
322
|
+
{ name: 'bar', age: 32 }
|
323
|
+
]
|
324
|
+
Papercraft.html {
|
325
|
+
div(_for: data, class: 'row') { |row|
|
326
|
+
div(_for: row) { |k, v|
|
327
|
+
span k
|
328
|
+
span v
|
329
|
+
}
|
330
|
+
}
|
331
|
+
}.render
|
332
|
+
#=> '<div class="row"><div><span>name</span><span>foo</span></div><div><span>age</span><span>16</span></div></div>'
|
333
|
+
#=> '<div class="row"><div><span>name</span><span>bar</span></div><div><span>age</span><span>32</span></div></div>'
|
334
|
+
```
|
335
|
+
|
274
336
|
## Template Parameters
|
275
337
|
|
276
338
|
In Papercraft, parameters are always passed explicitly. This means that template
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Papercraft
|
4
|
-
|
4
|
+
|
5
5
|
# An ExtensionProxy proxies method calls to a renderer. Extension proxies are
|
6
6
|
# used to provide an namespaced interface to Papercraft extensions. When an
|
7
7
|
# extension is installed using `Papercraft.extension`, a corresponding method
|
@@ -18,7 +18,7 @@ module Papercraft
|
|
18
18
|
@renderer = renderer
|
19
19
|
extend(mod)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# Proxies missing methods to the renderer.
|
23
23
|
#
|
24
24
|
# @param sym [Symbol] method name
|
data/lib/papercraft/html.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './tags'
|
4
4
|
|
5
|
-
module Papercraft
|
5
|
+
module Papercraft
|
6
6
|
# HTML Markup extensions
|
7
7
|
module HTML
|
8
8
|
include Tags
|
@@ -55,7 +55,7 @@ module Papercraft
|
|
55
55
|
|
56
56
|
@buffer << '>' << css << '</style>'
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
# Emits an inline JS script element.
|
60
60
|
#
|
61
61
|
# @param js [String, nil] Javascript code
|
@@ -82,8 +82,8 @@ module Papercraft
|
|
82
82
|
fn = File.join(root_path, href)
|
83
83
|
version = File.stat(fn).mtime.to_i rescue 0
|
84
84
|
"#{root_url}/#{href}?v=#{version}"
|
85
|
-
end
|
86
|
-
|
85
|
+
end
|
86
|
+
|
87
87
|
# Emits an import map scrit tag. If a hash is given, emits the hash as is.
|
88
88
|
# If a string is given, searches for all *.js files under the given path,
|
89
89
|
# and emits an import map including all found files, with versioned URLs.
|
@@ -135,7 +135,7 @@ module Papercraft
|
|
135
135
|
# @return [Bool] is it a void element
|
136
136
|
def is_void_element_tag?(tag)
|
137
137
|
case tag
|
138
|
-
#
|
138
|
+
#
|
139
139
|
when 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'source', 'track', 'wbr'
|
140
140
|
true
|
141
141
|
else
|
data/lib/papercraft/json.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'json'
|
4
4
|
|
5
|
-
module Papercraft
|
5
|
+
module Papercraft
|
6
6
|
# JSON renderer extensions
|
7
7
|
module JSON
|
8
8
|
# Initializes a JSON renderer, setting up an object stack.
|
@@ -18,7 +18,9 @@ module Papercraft
|
|
18
18
|
# @param value [Object] item
|
19
19
|
# @param &block [Proc] template block
|
20
20
|
# @return [void]
|
21
|
-
def item(value = nil, &block)
|
21
|
+
def item(value = nil, _for: nil, &block)
|
22
|
+
return _for.each { |*a| item(value) { block.(*a)} } if _for
|
23
|
+
|
22
24
|
verify_array_target
|
23
25
|
if block
|
24
26
|
value = enter_object(&block)
|
@@ -121,6 +123,6 @@ module Papercraft
|
|
121
123
|
@object_stack << nil
|
122
124
|
instance_eval(&block)
|
123
125
|
@object_stack.pop
|
124
|
-
end
|
126
|
+
end
|
125
127
|
end
|
126
128
|
end
|
data/lib/papercraft/renderer.rb
CHANGED
@@ -6,7 +6,7 @@ require_relative './json'
|
|
6
6
|
require_relative './extension_proxy'
|
7
7
|
|
8
8
|
module Papercraft
|
9
|
-
|
9
|
+
|
10
10
|
# A Renderer renders a Papercraft template into a string
|
11
11
|
class Renderer
|
12
12
|
|
@@ -46,7 +46,7 @@ module Papercraft
|
|
46
46
|
# the the renderer instance, so they can look just like normal proc
|
47
47
|
# components. In cases where method names in the module clash with XML
|
48
48
|
# tag names, you can use the `#tag` method to emit the relevant tag.
|
49
|
-
#
|
49
|
+
#
|
50
50
|
# module ComponentLibrary
|
51
51
|
# def card(title, content)
|
52
52
|
# div(class: 'card') {
|
@@ -96,9 +96,9 @@ module Papercraft
|
|
96
96
|
#
|
97
97
|
# greeter = proc { |name| h1 "Hello, #{name}!" }
|
98
98
|
# Papercraft.html { emit(greeter, 'world') }.render #=> "<h1>Hello, world!</h1>"
|
99
|
-
#
|
99
|
+
#
|
100
100
|
# Papercraft.html { emit 'hi&<bye>' }.render #=> "hi&<bye>"
|
101
|
-
#
|
101
|
+
#
|
102
102
|
# Papercraft.html { emit nil }.render #=> ""
|
103
103
|
#
|
104
104
|
# @param o [Proc, Papercraft::Template, String] emitted object
|
@@ -131,10 +131,10 @@ module Papercraft
|
|
131
131
|
def emit_yield(*a, **b)
|
132
132
|
block = @emit_yield_stack&.pop
|
133
133
|
raise Papercraft::Error, "No block given" unless block
|
134
|
-
|
134
|
+
|
135
135
|
instance_exec(*a, **b, &block)
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
private
|
139
139
|
|
140
140
|
# Pushes the given block onto the emit_yield stack.
|
data/lib/papercraft/tags.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './extension_proxy'
|
4
4
|
|
5
|
-
module Papercraft
|
5
|
+
module Papercraft
|
6
6
|
# Markup (HTML/XML) extensions
|
7
7
|
module Tags
|
8
8
|
S_LT = '<'
|
@@ -22,7 +22,9 @@ module Papercraft
|
|
22
22
|
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
23
23
|
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
24
24
|
|
25
|
-
def %<tag>s(text = nil, **props, &block)
|
25
|
+
def %<tag>s(text = nil, _for: nil, **props, &block)
|
26
|
+
return _for.each { |*a| %<tag>s(text, **props) { block.(*a)} } if _for
|
27
|
+
|
26
28
|
if text.is_a?(Hash) && props.empty?
|
27
29
|
props = text
|
28
30
|
text = nil
|
@@ -52,7 +54,9 @@ module Papercraft
|
|
52
54
|
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
53
55
|
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
54
56
|
|
55
|
-
def %<tag>s(text = nil, **props, &block)
|
57
|
+
def %<tag>s(text = nil, _for: nil, **props, &block)
|
58
|
+
return _for.each { |*a| %<tag>s(text, **props) { block.(*a)} } if _for
|
59
|
+
|
56
60
|
if text.is_a?(Hash) && props.empty?
|
57
61
|
props = text
|
58
62
|
text = nil
|
@@ -141,8 +145,8 @@ module Papercraft
|
|
141
145
|
end
|
142
146
|
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
143
147
|
end
|
144
|
-
|
145
|
-
|
148
|
+
|
149
|
+
|
146
150
|
# Emits an XML tag with the given content, properties and optional block.
|
147
151
|
# This method is an alternative to emitting XML tags using dynamically
|
148
152
|
# created methods. This is particularly useful when using extensions that
|
@@ -159,7 +163,9 @@ module Papercraft
|
|
159
163
|
# @param **props [Hash] tag attributes
|
160
164
|
# @param &block [Proc] optional inner XML
|
161
165
|
# @return [void]
|
162
|
-
def tag(sym, text = nil, **props, &block)
|
166
|
+
def tag(sym, text = nil, _for: nil, **props, &block)
|
167
|
+
return _for.each { |*a| tag(sym, text, **props) { block.(*a)} } if _for
|
168
|
+
|
163
169
|
if text.is_a?(Hash) && props.empty?
|
164
170
|
props = text
|
165
171
|
text = nil
|
@@ -337,7 +343,7 @@ module Papercraft
|
|
337
343
|
# @return [void]
|
338
344
|
def render_deferred_proc(&block)
|
339
345
|
old_buffer = @buffer
|
340
|
-
|
346
|
+
|
341
347
|
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
342
348
|
@parts = nil
|
343
349
|
|
data/lib/papercraft/template.rb
CHANGED
@@ -85,7 +85,7 @@ module Papercraft
|
|
85
85
|
# links_with_anchors.render
|
86
86
|
#
|
87
87
|
class Template < Proc
|
88
|
-
|
88
|
+
|
89
89
|
# Determines the rendering mode: `:html` or `:xml`.
|
90
90
|
attr_accessor :mode
|
91
91
|
|
@@ -107,9 +107,9 @@ module Papercraft
|
|
107
107
|
@mime_type = mime_type || STOCK_MIME_TYPE[mode]
|
108
108
|
super(&block)
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
H_EMPTY = {}.freeze
|
112
|
-
|
112
|
+
|
113
113
|
# Renders the template with the given parameters and or block, and returns
|
114
114
|
# the string result.
|
115
115
|
#
|
@@ -123,7 +123,7 @@ module Papercraft
|
|
123
123
|
instance_exec(*a, **b, &template)
|
124
124
|
end.to_s
|
125
125
|
end
|
126
|
-
|
126
|
+
|
127
127
|
# Creates a new template, applying the given parameters and or block to the
|
128
128
|
# current one. Application is one of the principal methods of composing
|
129
129
|
# templates, particularly when passing inner templates as blocks:
|
@@ -149,7 +149,7 @@ module Papercraft
|
|
149
149
|
instance_exec(*a, *x, **b, **y, &template)
|
150
150
|
end)
|
151
151
|
end
|
152
|
-
|
152
|
+
|
153
153
|
# Returns the Renderer class used for rendering the templates, according to
|
154
154
|
# the template's mode.
|
155
155
|
#
|
@@ -173,7 +173,7 @@ module Papercraft
|
|
173
173
|
def mime_type
|
174
174
|
@mime_type
|
175
175
|
end
|
176
|
-
|
176
|
+
|
177
177
|
# def compile
|
178
178
|
# Papercraft::Compiler.new.compile(self)
|
179
179
|
# end
|
data/lib/papercraft/version.rb
CHANGED
data/lib/papercraft/xml.rb
CHANGED
data/lib/papercraft.rb
CHANGED
@@ -13,9 +13,9 @@ require_relative 'papercraft/renderer'
|
|
13
13
|
module Papercraft
|
14
14
|
# Exception class used to signal templating-related errors
|
15
15
|
class Error < RuntimeError; end
|
16
|
-
|
16
|
+
|
17
17
|
class << self
|
18
|
-
|
18
|
+
|
19
19
|
# Installs one or more extensions. Extensions enhance templating capabilities
|
20
20
|
# by adding namespaced methods to emplates. An extension is implemented as a
|
21
21
|
# Ruby module containing one or more methods. Each method in the extension
|
@@ -29,7 +29,7 @@ module Papercraft
|
|
29
29
|
def extension(map)
|
30
30
|
Renderer.extension(map)
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
# Creates a new papercraft template. `Papercraft.html` can take either a proc
|
34
34
|
# argument or a block. In both cases, the proc is converted to a
|
35
35
|
# `Papercraft::Template`.
|
@@ -44,7 +44,7 @@ module Papercraft
|
|
44
44
|
template ||= o
|
45
45
|
Papercraft::Template.new(mode: :html, mime_type: mime_type, &template)
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
# Creates a new Papercraft template in XML mode. `Papercraft.xml` can take
|
49
49
|
# either a proc argument or a block. In both cases, the proc is converted to a
|
50
50
|
# `Papercraft::Template`.
|
@@ -59,7 +59,7 @@ module Papercraft
|
|
59
59
|
template ||= o
|
60
60
|
Papercraft::Template.new(mode: :xml, mime_type: mime_type, &template)
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
# Creates a new Papercraft template in JSON mode. `Papercraft.json` can take
|
64
64
|
# either a proc argument or a block. In both cases, the proc is converted to a
|
65
65
|
# `Papercraft::Template`.
|
@@ -85,7 +85,7 @@ module Papercraft
|
|
85
85
|
opts = default_kramdown_options.merge(opts)
|
86
86
|
Kramdown::Document.new(markdown, **opts).to_html
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
# Returns the default Kramdown options used for rendering Markdown.
|
90
90
|
#
|
91
91
|
# @return [Hash] Kramdown options
|
@@ -94,7 +94,7 @@ module Papercraft
|
|
94
94
|
entity_output: :numeric,
|
95
95
|
syntax_highlighter: :rouge,
|
96
96
|
input: 'GFM',
|
97
|
-
hard_wrap: false
|
97
|
+
hard_wrap: false
|
98
98
|
}
|
99
99
|
end
|
100
100
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: papercraft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '1.
|
4
|
+
version: '1.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03
|
11
|
+
date: 2023-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: escape_utils
|