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/lib/papercraft/tags.rb
DELETED
@@ -1,408 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative './extension_proxy'
|
4
|
-
require 'escape_utils'
|
5
|
-
|
6
|
-
module Papercraft
|
7
|
-
# Markup (HTML/XML) extensions
|
8
|
-
module Tags
|
9
|
-
S_LT = '<'
|
10
|
-
S_GT = '>'
|
11
|
-
S_LT_SLASH = '</'
|
12
|
-
S_SPACE_LT_SLASH = ' </'
|
13
|
-
S_SLASH_GT = '/>'
|
14
|
-
S_GT_LT_SLASH = '></'
|
15
|
-
S_SPACE = ' '
|
16
|
-
S_EQUAL_QUOTE = '="'
|
17
|
-
S_QUOTE = '"'
|
18
|
-
|
19
|
-
# The tag method template below is optimized for performance. Do not touch!
|
20
|
-
|
21
|
-
S_TAG_METHOD_LINE = __LINE__ + 2
|
22
|
-
S_TAG_METHOD = <<~EOF
|
23
|
-
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
24
|
-
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
25
|
-
|
26
|
-
def %<tag>s(text = nil, _for: nil, **attributes, &block)
|
27
|
-
return if @render_fragment && @fragment != @render_fragment
|
28
|
-
|
29
|
-
return _for.each { |*a| %<tag>s(text, **attributes) { block.(*a)} } if _for
|
30
|
-
|
31
|
-
if text.is_a?(Hash) && attributes.empty?
|
32
|
-
attributes = text
|
33
|
-
text = nil
|
34
|
-
end
|
35
|
-
|
36
|
-
@buffer << S_TAG_%<TAG>s_PRE
|
37
|
-
emit_attributes(attributes) unless attributes.empty?
|
38
|
-
|
39
|
-
if block
|
40
|
-
@buffer << S_GT
|
41
|
-
instance_eval(&block)
|
42
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
43
|
-
elsif Proc === text
|
44
|
-
@buffer << S_GT
|
45
|
-
emit(text)
|
46
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
47
|
-
elsif text
|
48
|
-
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
49
|
-
else
|
50
|
-
@buffer << S_GT << S_TAG_%<TAG>s_CLOSE
|
51
|
-
end
|
52
|
-
end
|
53
|
-
EOF
|
54
|
-
|
55
|
-
S_VOID_TAG_METHOD_LINE = __LINE__ + 2
|
56
|
-
S_VOID_TAG_METHOD = <<~EOF
|
57
|
-
S_TAG_%<TAG>s_PRE = %<tag_pre>s
|
58
|
-
S_TAG_%<TAG>s_CLOSE = %<tag_close>s
|
59
|
-
|
60
|
-
def %<tag>s(text = nil, _for: nil, **attributes, &block)
|
61
|
-
return if @render_fragment && @fragment != @render_fragment
|
62
|
-
|
63
|
-
return _for.each { |*a| %<tag>s(text, **attributes) { block.(*a)} } if _for
|
64
|
-
|
65
|
-
if text.is_a?(Hash) && attributes.empty?
|
66
|
-
attributes = text
|
67
|
-
text = nil
|
68
|
-
end
|
69
|
-
|
70
|
-
@buffer << S_TAG_%<TAG>s_PRE
|
71
|
-
emit_attributes(attributes) unless attributes.empty?
|
72
|
-
|
73
|
-
if block
|
74
|
-
@buffer << S_GT
|
75
|
-
instance_eval(&block)
|
76
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
77
|
-
elsif Proc === text
|
78
|
-
@buffer << S_GT
|
79
|
-
emit(text)
|
80
|
-
@buffer << S_TAG_%<TAG>s_CLOSE
|
81
|
-
elsif text
|
82
|
-
@buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
|
83
|
-
else
|
84
|
-
@buffer << S_SLASH_GT
|
85
|
-
end
|
86
|
-
end
|
87
|
-
EOF
|
88
|
-
|
89
|
-
INITIAL_BUFFER_CAPACITY = 8192
|
90
|
-
|
91
|
-
# Initializes a tag renderer.
|
92
|
-
def initialize(render_fragment = nil, &template)
|
93
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
94
|
-
super(render_fragment)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Returns the rendered template.
|
98
|
-
#
|
99
|
-
# @return [String]
|
100
|
-
def to_s
|
101
|
-
if @parts
|
102
|
-
last = @buffer
|
103
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
104
|
-
parts = @parts
|
105
|
-
@parts = nil
|
106
|
-
parts.each do |p|
|
107
|
-
if Proc === p
|
108
|
-
render_deferred_proc(&p)
|
109
|
-
else
|
110
|
-
@buffer << p
|
111
|
-
end
|
112
|
-
end
|
113
|
-
@buffer << last unless last.empty?
|
114
|
-
end
|
115
|
-
@buffer
|
116
|
-
end
|
117
|
-
|
118
|
-
# Defers the given block to be evaluated later. Deferred evaluation allows
|
119
|
-
# Papercraft templates to inject state into sibling components, regardless
|
120
|
-
# of the component's order in the container component. For example, a nested
|
121
|
-
# component may set an instance variable used by another component. This is
|
122
|
-
# an elegant solution to the problem of setting the XML page's title, or
|
123
|
-
# adding elements to the `<head>` section. Here's how a title can be
|
124
|
-
# controlled from a nested component:
|
125
|
-
#
|
126
|
-
# layout = Papercraft.html {
|
127
|
-
# html {
|
128
|
-
# head {
|
129
|
-
# defer { title @title }
|
130
|
-
# }
|
131
|
-
# body {
|
132
|
-
# emit_yield
|
133
|
-
# }
|
134
|
-
# }
|
135
|
-
# }
|
136
|
-
#
|
137
|
-
# html.render {
|
138
|
-
# @title = 'My super page'
|
139
|
-
# h1 'content'
|
140
|
-
# }
|
141
|
-
#
|
142
|
-
# @return [void]
|
143
|
-
def defer(&block)
|
144
|
-
if !@parts
|
145
|
-
@parts = [@buffer, block]
|
146
|
-
else
|
147
|
-
@parts << @buffer unless @buffer.empty?
|
148
|
-
@parts << block
|
149
|
-
end
|
150
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
151
|
-
end
|
152
|
-
|
153
|
-
|
154
|
-
# Emits an XML tag with the given content, properties and optional block.
|
155
|
-
# This method is an alternative to emitting XML tags using dynamically
|
156
|
-
# created methods. This is particularly useful when using extensions that
|
157
|
-
# have method names that clash with XML tags, such as `button` or `a`, or
|
158
|
-
# when you need to override the behaviour of a particular XML tag.
|
159
|
-
#
|
160
|
-
# The following two method calls have the same effect:
|
161
|
-
#
|
162
|
-
# button 'text', id: 'button1'
|
163
|
-
# tag :button, 'text', id: 'button1'
|
164
|
-
#
|
165
|
-
# @param sym [Symbol, String] XML tag
|
166
|
-
# @param text [String, nil] tag content
|
167
|
-
# @param **attributes [Hash] tag attributes
|
168
|
-
# @return [void]
|
169
|
-
def tag(sym, text = nil, _for: nil, **attributes, &block)
|
170
|
-
return if @render_fragment && @fragment != @render_fragment
|
171
|
-
|
172
|
-
return _for.each { |*a| tag(sym, text, **attributes) { block.(*a)} } if _for
|
173
|
-
|
174
|
-
if text.is_a?(Hash) && attributes.empty?
|
175
|
-
attributes = text
|
176
|
-
text = nil
|
177
|
-
end
|
178
|
-
|
179
|
-
tag = tag_repr(sym)
|
180
|
-
|
181
|
-
@buffer << S_LT << tag
|
182
|
-
emit_attributes(attributes) unless attributes.empty?
|
183
|
-
|
184
|
-
if block
|
185
|
-
@buffer << S_GT
|
186
|
-
instance_eval(&block)
|
187
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
188
|
-
elsif Proc === text
|
189
|
-
@buffer << S_GT
|
190
|
-
emit(text)
|
191
|
-
@buffer << S_LT_SLASH << tag << S_GT
|
192
|
-
elsif text
|
193
|
-
@buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
|
194
|
-
elsif is_void_element_tag?(sym)
|
195
|
-
@buffer << S_SLASH_GT
|
196
|
-
else
|
197
|
-
@buffer << S_GT_LT_SLASH << tag << S_GT
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
# Catches undefined tag method call and handles it by defining the method.
|
202
|
-
#
|
203
|
-
# @param sym [Symbol] tag or component identifier
|
204
|
-
# @param args [Array] method arguments
|
205
|
-
# @param opts [Hash] named method arguments
|
206
|
-
# @return [void]
|
207
|
-
def method_missing(sym, *args, **opts, &block)
|
208
|
-
tag = sym.to_s
|
209
|
-
if tag =~ /^[A-Z]/ && (Object.const_defined?(tag))
|
210
|
-
define_const_tag_method(tag)
|
211
|
-
else
|
212
|
-
define_tag_method(tag)
|
213
|
-
end
|
214
|
-
|
215
|
-
send(sym, *args, **opts, &block)
|
216
|
-
end
|
217
|
-
|
218
|
-
# Emits text into the rendering buffer, escaping any special characters to
|
219
|
-
# the respective XML entities.
|
220
|
-
#
|
221
|
-
# @param data [String, nil] text
|
222
|
-
# @return [void]
|
223
|
-
def text(data = nil)
|
224
|
-
return if !data
|
225
|
-
return if @render_fragment && @fragment != @render_fragment
|
226
|
-
|
227
|
-
@buffer << escape_text(data)
|
228
|
-
end
|
229
|
-
|
230
|
-
# Defines a custom tag. This is handy for defining helper or extension
|
231
|
-
# methods inside the template body.
|
232
|
-
#
|
233
|
-
# Papercraft.html {
|
234
|
-
# def_tag(:section) { |title, &inner|
|
235
|
-
# div {
|
236
|
-
# h1 title
|
237
|
-
# emit inner
|
238
|
-
# }
|
239
|
-
# }
|
240
|
-
#
|
241
|
-
# section('Foo') {
|
242
|
-
# p 'Bar'
|
243
|
-
# }
|
244
|
-
# }
|
245
|
-
#
|
246
|
-
# @param tag [Symbol, String] tag/method name
|
247
|
-
# @param block [Proc] method body
|
248
|
-
# @return [void]
|
249
|
-
def def_tag(tag, &block)
|
250
|
-
self.class.define_method(tag, &block)
|
251
|
-
end
|
252
|
-
|
253
|
-
alias_method :orig_extend, :extend
|
254
|
-
|
255
|
-
# Extends the template with the provided module or map of modules. When
|
256
|
-
# given a module, the template body will be extended with the module,
|
257
|
-
# and will have access to all the module's methods:
|
258
|
-
#
|
259
|
-
# module CustomTags
|
260
|
-
# def label(text)
|
261
|
-
# span text, class: 'label'
|
262
|
-
# end
|
263
|
-
# end
|
264
|
-
#
|
265
|
-
# Papercraft.html {
|
266
|
-
# extend CustomTags
|
267
|
-
# label('foo')
|
268
|
-
# }
|
269
|
-
#
|
270
|
-
# When given a hash, each module in the hash is namespaced, and can be
|
271
|
-
# accessed using its key:
|
272
|
-
#
|
273
|
-
# Papercraft.html {
|
274
|
-
# extend custom: CustomTags
|
275
|
-
# custom.label('foo')
|
276
|
-
# }
|
277
|
-
#
|
278
|
-
# @param ext [Module, Hash] extension module or hash mapping symbols to modules
|
279
|
-
# @return [Object] self
|
280
|
-
def extend(ext)
|
281
|
-
if ext.is_a?(Module)
|
282
|
-
orig_extend(ext)
|
283
|
-
else
|
284
|
-
ext.each do |sym, mod|
|
285
|
-
define_extension_method(sym, mod)
|
286
|
-
end
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
private
|
291
|
-
|
292
|
-
# Defines a method that emits the given tag based on a constant. The
|
293
|
-
# constant must be defined on the main (Object) binding.
|
294
|
-
#
|
295
|
-
# @param tag [Symbol, String] tag/method name
|
296
|
-
# @return [void]
|
297
|
-
def define_const_tag_method(tag)
|
298
|
-
const = Object.const_get(tag)
|
299
|
-
self.class.define_method(tag) { |*a, **b, &blk|
|
300
|
-
emit const, *a, **b, &blk
|
301
|
-
}
|
302
|
-
end
|
303
|
-
|
304
|
-
# Defines a normal tag method.
|
305
|
-
#
|
306
|
-
# @param tag [Symbol, String] tag/method name
|
307
|
-
# @return [void]
|
308
|
-
def define_tag_method(tag)
|
309
|
-
repr = tag_repr(tag)
|
310
|
-
if is_void_element_tag?(tag)
|
311
|
-
tmpl = S_VOID_TAG_METHOD
|
312
|
-
line = S_VOID_TAG_METHOD_LINE
|
313
|
-
else
|
314
|
-
tmpl = S_TAG_METHOD
|
315
|
-
line = S_TAG_METHOD_LINE
|
316
|
-
end
|
317
|
-
code = tmpl % {
|
318
|
-
tag: tag,
|
319
|
-
TAG: tag.upcase,
|
320
|
-
tag_pre: "<#{repr}".inspect,
|
321
|
-
tag_close: "</#{repr}>".inspect
|
322
|
-
}
|
323
|
-
self.class.class_eval(code, __FILE__, line)
|
324
|
-
end
|
325
|
-
|
326
|
-
# Defines a namespace referring to the given module.
|
327
|
-
#
|
328
|
-
# @param sym [Symbol] namespace
|
329
|
-
# @param mod [Module] module
|
330
|
-
# @return [void]
|
331
|
-
def define_extension_method(sym, mod)
|
332
|
-
self.singleton_class.define_method(sym) do
|
333
|
-
(@extension_proxies ||= {})[mod] ||= ExtensionProxy.new(self, mod)
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
# Emits an arbitrary object by converting it to string, then adding it to
|
338
|
-
# the internal buffer. This method is called internally by `Renderer#emit`.
|
339
|
-
#
|
340
|
-
# @param obj [Object] emitted object
|
341
|
-
# @return [void]
|
342
|
-
def emit_object(obj)
|
343
|
-
return if @render_fragment && @fragment != @render_fragment
|
344
|
-
|
345
|
-
@buffer << obj.to_s
|
346
|
-
end
|
347
|
-
|
348
|
-
# Renders a deferred proc by evaluating it, then adding the rendered result
|
349
|
-
# to the buffer.
|
350
|
-
#
|
351
|
-
# @return [void]
|
352
|
-
def render_deferred_proc(&block)
|
353
|
-
old_buffer = @buffer
|
354
|
-
|
355
|
-
@buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
|
356
|
-
@parts = nil
|
357
|
-
|
358
|
-
instance_eval(&block)
|
359
|
-
|
360
|
-
old_buffer << to_s
|
361
|
-
@buffer = old_buffer
|
362
|
-
end
|
363
|
-
|
364
|
-
# Escapes text. This method must be overriden in Renderers which include
|
365
|
-
# this module.
|
366
|
-
#
|
367
|
-
# @param text [String] text to be escaped
|
368
|
-
def escape_text(text)
|
369
|
-
raise NotImplementedError
|
370
|
-
end
|
371
|
-
|
372
|
-
# Converts a tag to its string representation. This method must be overriden
|
373
|
-
# in Renderers which include this module.
|
374
|
-
#
|
375
|
-
# @param tag [Symbol, String] tag
|
376
|
-
def tag_repr(tag)
|
377
|
-
raise NotImplementedError
|
378
|
-
end
|
379
|
-
|
380
|
-
# Converts an attribute to its string representation. This method must be
|
381
|
-
# overriden in Renderers which include this module.
|
382
|
-
#
|
383
|
-
# @param att [Symbol, String] attribute
|
384
|
-
def att_repr(att)
|
385
|
-
raise NotImplementedError
|
386
|
-
end
|
387
|
-
|
388
|
-
# Emits tag attributes into the rendering buffer.
|
389
|
-
#
|
390
|
-
# @param attributes [Hash] tag attributes
|
391
|
-
# @return [void]
|
392
|
-
def emit_attributes(attributes)
|
393
|
-
attributes.each do |k, v|
|
394
|
-
case v
|
395
|
-
when true
|
396
|
-
@buffer << S_SPACE << att_repr(k)
|
397
|
-
when false, nil
|
398
|
-
# emit nothing
|
399
|
-
else
|
400
|
-
v = v.join(S_SPACE) if v.is_a?(Array)
|
401
|
-
@buffer << S_SPACE << att_repr(k) <<
|
402
|
-
S_EQUAL_QUOTE << escape_text(v) << S_QUOTE
|
403
|
-
end
|
404
|
-
end
|
405
|
-
end
|
406
|
-
|
407
|
-
end
|
408
|
-
end
|
data/lib/papercraft/xml.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative './tags'
|
4
|
-
require 'escape_utils'
|
5
|
-
|
6
|
-
module Papercraft
|
7
|
-
# XML renderer extensions
|
8
|
-
module XML
|
9
|
-
include Tags
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
# Returns false (no void elements in XML)
|
14
|
-
#
|
15
|
-
# @param tag [String] tag
|
16
|
-
# @return [false] false
|
17
|
-
def is_void_element_tag?(tag)
|
18
|
-
false
|
19
|
-
end
|
20
|
-
|
21
|
-
# Converts a tag to its string representation. Underscores will be converted
|
22
|
-
# to dashes, double underscores will be converted to colon.
|
23
|
-
#
|
24
|
-
# @param tag [Symbol, String] tag
|
25
|
-
# @return [String] tag string
|
26
|
-
def tag_repr(tag)
|
27
|
-
tag.to_s.gsub('__', ':').tr('_', '-')
|
28
|
-
end
|
29
|
-
|
30
|
-
# Converts an attribute to its string representation. Underscores will be
|
31
|
-
# converted to dashes, double underscores will be converted to colon.
|
32
|
-
#
|
33
|
-
# @param att [Symbol, String] attribute
|
34
|
-
# @return [String] attribute string
|
35
|
-
def att_repr(att)
|
36
|
-
att.to_s.gsub('__', ':').tr('_', '-')
|
37
|
-
end
|
38
|
-
|
39
|
-
# Escapes the given text using XML entities.
|
40
|
-
#
|
41
|
-
# @param text [String] text
|
42
|
-
# @return [String] escaped text
|
43
|
-
def escape_text(text)
|
44
|
-
EscapeUtils.escape_xml(text.to_s)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
data/lib/tilt/papercraft.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'papercraft'
|
4
|
-
require 'tilt'
|
5
|
-
|
6
|
-
# Tilt.
|
7
|
-
module Tilt
|
8
|
-
# Papercraft templating engine for Tilt
|
9
|
-
class PapercraftTemplate < Template
|
10
|
-
metadata[:mime_type] = 'text/html'
|
11
|
-
|
12
|
-
protected
|
13
|
-
|
14
|
-
def prepare
|
15
|
-
inner = eval("proc { |scope:, locals:, block:|\n#{data}\n}")
|
16
|
-
@template = Papercraft.html(&inner)
|
17
|
-
end
|
18
|
-
|
19
|
-
def evaluate(scope, locals, &block)
|
20
|
-
@template.render(scope: scope, locals: locals, block: block)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
register(PapercraftTemplate, 'papercraft')
|
25
|
-
end
|