papercraft 0.16 → 0.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 996d95088c85415ca883c27b6b1c98e65af5c046e3121ea08f092ac7bcdcc588
4
- data.tar.gz: 8fe294225c04dcfc82289fd2b9047760260b814712e83e9bf5ea7d361dad6950
3
+ metadata.gz: 2eaf6ba31b9300176af0bf55b76f7bbdc16508d0e495ed1d76f9f273ea88f250
4
+ data.tar.gz: 90e132928ebfe78e577e71c8dc498f0ce1b1f2c58f453fbedaad96121c448a48
5
5
  SHA512:
6
- metadata.gz: 54fdbe164a4a2541153e57b90cdb35ba71b6887fbe2d199505da3389730f1bd937fe0254f8c019b39d81957058c5f51ad71bc900f324e3178b979f368642ee57
7
- data.tar.gz: fa05ea9ae79a57a3eace2e708b01fe5f44412591875776cf1a2fe95cc6d9d1586357756e702e8a17e5458b3fa4c3e3224290bc234b71fdcd426db0dedff31dc0
6
+ metadata.gz: fcada325a3f8cd1c4cbe38674efaf6a86d0eb947ad5815d07778e2a09a54bee2c7d6b731e87d110390e9c87abc0a61f81cbc4cb30fdce11a4fc8a8cd322466a0
7
+ data.tar.gz: 031c86f7dade95af3dca979b4543fcb26e1fab2eb98c5642746d6efcf8b4bd56fe30b70f2f5aa8309352f421cae9f116a901f464216f54a0be50630dcc52815f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ ## 0.20 2022-02-13
2
+
3
+ - Add support for XML namespaced tags and attributes (#9)
4
+ - Move and refactor HTML/XML common code to Tags module
5
+
6
+ ## 0.19 2022-02-05
7
+
8
+ - Rename `Papercraft::Component` to `Papercraft::Template`
9
+
10
+ ## 0.18 2022-02-04
11
+
12
+ - Cleanup and update examples
13
+ - Fix behaviour of #emit with block
14
+ - Improve README
15
+
16
+ ## 0.17 2022-01-23
17
+
18
+ - Refactor markdown code, add `Papercraft.markdown` method (#8)
19
+
1
20
  ## 0.16 2022-01-23
2
21
 
3
22
  - Implement JSON templating (#7)
data/README.md CHANGED
@@ -24,35 +24,63 @@
24
24
 
25
25
  ## What is Papercraft?
26
26
 
27
+ Papercraft is a templating engine for dynamically producing HTML, XML or JSON.
28
+ Papercraft templates are expressed in plain Ruby, leading to easier debugging,
29
+ better protection against HTML/XML injection attacks, and better code reuse.
30
+
31
+ Papercraft templates can be composed in a variety of ways, facilitating the
32
+ usage of layout templates, and enabling a component-oriented approach to
33
+ building complex web interfaces.
34
+
35
+ In Papercraft, dynamic data is passed explicitly to the template as block
36
+ arguments, making the data flow easy to follow and understand. Papercraft also
37
+ lets developers create derivative templates using full or partial parameter
38
+ application.
39
+
40
+ Papercraft includes built-in support for rendering Markdown (using
41
+ [Kramdown](https://github.com/gettalong/kramdown/)), as well as support for
42
+ creating template extensions in order to allow the creation of component
43
+ libraries.
44
+
27
45
  ```ruby
28
46
  require 'papercraft'
29
47
 
30
48
  page = Papercraft.html { |*args|
31
49
  html {
32
- head { }
50
+ head { title 'Title' }
33
51
  body { emit_yield *args }
34
52
  }
35
53
  }
36
54
  page.render { p 'foo' }
37
- #=> "<html><head/><body><p>foo</p></body></html>"
55
+ #=> "<html><head><title>Title</title></head><body><p>foo</p></body></html>"
38
56
 
39
57
  hello = page.apply { |name| h1 "Hello, #{name}!" }
40
58
  hello.render('world')
41
- #=> "<html><head/><body><h1>Hello, world!</h1></body></html>"
42
- ```
43
-
44
- Papercraft is a templating engine for Ruby that offers the following features:
45
-
46
- - HTML, XML and JSON templating using plain Ruby syntax
47
- - Minimal boilerplate
48
- - Mix logic and tags freely
49
- - Automatic HTML and XML escaping
50
- - Composable components
51
- - Standard or custom MIME types
52
- - Explicit parameter passing to nested components
53
- - Higher order components
54
- - Built-in support for rendering [Markdown](#emitting-markdown)
55
- - Support for namespaced extensions
59
+ #=> "<html><head><title>Title</title></head><body><h1>Hello, world!</h1></body></html>"
60
+ ```
61
+
62
+ ## Table of content
63
+
64
+ - [Installing papercraft](#installing-papercraft)
65
+ - [Basic usage](#basic-usage)
66
+ - [Adding tags](#adding-tags)
67
+ - [Template parameters](#template-parameters)
68
+ - [Template logic](#template-logic)
69
+ - [Template blocks](#template-blocks)
70
+ - [Plain procs as templates](#plain-procs-as-templates)
71
+ - [Template composition](#template-composition)
72
+ - [Parameter and block application](#parameter-and-block-application)
73
+ - [Higher-order templates](#higher-order-templates)
74
+ - [Layout template composition](#layout-template-composition)
75
+ - [Emitting raw HTML](#emitting-raw-html)
76
+ - [Emitting a string with HTML Encoding](#emitting-a-string-with-html-encoding)
77
+ - [Emitting Markdown](#emitting-markdown)
78
+ - [Working with MIME types](#working-with-mime-types)
79
+ - [Deferred evaluation](#deferred-evaluation)
80
+ - [Papercraft extensions](#papercraft-extensions)
81
+ - [XML templates](#xml-templates)
82
+ - [JSON templates](#json-templates)
83
+ - [API Reference](#api-reference)
56
84
 
57
85
  ## Installing Papercraft
58
86
 
@@ -68,9 +96,9 @@ Or manually:
68
96
  $ gem install papercraft
69
97
  ```
70
98
 
71
- ## Getting started
99
+ ## Basic usage
72
100
 
73
- To create a template use the global method `Kernel#H`:
101
+ To create an HTML template use `Papercraft.html`:
74
102
 
75
103
  ```ruby
76
104
  require 'papercraft'
@@ -80,13 +108,16 @@ html = Papercraft.html {
80
108
  }
81
109
  ```
82
110
 
111
+ (You can also use `Papercraft.xml` and `Papercraft.json` to create XML and JSON
112
+ templates, respectively.)
113
+
83
114
  Rendering a template is done using `#render`:
84
115
 
85
116
  ```ruby
86
117
  html.render #=> "<div id="greeter"><p>Hello!</p></div>"
87
118
  ```
88
119
 
89
- ## All about tags
120
+ ## Adding tags
90
121
 
91
122
  Tags are added using unqualified method calls, and can be nested using blocks:
92
123
 
@@ -141,7 +172,7 @@ greeting = Papercraft.html { |name:| h1 "Hello, #{name}!" }
141
172
  greeting.render(name: 'world') #=> "<h1>Hello, world!</h1>"
142
173
  ```
143
174
 
144
- ## Logic in templates
175
+ ## Template logic
145
176
 
146
177
  Since Papercraft templates are just a bunch of Ruby, you can easily write your
147
178
  view logic right in the template:
@@ -171,10 +202,10 @@ page = Papercraft.html {
171
202
  page.render { h1 'hi' }
172
203
  ```
173
204
 
174
- ## Plain procs as components
205
+ ## Plain procs as templates
175
206
 
176
207
  With Papercraft you can write a template as a plain Ruby proc, and later render
177
- it by passing it as a block to `H`:
208
+ it by passing it as a block to `Papercraft.html`:
178
209
 
179
210
  ```ruby
180
211
  greeting = proc { |name| h1 "Hello, #{name}!" }
@@ -188,10 +219,10 @@ greeting = ->(name) { h1 "Hello, #{name}!" }
188
219
  Papercraft.html(&greeting).render('world')
189
220
  ```
190
221
 
191
- ## Component composition
222
+ ## Template composition
192
223
 
193
- Papercraft makes it easy to compose multiple components into a whole HTML
194
- document. A Papercraft component can contain other components, as the following
224
+ Papercraft makes it easy to compose multiple templates into a whole HTML
225
+ document. A Papercraft template can contain other templates, as the following
195
226
  example shows.
196
227
 
197
228
  ```ruby
@@ -219,14 +250,14 @@ page = Papercraft.html { |title, items|
219
250
  }
220
251
  }
221
252
 
222
- page.render('Hello from components', [
253
+ page.render('Hello from composed templates', [
223
254
  { id: 1, text: 'foo', checked: false },
224
255
  { id: 2, text: 'bar', checked: true }
225
256
  ])
226
257
  ```
227
258
 
228
- In addition to using components defined as constants, you can also use
229
- non-constant components by invoking the `#emit` method:
259
+ In addition to using templates defined as constants, you can also use
260
+ non-constant templates by invoking the `#emit` method:
230
261
 
231
262
  ```ruby
232
263
  greeting = -> { span "Hello, world" }
@@ -241,11 +272,11 @@ Papercraft.html {
241
272
  ## Parameter and block application
242
273
 
243
274
  Parameters and blocks can be applied to a template without it being rendered, by
244
- using `#apply`. This mechanism is what allows component composition and the
245
- creation of higher-order components.
275
+ using `#apply`. This mechanism is what allows template composition and the
276
+ creation of higher-order templates.
246
277
 
247
- The `#apply` method returns a new component which applies the given parameters and
248
- or block to the original component:
278
+ The `#apply` method returns a new template which applies the given parameters and
279
+ or block to the original template:
249
280
 
250
281
  ```ruby
251
282
  # parameter application
@@ -258,19 +289,19 @@ div_wrap = Papercraft.html { div { emit_yield } }
258
289
  wrapped_h1 = div_wrap.apply { h1 'hi' }
259
290
  wrapped_h1.render #=> "<div><h1>hi</h1></div>"
260
291
 
261
- # wrap a component
292
+ # wrap a template
262
293
  wrapped_hello_world = div_wrap.apply(&hello_world)
263
294
  wrapped_hello_world.render #=> "<div><h1>Hello, world!</h1></div>"
264
295
  ```
265
296
 
266
- ## Higher-order components
297
+ ## Higher-order templates
267
298
 
268
- Papercraft also lets you create higher-order components (HOCs), that is,
269
- components that take other components as parameters, or as blocks. Higher-order
270
- components are handy for creating layouts, wrapping components in arbitrary
271
- markup, enhancing components or injecting component parameters.
299
+ Papercraft also lets you create higher-order templates, that is,
300
+ templates that take other templates as parameters, or as blocks. Higher-order
301
+ templates are handy for creating layouts, wrapping templates in arbitrary
302
+ markup, enhancing templates or injecting template parameters.
272
303
 
273
- Here is a HOC that takes a component as parameter:
304
+ Here is a higher-order template that takes a template as parameter:
274
305
 
275
306
  ```ruby
276
307
  div_wrap = Papercraft.html { |inner| div { emit inner } }
@@ -279,7 +310,7 @@ wrapped_greeter = div_wrap.apply(greeter)
279
310
  wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
280
311
  ```
281
312
 
282
- The inner component can also be passed as a block, as shown above:
313
+ The inner template can also be passed as a block, as shown above:
283
314
 
284
315
  ```ruby
285
316
  div_wrap = Papercraft.html { div { emit_yield } }
@@ -289,7 +320,7 @@ wrapped_greeter.render #=> "<div><h1>hi</h1></div>"
289
320
 
290
321
  ## Layout template composition
291
322
 
292
- One of the principal uses of higher-order components is the creation of nested
323
+ One of the principal uses of higher-order templates is the creation of nested
293
324
  layouts. Suppose we have a website with a number of different layouts, and we'd
294
325
  like to avoid having to repeat the same code in the different layouts. We can do
295
326
  this by creating a `default` page template that takes a block, then use `#apply`
@@ -358,6 +389,14 @@ template = Papercraft.html { |md| div { emit_markdown md, auto_ids: false } }
358
389
  template.render("# title") #=> "<div><h1>title</h1></div>"
359
390
  ```
360
391
 
392
+ The `#emit_markdown` method is available only to HTML templates. If you need to
393
+ render markdown in XML or JSON templates (usually for implementing RSS or JSON
394
+ feeds), you can use `Papercraft.markdown` directly:
395
+
396
+ ```ruby
397
+ Papercraft.markdown('# title') #=> "<h1>title</h1>"
398
+ ```
399
+
361
400
  The default Kramdown options are:
362
401
 
363
402
  ```ruby
@@ -370,17 +409,33 @@ The default Kramdown options are:
370
409
  ```
371
410
 
372
411
  The deafult options can be configured by accessing
373
- `Papercraft::HTML.kramdown_options`, e.g.:
412
+ `Papercraft.default_kramdown_options`, e.g.:
413
+
414
+ ```ruby
415
+ Papercraft.default_kramdown_options[:auto_ids] = false
416
+ ```
417
+
418
+ ## Working with MIME types
419
+
420
+ Papercraft lets you set and interrogate a template's MIME type, in order to be
421
+ able to dynamically set the `Content-Type` HTTP response header. A template's
422
+ MIME type can be set when creating the template, e.g. `Papercraft.xml(mime_type:
423
+ 'application/rss+xml')`. You can interrogate the template's MIME type using
424
+ `#mime_type`:
374
425
 
375
426
  ```ruby
376
- Papercraft::HTML.kramdown_options[:auto_ids] = false
427
+ # using Qeweney (https://github.com/digital-fabric/qeweney)
428
+ def serve_template(req, template)
429
+ body = template.render
430
+ respond(body, 'Content-Type' => template.mime_type)
431
+ end
377
432
  ```
378
433
 
379
434
  ## Deferred evaluation
380
435
 
381
436
  Deferred evaluation allows deferring the rendering of parts of a template until
382
- the last moment, thus allowing an inner component to manipulate the state of the
383
- outer component. To in order to defer a part of a template, use `#defer`, and
437
+ the last moment, thus allowing an inner template to manipulate the state of the
438
+ outer template. To in order to defer a part of a template, use `#defer`, and
384
439
  include any markup in the provided block. This technique, in in conjunction with
385
440
  holding state in instance variables, is an alternative to passing parameters,
386
441
  which can be limiting in some situations.
@@ -389,11 +444,11 @@ A few use cases for deferred evaulation come to mind:
389
444
 
390
445
  - Setting the page title.
391
446
  - Adding a flash message to a page.
392
- - Using components that dynamically add static dependencies (JS and CSS) to the
447
+ - Using templates that dynamically add static dependencies (JS and CSS) to the
393
448
  page.
394
449
 
395
450
  The last use case is particularly interesting. Imagine a `DependencyMananger`
396
- class that can collect JS and CSS dependencies from the different components
451
+ class that can collect JS and CSS dependencies from the different templates
397
452
  integrated into the page, and adds them to the page's `<head>` element:
398
453
 
399
454
  ```ruby
@@ -494,11 +549,47 @@ Papercraft.html {
494
549
  }
495
550
  ```
496
551
 
497
- ## JSON templating
498
552
 
499
- You can create a JSON template using the same API used for HTML and XML
500
- templating. The only difference is that for adding array items you'll need to
501
- use the `#item` method:
553
+
554
+ ## XML templates
555
+
556
+ XML templates behave largely the same as HTML templates, with a few minor
557
+ differences. XML templates employ a different encoding algorithm, and lack some
558
+ specific HTML functionality, such as emitting Markdown.
559
+
560
+ Here's an example showing how to create an RSS feed:
561
+
562
+ ```ruby
563
+ rss = Papercraft.xml(mime_type: 'text/xml; charset=utf-8') { |resource:, **props|
564
+ rss(version: '2.0', 'xmlns:atom' => 'http://www.w3.org/2005/Atom') {
565
+ channel {
566
+ title 'Noteflakes'
567
+ link 'https://noteflakes.com/'
568
+ description 'A website by Sharon Rosner'
569
+ language 'en-us'
570
+ pubDate Time.now.httpdate
571
+ emit '<atom:link href="https://noteflakes.com/feeds/rss" rel="self" type="application/rss+xml" />'
572
+
573
+ article_entries = resource.page_list('/articles').reverse
574
+
575
+ article_entries.each { |e|
576
+ item {
577
+ title e[:title]
578
+ link "https://noteflakes.com#{e[:url]}"
579
+ guid "https://noteflakes.com#{e[:url]}"
580
+ pubDate e[:date].to_time.httpdate
581
+ description e[:html_content]
582
+ }
583
+ }
584
+ }
585
+ }
586
+ }
587
+ ```
588
+
589
+ ## JSON templates
590
+
591
+ JSON templates behave largely the same as HTML and XML templates. The only major
592
+ difference is that for adding array items you'll need to use the `#item` method:
502
593
 
503
594
  ```ruby
504
595
  Papercraft.json {
@@ -523,7 +614,8 @@ Papercraft.json {
523
614
  }.render #=> "{\"foo\":{\"bar\":[null,true,123.456]}}"
524
615
  ```
525
616
 
526
- Papercraft uses the [JSON gem](https://rubyapi.org/3.1/o/json) under the hood.
617
+ Papercraft uses the [JSON gem](https://rubyapi.org/3.1/o/json) under the hood in
618
+ order to generate actual JSON.
527
619
 
528
620
  ## API Reference
529
621
 
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kramdown'
4
- require 'rouge'
5
- require 'kramdown-parser-gfm'
3
+ require_relative './tags'
6
4
 
7
5
  module Papercraft
8
6
  # HTML Markup extensions
9
7
  module HTML
8
+ include Tags
9
+
10
10
  # Emits the p tag (overrides Object#p)
11
11
  #
12
12
  # @param text [String] text content of tag
@@ -83,41 +83,14 @@ module Papercraft
83
83
  # @param **opts [Hash] Kramdown options
84
84
  # @return [void]
85
85
  def emit_markdown(markdown, **opts)
86
- emit Kramdown::Document.new(markdown, **kramdown_options(opts)).to_html
87
- end
88
-
89
- class << self
90
- # Returns the default Kramdown options used for converting Markdown to
91
- # HTML.
92
- #
93
- # @return [Hash] Default Kramdown options
94
- def kramdown_options
95
- @kramdown_options ||= {
96
- entity_output: :numeric,
97
- syntax_highlighter: :rouge,
98
- input: 'GFM',
99
- hard_wrap: false
100
- }
101
- end
102
-
103
- # Sets the default Kramdown options used for converting Markdown to
104
- # HTML.
105
- #
106
- # @param opts [Hash] New deafult Kramdown options
107
- # @return [Hash] New default Kramdown options
108
- def kramdown_options=(opts)
109
- @kramdown_options = opts
110
- end
86
+ emit Papercraft.markdown(markdown, **opts)
111
87
  end
112
88
 
113
89
  private
114
90
 
115
- # Returns the default Kramdown options, merged with the given overrides.
116
- #
117
- # @param opts [Hash] Kramdown option overrides
118
- # @return [Hash] Merged Kramdown options
119
- def kramdown_options(opts)
120
- HTML.kramdown_options.merge(**opts)
91
+ # Escapes the given text using XML entities.
92
+ def escape_text(text)
93
+ EscapeUtils.escape_html(text.to_s)
121
94
  end
122
95
  end
123
96
  end
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './html'
4
+ require_relative './xml'
4
5
  require_relative './json'
5
6
  require_relative './extension_proxy'
6
7
 
7
8
  module Papercraft
8
9
 
9
- # A Renderer renders a Papercraft component into a string
10
+ # A Renderer renders a Papercraft template into a string
10
11
  class Renderer
11
-
12
+
12
13
  class << self
13
14
 
14
15
  # Verifies that the given template proc can be called with the given
@@ -41,9 +42,9 @@ module Papercraft
41
42
  #
42
43
  # Installs the given extensions, passed in the form of a Ruby hash mapping
43
44
  # methods to extension modules. The methods will be available to all
44
- # Papercraft components. Extension methods are executed in the context of
45
+ # Papercraft templates. Extension methods are executed in the context of
45
46
  # the the renderer instance, so they can look just like normal proc
46
- # components. In cases where method names in the module clash with HTML
47
+ # components. In cases where method names in the module clash with XML
47
48
  # tag names, you can use the `#tag` method to emit the relevant tag.
48
49
  #
49
50
  # module ComponentLibrary
@@ -111,99 +112,6 @@ module Papercraft
111
112
  @buffer
112
113
  end
113
114
 
114
- # The tag method template below is optimized for performance. Do not touch!
115
-
116
- S_TAG_METHOD_LINE = __LINE__ + 2
117
- S_TAG_METHOD = <<~EOF
118
- S_TAG_%<TAG>s_PRE = %<tag_pre>s
119
- S_TAG_%<TAG>s_CLOSE = %<tag_close>s
120
-
121
- def %<tag>s(text = nil, **props, &block)
122
- if text.is_a?(Hash) && props.empty?
123
- props = text
124
- text = nil
125
- end
126
-
127
- @buffer << S_TAG_%<TAG>s_PRE
128
- emit_props(props) unless props.empty?
129
-
130
- if block
131
- @buffer << S_GT
132
- instance_eval(&block)
133
- @buffer << S_TAG_%<TAG>s_CLOSE
134
- elsif Proc === text
135
- @buffer << S_GT
136
- emit(text)
137
- @buffer << S_TAG_%<TAG>s_CLOSE
138
- elsif text
139
- @buffer << S_GT << escape_text(text.to_s) << S_TAG_%<TAG>s_CLOSE
140
- else
141
- @buffer << S_SLASH_GT
142
- end
143
- end
144
- EOF
145
-
146
- # Emits an HTML tag with the given content, properties and optional block.
147
- # This method is an alternative to emitting HTML tags using dynamically
148
- # created methods. This is particularly useful when using extensions that
149
- # have method names that clash with HTML tags, such as `button` or `a`, or
150
- # when you need to override the behaviour of a particular HTML tag.
151
- #
152
- # The following two method calls have the same effect:
153
- #
154
- # button 'text', id: 'button1'
155
- # tag :button, 'text', id: 'button1'
156
- #
157
- # @param sym [Symbol, String] HTML tag
158
- # @param text [String, nil] tag content
159
- # @param **props [Hash] tag attributes
160
- # @param &block [Proc] optional inner HTML
161
- # @return [void]
162
- def tag(sym, text = nil, **props, &block)
163
- if text.is_a?(Hash) && props.empty?
164
- props = text
165
- text = nil
166
- end
167
-
168
- tag = sym.to_s.tr('_', '-')
169
-
170
- @buffer << S_LT << tag
171
- emit_props(props) unless props.empty?
172
-
173
- if block
174
- @buffer << S_GT
175
- instance_eval(&block)
176
- @buffer << S_LT_SLASH << tag << S_GT
177
- elsif Proc === text
178
- @buffer << S_GT
179
- emit(text)
180
- @buffer << S_LT_SLASH << tag << S_GT
181
- elsif text
182
- @buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
183
- else
184
- @buffer << S_SLASH_GT
185
- end
186
- end
187
-
188
- # Catches undefined tag method call and handles it by defining the method.
189
- #
190
- # @param sym [Symbol] HTML tag or component identifier
191
- # @param args [Array] method arguments
192
- # @param opts [Hash] named method arguments
193
- # @param &block [Proc] block passed to method
194
- # @return [void]
195
- def method_missing(sym, *args, **opts, &block)
196
- tag = sym.to_s
197
- code = S_TAG_METHOD % {
198
- tag: tag,
199
- TAG: tag.upcase,
200
- tag_pre: "<#{tag.tr('_', '-')}".inspect,
201
- tag_close: "</#{tag.tr('_', '-')}>".inspect
202
- }
203
- self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
204
- send(sym, *args, **opts, &block)
205
- end
206
-
207
115
  # Emits the given object into the rendering buffer. If the given object is a
208
116
  # proc or a component, `emit` will passes any additional arguments and named
209
117
  # arguments to the object when rendering it. If the given object is nil,
@@ -217,14 +125,15 @@ module Papercraft
217
125
  #
218
126
  # Papercraft.html { emit nil }.render #=> ""
219
127
  #
220
- # @param o [Proc, Papercraft::Component, String] emitted object
128
+ # @param o [Proc, Papercraft::Template, String] emitted object
221
129
  # @param *a [Array<any>] arguments to pass to a proc
222
130
  # @param **b [Hash] named arguments to pass to a proc
223
131
  # @return [void]
224
- def emit(o, *a, **b)
132
+ def emit(o, *a, **b, &block)
225
133
  case o
226
134
  when ::Proc
227
135
  Renderer.verify_proc_parameters(o, a, b)
136
+ push_emit_yield_block(block) if block
228
137
  instance_exec(*a, **b, &o)
229
138
  when nil
230
139
  else
@@ -250,10 +159,10 @@ module Papercraft
250
159
  end
251
160
 
252
161
  # Defers the given block to be evaluated later. Deferred evaluation allows
253
- # Papercraft components to inject state into sibling components, regardless
162
+ # Papercraft templates to inject state into sibling components, regardless
254
163
  # of the component's order in the container component. For example, a nested
255
164
  # component may set an instance variable used by another component. This is
256
- # an elegant solution to the problem of setting the HTML page's title, or
165
+ # an elegant solution to the problem of setting the XML page's title, or
257
166
  # adding elements to the `<head>` section. Here's how a title can be
258
167
  # controlled from a nested component:
259
168
  #
@@ -285,24 +194,6 @@ module Papercraft
285
194
  @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
286
195
  end
287
196
 
288
- S_LT = '<'
289
- S_GT = '>'
290
- S_LT_SLASH = '</'
291
- S_SPACE_LT_SLASH = ' </'
292
- S_SLASH_GT = '/>'
293
- S_SPACE = ' '
294
- S_EQUAL_QUOTE = '="'
295
- S_QUOTE = '"'
296
-
297
- # Emits text into the rendering buffer, escaping any special characters to
298
- # the respective HTML entities.
299
- #
300
- # @param data [String] text
301
- # @return [void]
302
- def text(data)
303
- @buffer << escape_text(data)
304
- end
305
-
306
197
  private
307
198
 
308
199
  # Escapes text. This method must be overriden in descendant classes.
@@ -319,29 +210,6 @@ module Papercraft
319
210
  (@emit_yield_stack ||= []) << block
320
211
  end
321
212
 
322
- # Emits tag attributes into the rendering buffer
323
- # @param props [Hash] tag attributes
324
- # @return [void]
325
- def emit_props(props)
326
- props.each { |k, v|
327
- case k
328
- when :src, :href
329
- @buffer << S_SPACE << k.to_s << S_EQUAL_QUOTE <<
330
- EscapeUtils.escape_uri(v) << S_QUOTE
331
- else
332
- case v
333
- when true
334
- @buffer << S_SPACE << k.to_s.tr('_', '-')
335
- when false, nil
336
- # emit nothing
337
- else
338
- @buffer << S_SPACE << k.to_s.tr('_', '-') <<
339
- S_EQUAL_QUOTE << v << S_QUOTE
340
- end
341
- end
342
- }
343
- end
344
-
345
213
  # Renders a deferred proc by evaluating it, then adding the rendered result
346
214
  # to the buffer.
347
215
  #
@@ -363,23 +231,11 @@ module Papercraft
363
231
  # Implements an HTML renderer
364
232
  class HTMLRenderer < Renderer
365
233
  include HTML
366
-
367
- private
368
-
369
- # Escapes the given text using HTML entities.
370
- def escape_text(text)
371
- EscapeUtils.escape_html(text.to_s)
372
- end
373
234
  end
374
235
 
375
236
  # Implements an XML renderer
376
237
  class XMLRenderer < Renderer
377
- private
378
-
379
- # Escapes the given text using XML entities.
380
- def escape_text(text)
381
- EscapeUtils.escape_xml(text.to_s)
382
- end
238
+ include XML
383
239
  end
384
240
 
385
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
@@ -4,30 +4,30 @@ require_relative './html'
4
4
 
5
5
  module Papercraft
6
6
 
7
- # Component represents a distinct, reusable HTML template. A component can
8
- # include other components, and also be nested inside other components.
7
+ # Template represents a distinct, reusable HTML template. A template can
8
+ # include other templates, and also be nested inside other templates.
9
9
  #
10
- # Since in Papercraft HTML is expressed using blocks (or procs,) the Component
10
+ # Since in Papercraft HTML is expressed using blocks (or procs,) the Template
11
11
  # class is simply a special kind of Proc, which has some enhanced
12
12
  # capabilities, allowing it to be easily composed in a variety of ways.
13
13
  #
14
- # Components are usually created using the class methods `html`, `xml` or
14
+ # Templates are usually created using the class methods `html`, `xml` or
15
15
  # `json`, for HTML, XML or JSON templates, respectively:
16
16
  #
17
17
  # greeter = Papercraft.html { |name| h1 "Hello, #{name}!" }
18
18
  # greeter.render('world') #=> "<h1>Hello, world!</h1>"
19
19
  #
20
- # Components can also be created using the normal constructor:
20
+ # Templates can also be created using the normal constructor:
21
21
  #
22
- # greeter = Papercraft::Component.new(mode: :html) { |name| h1 "Hello, #{name}!" }
22
+ # greeter = Papercraft::Template.new(mode: :html) { |name| h1 "Hello, #{name}!" }
23
23
  # greeter.render('world') #=> "<h1>Hello, world!</h1>"
24
24
  #
25
- # The different methods for creating components can also take a custom MIME
25
+ # The different methods for creating templates can also take a custom MIME
26
26
  # type, by passing a `mime_type` named argument:
27
27
  #
28
28
  # json = Papercraft.json(mime_type: 'application/feed+json') { ... }
29
29
  #
30
- # In the component block, HTML elements are created by simply calling
30
+ # In the template block, HTML elements are created by simply calling
31
31
  # unqualified methods:
32
32
  #
33
33
  # page_layout = Papercraft.html {
@@ -41,20 +41,20 @@ module Papercraft
41
41
  # }
42
42
  # }
43
43
  #
44
- # Papercraft components can take explicit parameters in order to render
44
+ # Papercraft templates can take explicit parameters in order to render
45
45
  # dynamic content. This can be in the form of regular or named parameters. The
46
46
  # `greeter` template shown above takes a single `name` parameter. Here's how a
47
- # anchor component could be implemented with named parameters:
47
+ # anchor template could be implemented with named parameters:
48
48
  #
49
49
  # anchor = Papercraft.html { |uri: , text: | a(text, href: uri) }
50
50
  #
51
- # The above component could later be rendered by passing the needed arguments:
51
+ # The above template could later be rendered by passing the needed arguments:
52
52
  #
53
53
  # anchor.render(uri: 'https://example.com', text: 'Example')
54
54
  #
55
- # ## Component Composition
55
+ # ## Template Composition
56
56
  #
57
- # A component can be included in another component using the `emit` method:
57
+ # A template can be included in another template using the `emit` method:
58
58
  #
59
59
  # links = Papercraft.html {
60
60
  # emit anchor, uri: '/posts', text: 'Posts'
@@ -62,7 +62,7 @@ module Papercraft
62
62
  # emit anchor, uri: '/about', text: 'About'
63
63
  # }
64
64
  #
65
- # Another way of composing components is to pass the components themselves as
65
+ # Another way of composing templates is to pass the templates themselves as
66
66
  # parameters:
67
67
  #
68
68
  # links = Papercraft.html { |anchors|
@@ -74,8 +74,8 @@ module Papercraft
74
74
  # anchor.apply(uri: '/about', text: 'About')
75
75
  # ])
76
76
  #
77
- # The `#apply` method creates a new component, applying the given parameters
78
- # such that the component can be rendered without parameters:
77
+ # The `#apply` method creates a new template, applying the given parameters
78
+ # such that the template can be rendered without parameters:
79
79
  #
80
80
  # links_with_anchors = links.apply([
81
81
  # anchor.apply(uri: '/posts', text: 'Posts'),
@@ -84,7 +84,7 @@ module Papercraft
84
84
  # ])
85
85
  # links_with_anchors.render
86
86
  #
87
- class Component < Proc
87
+ class Template < Proc
88
88
 
89
89
  # Determines the rendering mode: `:html` or `:xml`.
90
90
  attr_accessor :mode
@@ -95,12 +95,12 @@ module Papercraft
95
95
  json: 'application/json'
96
96
  }.freeze
97
97
 
98
- # Initializes a component with the given block. The rendering mode (HTML or
98
+ # Initializes a template with the given block. The rendering mode (HTML or
99
99
  # XML) can be passed in the `mode:` parameter. If `mode:` is not specified,
100
- # the component defaults to HTML.
100
+ # the template defaults to HTML.
101
101
  #
102
102
  # @param mode [:html, :xml] rendering mode
103
- # @param mime_type [String, nil] the component's mime type (nil for default)
103
+ # @param mime_type [String, nil] the template's mime type (nil for default)
104
104
  # @param block [Proc] nested HTML block
105
105
  def initialize(mode: :html, mime_type: nil, &block)
106
106
  @mode = mode
@@ -124,9 +124,9 @@ module Papercraft
124
124
  end.to_s
125
125
  end
126
126
 
127
- # Creates a new component, applying the given parameters and or block to the
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
- # components, particularly when passing inner components as blocks:
129
+ # templates, particularly when passing inner templates as blocks:
130
130
  #
131
131
  # article_wrapper = Papercraft.html {
132
132
  # article {
@@ -141,19 +141,19 @@ module Papercraft
141
141
  # @param *a [<any>] normal parameters
142
142
  # @param **b [Hash] named parameters
143
143
  # @param &block [Proc] inner block
144
- # @return [Papercraft::Component] applied component
144
+ # @return [Papercraft::Template] applied template
145
145
  def apply(*a, **b, &block)
146
146
  template = self
147
- Component.new(mode: @mode, mime_type: @mime_type, &proc do |*x, **y|
147
+ Template.new(mode: @mode, mime_type: @mime_type, &proc do |*x, **y|
148
148
  push_emit_yield_block(block) if block
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
- # the component's mode.
154
+ # the template's mode.
155
155
  #
156
- # @return [Papercraft::Renderer] Renderer used for rendering the component
156
+ # @return [Papercraft::Renderer] Renderer used for rendering the template
157
157
  def renderer_class
158
158
  case @mode
159
159
  when :html
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Papercraft
4
- VERSION = '0.16'
4
+ VERSION = '0.20'
5
5
  end
@@ -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
data/lib/papercraft.rb CHANGED
@@ -1,73 +1,110 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'escape_utils'
3
+ require 'kramdown'
4
+ require 'rouge'
5
+ require 'kramdown-parser-gfm'
4
6
 
5
- require_relative 'papercraft/component'
7
+ require_relative 'papercraft/template'
6
8
  require_relative 'papercraft/renderer'
7
9
  require_relative 'papercraft/encoding'
8
10
  # require_relative 'papercraft/compiler'
9
11
 
10
- # Papercraft is a component-based HTML templating library
12
+
13
+ # Papercraft is a composable templating library
11
14
  module Papercraft
12
15
  # Exception class used to signal templating-related errors
13
16
  class Error < RuntimeError; end
17
+
18
+ class << self
19
+
20
+ # Installs one or more extensions. Extensions enhance templating capabilities
21
+ # by adding namespaced methods to emplates. An extension is implemented as a
22
+ # Ruby module containing one or more methods. Each method in the extension
23
+ # module can be used to render a specific HTML element or a set of elements.
24
+ #
25
+ # This is a convenience method. For more information on using Papercraft
26
+ # extensions, see `Papercraft::Renderer::extension`
27
+ #
28
+ # @param map [Hash] hash mapping methods to extension modules
29
+ # @return [void]
30
+ def extension(map)
31
+ Renderer.extension(map)
32
+ end
33
+
34
+ # Creates a new papercraft template. `Papercraft.html` can take either a proc
35
+ # argument or a block. In both cases, the proc is converted to a
36
+ # `Papercraft::Template`.
37
+ #
38
+ # Papercraft.html(proc { h1 'hi' }).render #=> "<h1>hi</h1>"
39
+ # Papercraft.html { h1 'hi' }.render #=> "<h1>hi</h1>"
40
+ #
41
+ # @param template [Proc] template block
42
+ # @return [Papercraft::Template] Papercraft template
43
+ def html(o = nil, mime_type: nil, &template)
44
+ return o if o.is_a?(Papercraft::Template)
45
+ template ||= o
46
+ Papercraft::Template.new(mode: :html, mime_type: mime_type, &template)
47
+ end
48
+
49
+ # Creates a new Papercraft template in XML mode. `Papercraft.xml` can take
50
+ # either a proc argument or a block. In both cases, the proc is converted to a
51
+ # `Papercraft::Template`.
52
+ #
53
+ # Papercraft.xml(proc { item 'foo' }).render #=> "<item>foo</item>"
54
+ # Papercraft.xml { item 'foo' }.render #=> "<item>foo</item>"
55
+ #
56
+ # @param template [Proc] template block
57
+ # @return [Papercraft::Template] Papercraft template
58
+ def xml(o = nil, mime_type: nil, &template)
59
+ return o if o.is_a?(Papercraft::Template)
60
+ template ||= o
61
+ Papercraft::Template.new(mode: :xml, mime_type: mime_type, &template)
62
+ end
63
+
64
+ # Creates a new Papercraft template in JSON mode. `Papercraft.json` can take
65
+ # either a proc argument or a block. In both cases, the proc is converted to a
66
+ # `Papercraft::Template`.
67
+ #
68
+ # Papercraft.json(proc { item 42 }).render #=> "[42]"
69
+ # Papercraft.json { foo 'bar' }.render #=> "{\"foo\": \"bar\"}"
70
+ #
71
+ # @param template [Proc] template block
72
+ # @return [Papercraft::Template] Papercraft template
73
+ def json(o = nil, mime_type: nil, &template)
74
+ return o if o.is_a?(Papercraft::Template)
75
+ template ||= o
76
+ Papercraft::Template.new(mode: :json, mime_type: mime_type, &template)
77
+ end
14
78
 
15
- # Installs one or more extensions. Extensions enhance templating capabilities
16
- # by adding namespaced methods to emplates. An extension is implemented as a
17
- # Ruby module containing one or more methods. Each method in the extension
18
- # module can be used to render a specific HTML element or a set of elements.
19
- #
20
- # This is a convenience method. For more information on using Papercraft
21
- # extensions, see `Papercraft::Renderer::extension`
22
- #
23
- # @param map [Hash] hash mapping methods to extension modules
24
- # @return [void]
25
- def self.extension(map)
26
- Renderer.extension(map)
27
- end
28
-
29
- # Creates a new papercraft component. `Papercraft.html` can take either a proc
30
- # argument or a block. In both cases, the proc is converted to a
31
- # `Papercraft::Component`.
32
- #
33
- # Papercraft.html(proc { h1 'hi' }).render #=> "<h1>hi</h1>"
34
- # Papercraft.html { h1 'hi' }.render #=> "<h1>hi</h1>"
35
- #
36
- # @param template [Proc] template block
37
- # @return [Papercraft::Component] Papercraft component
38
- def self.html(o = nil, mime_type: nil, &template)
39
- return o if o.is_a?(Papercraft::Component)
40
- template ||= o
41
- Papercraft::Component.new(mode: :html, mime_type: mime_type, &template)
42
- end
43
-
44
- # Creates a new papercraft component in XML mode. `Papercraft.xml` can take
45
- # either a proc argument or a block. In both cases, the proc is converted to a
46
- # `Papercraft::Component`.
47
- #
48
- # Papercraft.xml(proc { item 'foo' }).render #=> "<item>foo</item>"
49
- # Papercraft.xml { item 'foo' }.render #=> "<item>foo</item>"
50
- #
51
- # @param template [Proc] template block
52
- # @return [Papercraft::Component] Papercraft component
53
- def self.xml(o = nil, mime_type: nil, &template)
54
- return o if o.is_a?(Papercraft::Component)
55
- template ||= o
56
- Papercraft::Component.new(mode: :xml, mime_type: mime_type, &template)
57
- end
79
+ # Renders Markdown into HTML. The `opts` argument will be merged with the
80
+ # default Kramdown options in order to change the rendering behaviour.
81
+ #
82
+ # @param markdown [String] Markdown
83
+ # @param **opts [Hash] Kramdown option overrides
84
+ # @return [String] HTML
85
+ def markdown(markdown, **opts)
86
+ opts = default_kramdown_options.merge(opts)
87
+ Kramdown::Document.new(markdown, **opts).to_html
88
+ end
89
+
90
+ # Returns the default Kramdown options used for rendering Markdown.
91
+ #
92
+ # @return [Hash] Kramdown options
93
+ def default_kramdown_options
94
+ @default_kramdown_options ||= {
95
+ entity_output: :numeric,
96
+ syntax_highlighter: :rouge,
97
+ input: 'GFM',
98
+ hard_wrap: false
99
+ }
100
+ end
58
101
 
59
- # Creates a new papercraft component in JSON mode. `Papercraft.json` can take
60
- # either a proc argument or a block. In both cases, the proc is converted to a
61
- # `Papercraft::Component`.
62
- #
63
- # Papercraft.json(proc { item 42 }).render #=> "[42]"
64
- # Papercraft.json { foo 'bar' }.render #=> "{\"foo\": \"bar\"}"
65
- #
66
- # @param template [Proc] template block
67
- # @return [Papercraft::Component] Papercraft component
68
- def self.json(o = nil, mime_type: nil, &template)
69
- return o if o.is_a?(Papercraft::Component)
70
- template ||= o
71
- Papercraft::Component.new(mode: :json, mime_type: mime_type, &template)
102
+ # Sets the default Kramdown options used for rendering Markdown.
103
+ #
104
+ # @param opts [Hash] Kramdown options
105
+ # @return [void]
106
+ def default_kramdown_options=(opts)
107
+ @default_kramdown_options = opts
108
+ end
72
109
  end
73
110
  end
data/papercraft.png ADDED
Binary file
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.16'
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-01-23 00:00:00.000000000 Z
11
+ date: 2022-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -128,18 +128,22 @@ executables: []
128
128
  extensions: []
129
129
  extra_rdoc_files:
130
130
  - README.md
131
+ - papercraft.png
131
132
  files:
132
133
  - CHANGELOG.md
133
134
  - README.md
134
135
  - lib/papercraft.rb
135
136
  - lib/papercraft/compiler.rb
136
- - lib/papercraft/component.rb
137
137
  - lib/papercraft/encoding.rb
138
138
  - lib/papercraft/extension_proxy.rb
139
139
  - lib/papercraft/html.rb
140
140
  - lib/papercraft/json.rb
141
141
  - lib/papercraft/renderer.rb
142
+ - lib/papercraft/tags.rb
143
+ - lib/papercraft/template.rb
142
144
  - lib/papercraft/version.rb
145
+ - lib/papercraft/xml.rb
146
+ - papercraft.png
143
147
  homepage: http://github.com/digital-fabric/papercraft
144
148
  licenses:
145
149
  - MIT