papercraft 0.10.1 → 0.11

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: 757137ea8d3a2b5d5d329b7e841bad13cc26ea3486ebcb216f1ef016d7d3ff4a
4
- data.tar.gz: 281ee3b2911b1f4e1ff3337dbefef407ce07b17e21acb949dce158a93d8758a5
3
+ metadata.gz: 3452d115d85742018d1497b369ea27970f0c144e3dcb1772f4d81e33315dce51
4
+ data.tar.gz: e7217a9d9bcfd7af0dc92482650bc03392b0406cf4f3d6141c253a2918638545
5
5
  SHA512:
6
- metadata.gz: 22225498fce014a17962541bac3c17f20243239b709e191e75fa2f1abbc0f7f7fdef3b0c4d110753f0de1c2ca4efc19f516cb797a02cbafa70af95085449096e
7
- data.tar.gz: 57bb7aae102900de14ba9d8f4a0e2be83978e5106ff098ffda5acf39431273fd76be646d012da72094715ad8a3da7fd81a485e51151c74df94091a91e72866f4
6
+ metadata.gz: 8f76b9e73aa48f91af932af3308d3dd43239c3d5ca39e305cb1535417b5997483aa884f0e34ed128800b07a3135ebc1d18ea81d0df64e6170726a66c86969288
7
+ data.tar.gz: dbe744ca25996c3d263ff609ca26f42076784fdbceabd2dd1604dd1016e2d840830230f4205a673b9e5ba14eb8e480627612971b71ca6631e0a6e473d59ccb73
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.11 2022-01-04
2
+
3
+ - Add deferred evaluation
4
+
1
5
  ## 0.10.1 2021-12-25
2
6
 
3
7
  - Fix tag rendering with empty text in Ruby 3.0
data/README.md CHANGED
@@ -374,6 +374,58 @@ The deafult options can be configured by accessing
374
374
  Papercraft::HTML.kramdown_options[:auto_ids] = false
375
375
  ```
376
376
 
377
+ ## Deferred evaluation
378
+
379
+ Deferred evaluation allows deferring the rendering of parts of a template until
380
+ the last moment, thus allowing an inner component to manipulate the state of the
381
+ outer component. To in order to defer a part of a template, use `#defer`, and
382
+ include any markup in the provided block. This technique, in in conjunction with
383
+ holding state in instance variables, is an alternative to passing parameters,
384
+ which can be limiting in some situations.
385
+
386
+ A few use cases for deferred evaulation come to mind:
387
+
388
+ - Setting the page title.
389
+ - Adding a flash message to a page.
390
+ - Using components that dynamically add static dependencies (JS and CSS) to the
391
+ page.
392
+
393
+ The last use case is particularly interesting. Imagine a `DependencyMananger`
394
+ class that can collect JS and CSS dependencies from the different components
395
+ integrated into the page, and adds them to the page's `<head>` element:
396
+
397
+ ```ruby
398
+ default_layout = H { |**args|
399
+ @dependencies = DependencyMananger.new
400
+ head {
401
+ defer { emit @dependencies.head_markup }
402
+ }
403
+ body { emit_yield **args }
404
+ }
405
+
406
+ button = proc { |text, onclick|
407
+ @dependencies.js '/static/js/button.js'
408
+ @dependencies.css '/static/css/button.css'
409
+
410
+ button text, onclick: onclick
411
+ }
412
+
413
+ heading = proc { |text|
414
+ @dependencies.js '/static/js/heading.js'
415
+ @dependencies.css '/static/css/heading.css'
416
+
417
+ h1 text
418
+ }
419
+
420
+ page = default_layout.apply {
421
+ emit heading, "What's your favorite cheese?"
422
+
423
+ emit button, 'Beaufort', 'eat_beaufort()'
424
+ emit button, 'Mont d''or', 'eat_montdor()'
425
+ emit button, 'Époisses', 'eat_epoisses()'
426
+ }
427
+ ```
428
+
377
429
  ## Papercraft extensions
