papercraft 0.9 → 0.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1f7aa5872a78830c1a5662f055639ae2012b5b9d0f029640dce041f58d1ef4e
4
- data.tar.gz: 0c5acc8b14254c63db2132caab42ce003a8ae4713ae6daea7225d0390a420219
3
+ metadata.gz: be072094c35a6f89327b9c88b90264ad3db61cb43311d0e3baca865f403fdbc5
4
+ data.tar.gz: 7e565933dd83c48c05bac8c648cbe9b0007bc43f8fc5bd862af46dcd00dfb328
5
5
  SHA512:
6
- metadata.gz: 02bf1f7bc1300f4fa7871a6942c6a2eae2dd2f8202c7f3cc354171806f82e8a68262c3ca5e2232b7b6dcf6788a33b134b5811641561fa5baba3b7c342643b082
7
- data.tar.gz: 97a68a0f854f631a6a36b52a350faae4b1da1e337b27701209c6eec9dc16c62856555fdff3ab3d8f33e144e7fed8dc892b22433f38dd3f951a04010f710b63c4
6
+ metadata.gz: 9b8da36fb3c7298e8121b9c612cb81752609e1e3d1f01a94e4bf939864b29de718884989203077c5c1392926a54205c921adbcaa1bf3aa75d84694b7b4db4dcc
7
+ data.tar.gz: 778163311cb86ee425c4617ff54b10e8853d9b92404ef29abac848a4252818386b2e23058c355678c286a5bae3db9d4a7bfb182acb0a4aacf00a042431cc196a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,21 @@
1
+ ## 0.12 2022-01-06
2
+
3
+ - Improve documentation
4
+ - Add `Renderer#tag` method
5
+ - Add `HTML#style`, `HTML#script` methods
6
+
7
+ ## 0.11 2022-01-04
8
+
9
+ - Add deferred evaluation
10
+
11
+ ## 0.10.1 2021-12-25
12
+
13
+ - Fix tag rendering with empty text in Ruby 3.0
14
+
15
+ ## 0.10 2021-12-25
16
+
17
+ - Add support for extensions
18
+
1
19
  ## 0.9 2021-12-23
2
20
 
3
21
  - Add support for emitting Markdown
