papercraft 0.19 → 0.20
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 +5 -0
- data/lib/papercraft/html.rb +11 -0
- data/lib/papercraft/renderer.rb +5 -152
- data/lib/papercraft/tags.rb +152 -0
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft/xml.rb +26 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eaf6ba31b9300176af0bf55b76f7bbdc16508d0e495ed1d76f9f273ea88f250
|
4
|
+
data.tar.gz: 90e132928ebfe78e577e71c8dc498f0ce1b1f2c58f453fbedaad96121c448a48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcada325a3f8cd1c4cbe38674efaf6a86d0eb947ad5815d07778e2a09a54bee2c7d6b731e87d110390e9c87abc0a61f81cbc4cb30fdce11a4fc8a8cd322466a0
|
7
|
+
data.tar.gz: 031c86f7dade95af3dca979b4543fcb26e1fab2eb98c5642746d6efcf8b4bd56fe30b70f2f5aa8309352f421cae9f116a901f464216f54a0be50630dcc52815f
|
data/CHANGELOG.md
CHANGED
data/lib/papercraft/html.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './tags'
|
4
|
+
|
3
5
|
module Papercraft
|
4
6
|
# HTML Markup extensions
|
5
7
|
module HTML
|
8
|
+
include Tags
|
9
|
+
|
6
10
|
# Emits the p tag (overrides Object#p)
|
7
11
|
#
|
8
12
|
# @param text [String] text content of tag
|
@@ -81,5 +85,12 @@ module Papercraft
|
|
81
85
|
def emit_markdown(markdown, **opts)
|
82
86
|
emit Papercraft.markdown(markdown, **opts)
|
83
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Escapes the given text using XML entities.
|
92
|
+
def escape_text(text)
|
93
|
+
EscapeUtils.escape_html(text.to_s)
|
94
|
+
end
|
84
95
|
end
|
85
96
|
end
|
data/lib/papercraft/renderer.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'escape_utils'
|
4
|
-
|
5
3
|
require_relative './html'
|
4
|
+
require_relative './xml'
|
6
5
|
require_relative './json'
|
7
6
|
require_relative './extension_proxy'
|
8
7
|
|
@@ -10,7 +9,7 @@ module Papercraft
|
|
10
9
|
|
11
10
|
# A Renderer renders a Papercraft template into a string
|
12
11
|
class Renderer
|
13
|
-
|
12
|
+
|
14
13
|
class << self
|
15
14
|
|
16
15
|
# Verifies that the given template proc can be called with the given
|
@@ -45,7 +44,7 @@ module Papercraft
|
|
45
44
|
# methods to extension modules. The methods will be available to all
|
46
45
|
# Papercraft templates. Extension methods are executed in the context of
|
47
46
|
# the the renderer instance, so they can look just like normal proc
|
48
|
-
# components. In cases where method names in the module clash with
|
47
|
+
# components. In cases where method names in the module clash with XML
|
49
48
|
# tag names, you can use the `#tag` method to emit the relevant tag.
|
50
49
|
#
|
51
50
|
# module ComponentLibrary
|
@@ -113,99 +112,6 @@ module Papercraft
|
|
113
112
|
@buffer
|
114
113
|
end
|
115
114
|
|
116
|
-
# The tag method template below is optimized for performance. Do not touch!
|
117
|
-
|
118
|
-
S_TAG_METHOD_LINE = __LINE__ + 2
|
119
|
-
S_TAG_METHOD = <<~EOF
|
120
|
-
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
121
|
-
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
122
|
-
|
123
|
-
def %<tag>s(text = nil, **props, &block)
|
124
|
-
if text.is_a?(Hash) && props.empty?
|
125
|
-
props = text
|
126
|
-
text = nil
|
127
|
-
end
|
128
|
-
|
129
|
-
@buffer << S_TAG_%<TAG>s_PRE
|
130
|
-
emit_props(props) unless props.empty?
|
131
|
-
|
132
|
-
if block
|
133
|
-
@buffer << S_GT
|
134
|
-
instance_eval(&block)
|
135
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
136
|
-
elsif Proc === text
|
137
|
-
@buffer << S_GT
|
138
|
-
emit(text)
|
139
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
140
|
-
elsif text
|
141
|
-
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
142
|
-
else
|
143
|
-
@buffer << S_SLASH_GT
|
144
|
-
end
|
145
|
-
end
|
146
|
-
EOF
|
147
|
-
|
148
|
-
# Emits an HTML tag with the given content, properties and optional block.
|
149
|
-
# This method is an alternative to emitting HTML tags using dynamically
|
150
|
-
# created methods. This is particularly useful when using extensions that
|
151
|
-
# have method names that clash with HTML tags, such as `button` or `a`, or
|
152
|
-
# when you need to override the behaviour of a particular HTML tag.
|
153
|
-
#
|
154
|
-
# The following two method calls have the same effect:
|
155
|
-
#
|
156
|
-
# button 'text', id: 'button1'
|
157
|
-
# tag :button, 'text', id: 'button1'
|
158
|
-
#
|
159
|
-
# @param sym [Symbol, String] HTML tag
|
160
|
-
# @param text [String, nil] tag content
|
161
|
-
# @param **props [Hash] tag attributes
|
162
|
-
# @param &block [Proc] optional inner HTML
|
163
|
-
# @return [void]
|
164
|
-
def tag(sym, text = nil, **props, &block)
|
165
|
-
if text.is_a?(Hash) && props.empty?
|
166
|
-
props = text
|
167
|
-
text = nil
|
168
|
-
end
|
169
|
-
|
170
|
-
tag = sym.to_s.tr('_', '-')
|
171
|
-
|
172
|
-
@buffer << S_LT << tag
|
173
|
-
emit_props(props) unless props.empty?
|
174
|
-
|
175
|
-
if block
|
176
|
-
@buffer << S_GT
|
177
|
-
instance_eval(&block)
|
178
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
179
|
-
elsif Proc === text
|
180
|
-
@buffer << S_GT
|
181
|
-
emit(text)
|
182
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
183
|
-
elsif text
|
184
|
-
@buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
|
185
|
-
else
|
186
|
-
@buffer << S_SLASH_GT
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
# Catches undefined tag method call and handles it by defining the method.
|
191
|
-
#
|
192
|
-
# @param sym [Symbol] HTML tag or component identifier
|
193
|
-
# @param args [Array] method arguments
|
194
|
-
# @param opts [Hash] named method arguments
|
195
|
-
# @param &block [Proc] block passed to method
|
196
|
-
# @return [void]
|
197
|
-
def method_missing(sym, *args, **opts, &block)
|
198
|
-
tag = sym.to_s
|
199
|
-
code = S_TAG_METHOD % {
|
200
|
-
tag: tag,
|
201
|
-
TAG: tag.upcase,
|
202
|
-
tag_pre: "<#{tag.tr('_', '-')}".inspect,
|
203
|
-
tag_close: "</#{tag.tr('_', '-')}>".inspect
|
204
|
-
}
|
205
|
-
self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
|
206
|
-
send(sym, *args, **opts, &block)
|
207
|
-
end
|
208
|
-
|
209
115
|
# Emits the given object into the rendering buffer. If the given object is a
|
210
116
|
# proc or a component, `emit` will passes any additional arguments and named
|
211
117
|
# arguments to the object when rendering it. If the given object is nil,
|
@@ -256,7 +162,7 @@ module Papercraft
|
|
256
162
|
# Papercraft templates to inject state into sibling components, regardless
|
257
163
|
# of the component's order in the container component. For example, a nested
|
258
164
|
# component may set an instance variable used by another component. This is
|
259
|
-
# an elegant solution to the problem of setting the
|
165
|
+
# an elegant solution to the problem of setting the XML page's title, or
|
260
166
|
# adding elements to the `<head>` section. Here's how a title can be
|
261
167
|
# controlled from a nested component:
|
262
168
|
#
|
@@ -288,24 +194,6 @@ module Papercraft
|
|
288
194
|
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
289
195
|
end
|
290
196
|
|
291
|
-
S_LT = '<'
|
292
|
-
S_GT = '>'
|
293
|
-
S_LT_SLASH = '</'
|
294
|
-
S_SPACE_LT_SLASH = ' </'
|
295
|
-
S_SLASH_GT = '/>'
|
296
|
-
S_SPACE = ' '
|
297
|
-
S_EQUAL_QUOTE = '="'
|
298
|
-
S_QUOTE = '"'
|
299
|
-
|
300
|
-
# Emits text into the rendering buffer, escaping any special characters to
|
301
|
-
# the respective HTML entities.
|
302
|
-
#
|
303
|
-
# @param data [String] text
|
304
|
-
# @return [void]
|
305
|
-
def text(data)
|
306
|
-
@buffer << escape_text(data)
|
307
|
-
end
|
308
|
-
|
309
197
|
private
|
310
198
|
|
311
199
|
# Escapes text. This method must be overriden in descendant classes.
|
@@ -322,29 +210,6 @@ module Papercraft
|
|
322
210
|
(@emit_yield_stack ||= []) << block
|
323
211
|
end
|
324
212
|
|
325
|
-
# Emits tag attributes into the rendering buffer
|
326
|
-
# @param props [Hash] tag attributes
|
327
|
-
# @return [void]
|
328
|
-
def emit_props(props)
|
329
|
-
props.each { |k, v|
|
330
|
-
case k
|
331
|
-
when :src, :href
|
332
|
-
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
333
|
-
EscapeUtils.escape_uri(v) << S_QUOTE
|
334
|
-
else
|
335
|
-
case v
|
336
|
-
when true
|
337
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-')
|
338
|
-
when false, nil
|
339
|
-
# emit nothing
|
340
|
-
else
|
341
|
-
@buffer << S_SPACE << k.to_s.tr('_', '-') <<
|
342
|
-
S_EQUAL_QUOTE << v << S_QUOTE
|
343
|
-
end
|
344
|
-
end
|
345
|
-
}
|
346
|
-
end
|
347
|
-
|
348
213
|
# Renders a deferred proc by evaluating it, then adding the rendered result
|
349
214
|
# to the buffer.
|
350
215
|
#
|
@@ -366,23 +231,11 @@ module Papercraft
|
|
366
231
|
# Implements an HTML renderer
|
367
232
|
class HTMLRenderer < Renderer
|
368
233
|
include HTML
|
369
|
-
|
370
|
-
private
|
371
|
-
|
372
|
-
# Escapes the given text using HTML entities.
|
373
|
-
def escape_text(text)
|
374
|
-
EscapeUtils.escape_html(text.to_s)
|
375
|
-
end
|
376
234
|
end
|
377
235
|
|
378
236
|
# Implements an XML renderer
|
379
237
|
class XMLRenderer < Renderer
|
380
|
-
|
381
|
-
|
382
|
-
# Escapes the given text using XML entities.
|
383
|
-
def escape_text(text)
|
384
|
-
EscapeUtils.escape_xml(text.to_s)
|
385
|
-
end
|
238
|
+
include XML
|
386
239
|
end
|
387
240
|
|
388
241
|
class JSONRenderer < Renderer
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Papercraft
|
4
|
+
# Markup (HTML/XML) extensions
|
5
|
+
module Tags
|
6
|
+
S_LT = '<'
|
7
|
+
S_GT = '>'
|
8
|
+
S_LT_SLASH = '</'
|
9
|
+
S_SPACE_LT_SLASH = ' </'
|
10
|
+
S_SLASH_GT = '/>'
|
11
|
+
S_SPACE = ' '
|
12
|
+
S_EQUAL_QUOTE = '="'
|
13
|
+
S_QUOTE = '"'
|
14
|
+
|
15
|
+
# The tag method template below is optimized for performance. Do not touch!
|
16
|
+
|
17
|
+
S_TAG_METHOD_LINE = __LINE__ + 2
|
18
|
+
S_TAG_METHOD = <<~EOF
|
19
|
+
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
20
|
+
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
21
|
+
|
22
|
+
def %<tag>s(text = nil, **props, &block)
|
23
|
+
if text.is_a?(Hash) && props.empty?
|
24
|
+
props = text
|
25
|
+
text = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
@buffer << S_TAG_%<TAG>s_PRE
|
29
|
+
emit_props(props) unless props.empty?
|
30
|
+
|
31
|
+
if block
|
32
|
+
@buffer << S_GT
|
33
|
+
instance_eval(&block)
|
34
|
+
@buffer << S_TAG_%<TAG>s_CLOSE
|
35
|
+
elsif Proc === text
|
36
|
+
@buffer << S_GT
|
37
|
+
emit(text)
|
38
|
+
@buffer << S_TAG_%<TAG>s_CLOSE
|
39
|
+
elsif text
|
40
|
+
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
41
|
+
else
|
42
|
+
@buffer << S_SLASH_GT
|
43
|
+
end
|
44
|
+
end
|
45
|
+
EOF
|
46
|
+
|
47
|
+
# Emits an XML tag with the given content, properties and optional block.
|
48
|
+
# This method is an alternative to emitting XML tags using dynamically
|
49
|
+
# created methods. This is particularly useful when using extensions that
|
50
|
+
# have method names that clash with XML tags, such as `button` or `a`, or
|
51
|
+
# when you need to override the behaviour of a particular XML tag.
|
52
|
+
#
|
53
|
+
# The following two method calls have the same effect:
|
54
|
+
#
|
55
|
+
# button 'text', id: 'button1'
|
56
|
+
# tag :button, 'text', id: 'button1'
|
57
|
+
#
|
58
|
+
# @param sym [Symbol, String] XML tag
|
59
|
+
# @param text [String, nil] tag content
|
60
|
+
# @param **props [Hash] tag attributes
|
61
|
+
# @param &block [Proc] optional inner XML
|
62
|
+
# @return [void]
|
63
|
+
def tag(sym, text = nil, **props, &block)
|
64
|
+
if text.is_a?(Hash) && props.empty?
|
65
|
+
props = text
|
66
|
+
text = nil
|
67
|
+
end
|
68
|
+
|
69
|
+
tag = tag_repr(sym)
|
70
|
+
|
71
|
+
@buffer << S_LT << tag
|
72
|
+
emit_props(props) unless props.empty?
|
73
|
+
|
74
|
+
if block
|
75
|
+
@buffer << S_GT
|
76
|
+
instance_eval(&block)
|
77
|
+
@buffer << S_LT_SLASH << tag << S_GT
|
78
|
+
elsif Proc === text
|
79
|
+
@buffer << S_GT
|
80
|
+
emit(text)
|
81
|
+
@buffer << S_LT_SLASH << tag << S_GT
|
82
|
+
elsif text
|
83
|
+
@buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
|
84
|
+
else
|
85
|
+
@buffer << S_SLASH_GT
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Catches undefined tag method call and handles it by defining the method.
|
90
|
+
#
|
91
|
+
# @param sym [Symbol] XML tag or component identifier
|
92
|
+
# @param args [Array] method arguments
|
93
|
+
# @param opts [Hash] named method arguments
|
94
|
+
# @param &block [Proc] block passed to method
|
95
|
+
# @return [void]
|
96
|
+
def method_missing(sym, *args, **opts, &block)
|
97
|
+
# p method_missing: sym, self: self
|
98
|
+
tag = sym.to_s
|
99
|
+
repr = tag_repr(tag)
|
100
|
+
code = S_TAG_METHOD % {
|
101
|
+
tag: tag,
|
102
|
+
TAG: tag.upcase,
|
103
|
+
tag_pre: "<#{repr}".inspect,
|
104
|
+
tag_close: "</#{repr}>".inspect
|
105
|
+
}
|
106
|
+
self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
|
107
|
+
send(sym, *args, **opts, &block)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Emits text into the rendering buffer, escaping any special characters to
|
111
|
+
# the respective XML entities.
|
112
|
+
#
|
113
|
+
# @param data [String] text
|
114
|
+
# @return [void]
|
115
|
+
def text(data)
|
116
|
+
@buffer << escape_text(data)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def tag_repr(tag)
|
122
|
+
tag.to_s.tr('_', '-')
|
123
|
+
end
|
124
|
+
|
125
|
+
def att_repr(att)
|
126
|
+
att.to_s.tr('_', '-')
|
127
|
+
end
|
128
|
+
|
129
|
+
# Emits tag attributes into the rendering buffer
|
130
|
+
# @param props [Hash] tag attributes
|
131
|
+
# @return [void]
|
132
|
+
def emit_props(props)
|
133
|
+
props.each { |k, v|
|
134
|
+
case k
|
135
|
+
when :src, :href
|
136
|
+
@buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
|
137
|
+
EscapeUtils.escape_uri(v) << S_QUOTE
|
138
|
+
else
|
139
|
+
case v
|
140
|
+
when true
|
141
|
+
@buffer << S_SPACE << att_repr(k)
|
142
|
+
when false, nil
|
143
|
+
# emit nothing
|
144
|
+
else
|
145
|
+
@buffer << S_SPACE << att_repr(k) <<
|
146
|
+
S_EQUAL_QUOTE << v << S_QUOTE
|
147
|
+
end
|
148
|
+
end
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/lib/papercraft/version.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'escape_utils'
|
4
|
+
require_relative './tags'
|
5
|
+
|
6
|
+
module Papercraft
|
7
|
+
# XML renderer extensions
|
8
|
+
module XML
|
9
|
+
include Tags
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def tag_repr(tag)
|
14
|
+
tag.to_s.gsub('__', ':').tr('_', '-')
|
15
|
+
end
|
16
|
+
|
17
|
+
def att_repr(att)
|
18
|
+
att.to_s.gsub('__', ':').tr('_', '-')
|
19
|
+
end
|
20
|
+
|
21
|
+
# Escapes the given text using XML entities.
|
22
|
+
def escape_text(text)
|
23
|
+
EscapeUtils.escape_xml(text.to_s)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
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: '0.
|
4
|
+
version: '0.20'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: escape_utils
|
@@ -139,8 +139,10 @@ files:
|
|
139
139
|
- lib/papercraft/html.rb
|
140
140
|
- lib/papercraft/json.rb
|
141
141
|
- lib/papercraft/renderer.rb
|
142
|
+
- lib/papercraft/tags.rb
|
142
143
|
- lib/papercraft/template.rb
|
143
144
|
- lib/papercraft/version.rb
|
145
|
+
- lib/papercraft/xml.rb
|
144
146
|
- papercraft.png
|
145
147
|
homepage: http://github.com/digital-fabric/papercraft
|
146
148
|
licenses:
|