papercraft 0.10.1 → 0.11

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: 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'