data/README.md CHANGED
@@ -1,8 +1,24 @@
1
- # Papercraft - Composable HTML templating for Ruby
2
-
3
- [INSTALL](#installing-papercraft) |
4
- [TUTORIAL](#getting-started) |
5
- [DOCS](https://www.rubydoc.info/gems/papercraft)
1
+ <h1 align="center">
2
+ Papercraft
3
+ </h1>
4
+
5
+ <h4 align="center">Composable HTML templating for Ruby</h4>
6
+
7
+ <p align="center">
8
+ <a href="http://rubygems.org/gems/papercraft">
9
+ <img src="https://badge.fury.io/rb/papercraft.svg" alt="Ruby gem">
10
+ </a>
11
+ <a href="https://github.com/digital-fabric/papercraft/actions?query=workflow%3ATests">
12
+ <img src="https://github.com/digital-fabric/papercraft/workflows/Tests/badge.svg" alt="Tests">
13
+ </a>
14
+ <a href="https://github.com/digital-fabric/papercraft/blob/master/LICENSE">
15
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
16
+ </a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="https://www.rubydoc.info/gems/papercraft">API reference</a>
21
+ </p>
6
22
 
7
23
  ## What is Papercraft?
8
24
 
@@ -32,9 +48,7 @@ features:
32
48
  - Explicit parameter passing to nested components
33
49
  - Higher order components
34
50
  - Built-in support for rendering [Markdown](#emitting-markdown)
35
-
36
- With Papercraft you can structure your templates as nested HTML components, in a
37
- somewhat similar fashion to React.
51
+ - Support for namespaced extensions
38
52
 
39
53
  ## Installing Papercraft
40
54
 
@@ -52,43 +66,25 @@ $ gem install papercraft
52
66
 
53
67
  ## Getting started
54
68
 
55
- To use Papercraft in your code just require it:
69
+ To create a template use the global method `Kernel#H`:
56
70
 
57
71
  ```ruby
58
72
  require 'papercraft'
59
- ```
60
-
61
- To create a template use `Papercraft::Component.new` or the global method
62
- `Kernel#H`:
63
73
 
64
- ```ruby
65
- # can also use Papercraft.new
66
74
  html = H {
67
- div { p 'hello' }
75
+ div(id: 'greeter') { p 'Hello!' }
68
76
  }
69
77
  ```
70
78
 
71
- ## Rendering a template
72
-
73
- To render a Papercraft template use the `#render` method:
74
-
75
- ```ruby
76
- H { span 'best span' }.render #=> "<span>best span</span>"
77
- ```
78
-
79
- The render method accepts an arbitrary context variable:
79
+ Rendering a template is done using `#render`:
80
80
 
81
81
  ```ruby
82
- html = H {
83
- h1 context[:title]
84
- }
85
-
86
- html.render(title: 'My title') #=> "<h1>My title</h1>"
82
+ html.render #=> "<div id="greeter"><p>Hello!</p></div>"
87
83
  ```
88
84
 
89
85
  ## All about tags
90
86
 
91
- Tags are added using unqualified method calls, and are nested using blocks:
87
+ Tags are added using unqualified method calls, and can be nested using blocks:
92
88
 
93
89
  ```ruby
94
90
  H {
@@ -370,13 +366,131 @@ The default Kramdown options are:
370
366
  ```
371
367
 
372
368
  The deafult options can be configured by accessing
373
- `Papercraft::HTML.kramdown_options`:
369
+ `Papercraft::HTML.kramdown_options`, e.g.:
374
370
 
375
371
  ```ruby
376
372
  Papercraft::HTML.kramdown_options[:auto_ids] = false
377
373
  ```
378
374
 
379
- ## Documentation
375
+ ## Deferred evaluation
376
+
377
+ Deferred evaluation allows deferring the rendering of parts of a template until
378
+ the last moment, thus allowing an inner component to manipulate the state of the
379
+ outer component. To in order to defer a part of a template, use `#defer`, and
380
+ include any markup in the provided block. This technique, in in conjunction with
381
+ holding state in instance variables, is an alternative to passing parameters,
382
+ which can be limiting in some situations.
383
+
384
+ A few use cases for deferred evaulation come to mind:
385
+
386
+ - Setting the page title.
387
+ - Adding a flash message to a page.
388
+ - Using components that dynamically add static dependencies (JS and CSS) to the
389
+ page.
390
+
391
+ The last use case is particularly interesting. Imagine a `DependencyMananger`
392
+ class that can collect JS and CSS dependencies from the different components
393
+ integrated into the page, and adds them to the page's `<head>` element:
394
+
395
+ ```ruby
396
+ default_layout = H { |**args|
397
+ @dependencies = DependencyMananger.new
398
+ head {
399
+ defer { emit @dependencies.head_markup }
400
+ }
401
+ body { emit_yield **args }
402
+ }
403
+
404
+ button = proc { |text, onclick|
405
+ @dependencies.js '/static/js/button.js'
406
+ @dependencies.css '/static/css/button.css'
407
+
408
+ button text, onclick: onclick
409
+ }
410
+
411
+ heading = proc { |text|
412
+ @dependencies.js '/static/js/heading.js'
413
+ @dependencies.css '/static/css/heading.css'
414
+
415
+ h1 text
416
+ }
417
+
418
+ page = default_layout.apply {
419
+ emit heading, "What's your favorite cheese?"
420
+
421
+ emit button, 'Beaufort', 'eat_beaufort()'
422
+ emit button, 'Mont d''or', 'eat_montdor()'
423
+ emit button, 'Époisses', 'eat_epoisses()'
424
+ }
425
+ ```
426
+
427
+ ## Papercraft extensions
428
+
429
+ Papercraft extensions are modules that contain one or more methods that can be
430
+ used to render complex HTML components. Extension modules can be used by
431
+ installing them as a namespaced extension using `Papercraft::extension`.
432
+ Extensions are particularly useful when you work with CSS frameworks such as
433
+ [Bootstrap](https://getbootstrap.com/), [Tailwind](https://tailwindui.com/) or
434
+ [Primer](https://primer.style/).
435
+
436
+ For example, to create a Bootstrap card component, the following HTML markup is
437
+ needed (example taken from the [Bootstrap
438
+ docs](https://getbootstrap.com/docs/5.1/components/card/#titles-text-and-links)):
439
+
440
+ ```html
441
+ <div class="card" style="width: 18rem;">
442
+ <div class="card-body">
443
+ <h5 class="card-title">Card title</h5>
444
+ <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
445
+ <p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
446
+ <a href="#" class="card-link">Card link</a>
447
+ <a href="#" class="card-link">Another link</a>
448
+ </div>
449
+ </div>
450
+ ```
451
+
452
+ With Papercraft, we could create a `Bootstrap` extension with a `#card` method
453
+ and other associated methods:
454
+
455
+ ```ruby
456
+ module BootstrapComponents
457
+ ...
458
+
459
+ def card(**props)
460
+ div(class: 'card', **props) {
461
+ div(class: 'card-body') {
462
+ emit_yield
463
+ }
464
+ }
465
+ end
466
+
467
+ def card_title(title)
468
+ h5 title, class: 'card-title'
469
+ end
470
+
471
+ ...
472
+ end
473
+
474
+ Papercraft.extension(bootstrap: BootstrapComponents)
475
+ ```
476
+
477
+ The call to `Papercraft::extension` lets us access the different methods of
478
+ `BootstrapComponents` by calling `#bootstrap` inside a template. With this,
479
+ we'll be able to express the above markup as follows:
480
+
481
+ ```ruby
482
+ H {
483
+ bootstrap.card(style: 'width: 18rem') {
484
+ bootstrap.card_title 'Card title'
485
+ bootstrap.card_subtitle 'Card subtitle'
486
+ bootstrap.card_text 'Some quick example text to build on the card title and make up the bulk of the card''s content.'
487
+ bootstrap.card_link '#', 'Card link'
488
+ bootstrap.card_link '#', 'Another link'
489
+ }
490
+ }
491
+ ```
492
+
493
+ ## API Reference
380
494
 
381
- The complete documentation for this library can be found
495
+ The API reference for this library can be found
382
496
  [here](https://www.rubydoc.info/gems/papercraft).
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Papercraft
4
+
5
+ # An ExtensionProxy proxies method calls to a renderer. Extension proxies are
6
+ # used to provide an namespaced interface to Papercraft extensions. When an
7
+ # extension is installed using `Papercraft.extension`, a corresponding method
8
+ # is defined on the `Papercraft::Renderer` class that creates an extension
9
+ # proxy that gives access to the different extension methods.
10
+ class ExtensionProxy
11
+
12
+ # Initializes a new ExtensionProxy.
13
+ # @param renderer [Papercraft::Renderer] renderer to proxy to
14
+ # @param mod [Module] extension module
15
+ # @return [void]
16
+ def initialize(renderer, mod)
17
+ @renderer = renderer
18
+ extend(mod)
19
+ end
20
+
21
+ # Proxies missing methods to the renderer
22
+ # @param sym [Symbol] method name
23
+ # @param *args [Array] arguments
24
+ # @param &block [Proc] block
25
+ # @return void
26
+ def method_missing(sym, *args, &block)
27
+ @renderer.send(sym, *args, &block)
28
+ end
29
+ end
30
+ end
@@ -44,15 +44,53 @@ module Papercraft
44
44
  link(**attributes)
45
45
  end
46
46
 
47
- def emit_markdown(markdown, **opts)
48
- emit Kramdown::Document.new(markdown, **kramdown_options(opts)).to_html
47
+ # Emits an inline CSS style element.
48
+ #
49
+ # @param css [String] CSS code
50
+ # @param **props [Hash] optional element attributes
51
+ # @return [void]
52
+ def style(css, **props, &block)
53
+ @buffer << '<style'
54
+ emit_props(props) unless props.empty?
55
+
56
+ @buffer << '>' << css << '</style>'
49
57
  end
58
+
59
+ # Emits an inline JS script element.
60
+ #
61
+ # @param js [String, nil] Javascript code
62
+ # @param **props [Hash] optional element attributes
63
+ # @return [void]
64
+ def script(js = nil, **props, &block)
65
+ @buffer << '<script'
66
+ emit_props(props) unless props.empty?
50
67
 
51
- def kramdown_options(opts)
52
- HTML.kramdown_options.merge(**opts)
68
+ if js
69
+ @buffer << '>' << js << '</script>'
70
+ else
71
+ @buffer << '></script>'
72
+ end
73
+ end
74
+
75
+ # Converts and emits the given markdown. Papercraft uses
76
+ # [Kramdown](https://github.com/gettalong/kramdown/) to do the Markdown to
77
+ # HTML conversion. Optional Kramdown settings can be provided in order to
78
+ # control the conversion. Those are merged with the default Kramdown
79
+ # settings, which can be controlled using
80
+ # `Papercraft::HTML.kramdown_options`.
81
+ #
82
+ # @param markdown [String] Markdown content
83
+ # @param **opts [Hash] Kramdown options
84
+ # @return [void]
85
+ def emit_markdown(markdown, **opts)
86
+ emit Kramdown::Document.new(markdown, **kramdown_options(opts)).to_html
53
87
  end
54
88
 
55
89
  class << self
90
+ # Returns the default Kramdown options used for converting Markdown to
91
+ # HTML.
92
+ #
93
+ # @return [Hash] Default Kramdown options
56
94
  def kramdown_options
57
95
  @kramdown_options ||= {
58
96
  entity_output: :numeric,
@@ -62,9 +100,24 @@ module Papercraft
62
100
  }
63
101
  end
64
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
65
108
  def kramdown_options=(opts)
66
109
  @kramdown_options = opts
67
110
  end
68
111
  end
112
+
113
+ private
114
+
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)
121
+ end
69
122
  end
70
123
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './html'
4
+ require_relative './extension_proxy'
4
5
 
5
6
  module Papercraft
6
7
 
@@ -31,15 +32,60 @@ module Papercraft
31
32
  if param_count > args.size
32
33
  raise Papercraft::Error, "Missing template parameters"
33
34
  end
34
- end
35
+ end
36
+
37
+ # call_seq:
38
+ # Papercraft::Renderer.extension(name => mod, ...)
39
+ # Papercraft.extension(name => mod, ...)
40
+ #
41
+ # Installs the given extensions, passed in the form of a Ruby hash mapping
42
+ # methods to extension modules. The methods will be available to all
43
+ # Papercraft components. Extension methods are executed in the context of
44
+ # the the renderer instance, so they can look just like normal proc
45
+ # components. In cases where method names in the module clash with HTML
46
+ # tag names, you can use the `#tag` method to emit the relevant tag.
47
+ #
48
+ # module ComponentLibrary
49
+ # def card(title, content)
50
+ # div(class: 'card') {
51
+ # h3 title
52
+ # div(class: 'card-content') { emit_markdown content }
53
+ # }
54
+ # end
55
+ # end
56
+ #
57
+ # Papercraft.extension(components: ComponentLibrary)
58
+ # H { components.card('Foo', '**Bar**') }
59
+ #
60
+ # @param map [Hash] hash mapping methods to extension modules
61
+ # @return [void]
62
+ def extension(map)
63
+ map.each do |sym, mod|
64
+ define_extension_method(sym, mod)
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ # Defines a method returning an extension proxy for the given module
71
+ # @param sym [Symbol] method name
72
+ # @param mod [Module] extension module
73
+ # @return [void]
74
+ def define_extension_method(sym, mod)
75
+ define_method(sym) do
76
+ (@extension_proxies ||= {})[mod] ||= ExtensionProxy.new(self, mod)
77
+ end
78
+ end
35
79
  end
36
80
 
81
+ INITIAL_BUFFER_CAPACITY = 8192
82
+
37
83
  # Initializes the renderer and evaulates the given template in the
38
84
  # renderer's scope.
39
85
  #
40
86
  # @param &template [Proc] template block
41
87
  def initialize(&template)
42
- @buffer = +''
88
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
43
89
  instance_eval(&template)
44
90
  end
45
91
 
@@ -47,15 +93,36 @@ module Papercraft
47
93
  #
48
94
  # @return [String]
49
95
  def to_s
96
+ if @parts
97
+ last = @buffer
98
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
99
+ parts = @parts
100
+ @parts = nil
101
+ parts.each do |p|
102
+ if Proc === p
103
+ render_deferred_proc(&p)
104
+ else
105
+ @buffer << p
106
+ end
107
+ end
108
+ @buffer << last unless last.empty?
109
+ end
50
110
  @buffer
51
111
  end
52
112
 
113
+ # The tag method template below is optimized for performance. Do not touch!
114
+
53
115
  S_TAG_METHOD_LINE = __LINE__ + 1
54
116
  S_TAG_METHOD = <<~EOF
55
- S_TAG_%<TAG>s_PRE = '<%<tag>s'.tr('_', '-')
56
- S_TAG_%<TAG>s_CLOSE = '</%<tag>s>'.tr('_', '-')
117
+ S_TAG_%<TAG>s_PRE = %<tag_pre>s
118
+ S_TAG_%<TAG>s_CLOSE = %<tag_close>s
57
119
 
58
120
  def %<tag>s(text = nil, **props, &block)
121
+ if text.is_a?(Hash) && props.empty?
122
+ props = text
123
+ text = nil
124
+ end
125
+
59
126
  @buffer << S_TAG_%<TAG>s_PRE
60
127
  emit_props(props) unless props.empty?
61
128
 
@@ -75,6 +142,48 @@ module Papercraft
75
142
  end
76
143
  EOF
77
144
 
145
+ # Emits an HTML tag with the given content, properties and optional block.
146
+ # This method is an alternative to emitting HTML tags using dynamically
147
+ # created methods. This is particularly useful when using extensions that
148
+ # have method names that clash with HTML tags, such as `button` or `a`, or
149
+ # when you need to override the behaviour of a particular HTML tag.
150
+ #
151
+ # The following two method calls have the same effect:
152
+ #
153
+ # button 'text', id: 'button1'
154
+ # tag :button, 'text', id: 'button1'
155
+ #
156
+ # @param sym [Symbol, String] HTML tag
157
+ # @param text [String, nil] tag content
158
+ # @param **props [Hash] tag attributes
159
+ # @param &block [Proc] optional inner HTML
160
+ # @return [void]
161
+ def tag(sym, text = nil, **props, &block)
162
+ if text.is_a?(Hash) && props.empty?
163
+ props = text
164
+ text = nil
165
+ end
166
+
167
+ tag = sym.to_s.tr('_', '-')
168
+
169
+ @buffer << S_LT << tag
170
+ emit_props(props) unless props.empty?
171
+
172
+ if block
173
+ @buffer << S_GT
174
+ instance_eval(&block)
175
+ @buffer << S_LT_SLASH << tag << S_GT
176
+ elsif Proc === text
177
+ @buffer << S_GT
178
+ emit(text)
179
+ @buffer << S_LT_SLASH << tag << S_GT
180
+ elsif text
181
+ @buffer << S_GT << escape_text(text.to_s) << S_LT_SLASH << tag << S_GT
182
+ else
183
+ @buffer << S_SLASH_GT
184
+ end
185
+ end
186
+
78
187
  # Catches undefined tag method call and handles it by defining the method.
79
188
  #
80
189
  # @param sym [Symbol] HTML tag or component identifier
@@ -83,11 +192,13 @@ module Papercraft
83
192
  # @param &block [Proc] block passed to method
84
193
  # @return [void]
85
194
  def method_missing(sym, *args, **opts, &block)
86
- value = @local && @local[sym]
87
- return value if value
88
-
89
195
  tag = sym.to_s
90
- code = S_TAG_METHOD % { tag: tag, TAG: tag.upcase }
196
+ code = S_TAG_METHOD % {
197
+ tag: tag,
198
+ TAG: tag.upcase,
199
+ tag_pre: "<#{tag.tr('_', '-')}".inspect,
200
+ tag_close: "</#{tag.tr('_', '-')}>".inspect
201
+ }
91
202
  self.class.class_eval(code, __FILE__, S_TAG_METHOD_LINE)
92
203
  send(sym, *args, **opts, &block)
93
204
  end
@@ -135,6 +246,42 @@ module Papercraft
135
246
 
136
247
  instance_exec(*a, **b, &@inner_block)
137
248
  end
249
+
250
+ # Defers the given block to be evaluated later. Deferred evaluation allows
251
+ # Papercraft components to inject state into sibling components, regardless
252
+ # of the component's order in the container component. For example, a nested
253
+ # component may set an instance variable used by another component. This is
254
+ # an elegant solution to the problem of setting the HTML page's title, or
255
+ # adding elements to the `<head>` section. Here's how a title can be
256
+ # controlled from a nested component:
257
+ #
258
+ # layout = H {
259
+ # html {
260
+ # head {
261
+ # defer { title @title }
262
+ # }
263
+ # body {
264
+ # emit_yield
265
+ # }
266
+ # }
267
+ # }
268
+ #
269
+ # html.render {
270
+ # @title = 'My super page'
271
+ # h1 'content'
272
+ # }
273
+ #
274
+ # @param &block [Proc] Deferred block to be emitted
275
+ # @return [void]
276
+ def defer(&block)
277
+ if !@parts
278
+ @parts = [@buffer, block]
279
+ else
280
+ @parts << @buffer unless @buffer.empty?
281
+ @parts << block
282
+ end
283
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
284
+ end
138
285
 
139
286
  S_LT = '<'
140
287
  S_GT = '>'
@@ -192,6 +339,23 @@ module Papercraft
192
339
  end
193
340
  }
194
341
  end
342
+
343
+ # Renders a deferred proc by evaluating it, then adding the rendered result
344
+ # to the buffer.
345
+ #
346
+ # @param &block [Proc] deferred proc
347
+ # @return [void]
348
+ def render_deferred_proc(&block)
349
+ old_buffer = @buffer
350
+
351
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
352
+ @parts = nil
353
+
354
+ instance_eval(&block)
355
+
356
+ old_buffer << to_s
357
+ @buffer = old_buffer
358
+ end
195
359
  end
196
360
 
197
361
  # Implements an HTML renderer
@@ -200,6 +364,7 @@ module Papercraft
200
364
 
201
365
  private
202
366
 
367
+ # Escapes the given text using HTML entities.
203
368
  def escape_text(text)
204
369
  EscapeUtils.escape_html(text.to_s)
205
370
  end
@@ -209,6 +374,7 @@ module Papercraft
209
374
  class XMLRenderer < Renderer
210
375
  private
211
376
 
377
+ # Escapes the given text using XML entities.
212
378
  def escape_text(text)
213
379
  EscapeUtils.escape_xml(text.to_s)
214
380
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Papercraft
4
- VERSION = '0.9'
4
+ VERSION = '0.12'
5
5
  end
data/lib/papercraft.rb CHANGED
@@ -11,6 +11,20 @@ require_relative 'papercraft/encoding'
11
11
  module Papercraft
12
12
  # Exception class used to signal templating-related errors
13
13
  class Error < RuntimeError; end
14
+
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
14
28
  end
15
29
 
16
30
  # Kernel extensions
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.9'
4
+ version: '0.12'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-23 00:00:00.000000000 Z
11
+ date: 2022-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -122,7 +122,7 @@ dependencies:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 2.0.9
125
- description:
125
+ description:
126
126
  email: sharon@noteflakes.com
127
127
  executables: []
128
128
  extensions: []
@@ -135,6 +135,7 @@ files:
135
135
  - lib/papercraft/compiler.rb
136
136
  - lib/papercraft/component.rb
137
137
  - lib/papercraft/encoding.rb
138
+ - lib/papercraft/extension_proxy.rb
138
139
  - lib/papercraft/html.rb
139
140
  - lib/papercraft/renderer.rb
140
141
  - lib/papercraft/version.rb
@@ -146,7 +147,7 @@ metadata:
146
147
  documentation_uri: https://www.rubydoc.info/gems/papercraft
147
148
  homepage_uri: https://github.com/digital-fabric/papercraft
148
149
  changelog_uri: https://github.com/digital-fabric/papercraft/blob/master/CHANGELOG.md
149
- post_install_message:
150
+ post_install_message:
150
151
  rdoc_options:
151
152
  - "--title"
152
153
  - Papercraft
@@ -165,8 +166,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
166
  - !ruby/object:Gem::Version
166
167
  version: '0'
167
168
  requirements: []
168
- rubygems_version: 3.1.6
169
- signing_key:
169
+ rubygems_version: 3.3.3
170
+ signing_key:
170
171
  specification_version: 4
171
172
  summary: 'Papercraft: component-based HTML templating for Ruby'
172
173
  test_files: []