378
430
 
379
431
  Papercraft extensions are modules that contain one or more methods that can be
@@ -57,12 +57,14 @@ module Papercraft
57
57
  end
58
58
  end
59
59
 
60
+ INITIAL_BUFFER_CAPACITY = 8192
61
+
60
62
  # Initializes the renderer and evaulates the given template in the
61
63
  # renderer's scope.
62
64
  #
63
65
  # @param &template [Proc] template block
64
66
  def initialize(&template)
65
- @buffer = +''
67
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
66
68
  instance_eval(&template)
67
69
  end
68
70
 
@@ -70,6 +72,20 @@ module Papercraft
70
72
  #
71
73
  # @return [String]
72
74
  def to_s
75
+ if @parts
76
+ last = @buffer
77
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
78
+ parts = @parts
79
+ @parts = nil
80
+ parts.each do |p|
81
+ if Proc === p
82
+ render_deferred_proc(&p)
83
+ else
84
+ @buffer << p
85
+ end
86
+ end
87
+ @buffer << last unless last.empty?
88
+ end
73
89
  @buffer
74
90
  end
75
91
 
@@ -160,6 +176,42 @@ module Papercraft
160
176
 
161
177
  instance_exec(*a, **b, &@inner_block)
162
178
  end
179
+
180
+ # Defers the given block to be evaluated later. Deferred evaluation allows
181
+ # Papercraft components to inject state into sibling components, regardless
182
+ # of the component's order in the container component. For example, a nested
183
+ # component may set an instance variable used by another component. This is
184
+ # an elegant solution to the problem of setting the HTML page's title, or
185
+ # adding elements to the `<head>` section. Here's how a title can be
186
+ # controlled from a nested component:
187
+ #
188
+ # layout = H {
189
+ # html {
190
+ # head {
191
+ # defer { title @title }
192
+ # }
193
+ # body {
194
+ # emit_yield
195
+ # }
196
+ # }
197
+ # }
198
+ #
199
+ # html.render {
200
+ # @title = 'My super page'
201
+ # h1 'content'
202
+ # }
203
+ #
204
+ # @param &block [Proc] Deferred block to be emitted
205
+ # @return [void]
206
+ def defer(&block)
207
+ if !@parts
208
+ @parts = [@buffer, block]
209
+ else
210
+ @parts << @buffer unless @buffer.empty?
211
+ @parts << block
212
+ end
213
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
214
+ end
163
215
 
164
216
  S_LT = '<'
165
217
  S_GT = '>'
@@ -217,6 +269,23 @@ module Papercraft
217
269
  end
218
270
  }
219
271
  end
272
+
273
+ # Renders a deferred proc by evaluating it, then adding the rendered result
274
+ # to the buffer.
275
+ #
276
+ # @param &block [Proc] deferred proc
277
+ # @return [void]
278
+ def render_deferred_proc(&block)
279
+ old_buffer = @buffer
280
+
281
+ @buffer = String.new(capacity: INITIAL_BUFFER_CAPACITY)
282
+ @parts = nil
283
+
284
+ instance_eval(&block)
285
+
286
+ old_buffer << to_s
287
+ @buffer = old_buffer
288
+ end
220
289
  end
221
290
 
222
291
  # Implements an HTML renderer
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Papercraft
4
- VERSION = '0.10.1'
4
+ VERSION = '0.11'
5
5
  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.10.1
4
+ version: '0.11'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-25 00:00:00.000000000 Z
11
+ date: 2022-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: escape_utils
@@ -166,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
166
  - !ruby/object:Gem::Version
167
167
  version: '0'
168
168
  requirements: []
169
- rubygems_version: 3.2.32
169
+ rubygems_version: 3.3.3
170
170
  signing_key:
171
171
  specification_version: 4
172
172
  summary: 'Papercraft: component-based HTML templating for Ruby'