papercraft 0.17 → 0.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +135 -51
- data/lib/papercraft/extension_proxy.rb +3 -1
- data/lib/papercraft/html.rb +32 -0
- data/lib/papercraft/json.rb +89 -36
- data/lib/papercraft/renderer.rb +12 -240
- data/lib/papercraft/tags.rb +261 -0
- data/lib/papercraft/{component.rb → template.rb} +29 -26
- data/lib/papercraft/version.rb +1 -1
- data/lib/papercraft/xml.rb +39 -0
- data/lib/papercraft.rb +17 -17
- data/papercraft.png +0 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a23bf614d84747a18eecf84545604c9c2f58710dc3fa0e0c651a863a53c919b0
|
4
|
+
data.tar.gz: 6b1ca8f4a90801beff11807dd357ce801b7526b5d114d5cca5f41f41aec69958
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcd7eada8280cb0fcd68528eaea0d2500ff99ac149c8495dd5a3955a93c3f181301a4decc9eeb4d7efdd8cfe2c4f6047c9f838e5084b413c1118a76d6abc4252
|
7
|
+
data.tar.gz: 840ce0fc2a83de9b5a83bb10a5f4ed98986f7201737d6e31a4e89877aeb6bb22b2f2449c6efa08302473c073e26c830cfbe9e267456b450a56222b57a429fd15
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
## 0.21 2022-02-13
|
2
|
+
|
3
|
+
- Refactor and improve documentation
|
4
|
+
|
5
|
+
## 0.20 2022-02-13
|
6
|
+
|
7
|
+
- Add support for XML namespaced tags and attributes (#9)
|
8
|
+
- Move and refactor HTML/XML common code to Tags module
|
9
|
+
|
10
|
+
## 0.19 2022-02-05
|
11
|
+
|
12
|
+
- Rename `Papercraft::Component` to `Papercraft::Template`
|
13
|
+
|
14
|
+
## 0.18 2022-02-04
|
15
|
+
|
16
|
+
- Cleanup and update examples
|
17
|
+
- Fix behaviour of #emit with block
|
18
|
+
- Improve README
|
19
|
+
|
1
20
|
## 0.17 2022-01-23
|
2
21
|
|
3
22
|
- Refactor markdown code, add `Papercraft.markdown` method (#8)
|
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
|
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
|
42
|
-
```
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
-
|
47
|
-
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
-
|
52
|
-
-
|
53
|
-
-
|
54
|
-
-
|
55
|
-
-
|
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
|
-
##
|
99
|
+
## Basic usage
|
72
100
|
|
73
|
-
To create
|
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
|
-
##
|
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
|
-
##
|
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
|
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 `
|
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
|
-
##
|
222
|
+
## Template composition
|
192
223
|
|
193
|
-
Papercraft makes it easy to compose multiple
|
194
|
-
document. A Papercraft
|
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
|
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
|
229
|
-
non-constant
|
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
|
245
|
-
creation of higher-order
|
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
|
248
|
-
or block to the original
|
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
|
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
|
297
|
+
## Higher-order templates
|
267
298
|
|
268
|
-
Papercraft also lets you create higher-order
|
269
|
-
|
270
|
-
|
271
|
-
markup, enhancing
|
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
|
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
|
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
|
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`
|
@@ -384,11 +415,27 @@ The deafult options can be configured by accessing
|
|
384
415
|
Papercraft.default_kramdown_options[:auto_ids] = false
|
385
416
|
```
|
386
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`:
|
425
|
+
|
426
|
+
```ruby
|
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
|
432
|
+
```
|
433
|
+
|
387
434
|
## Deferred evaluation
|
388
435
|
|
389
436
|
Deferred evaluation allows deferring the rendering of parts of a template until
|
390
|
-
the last moment, thus allowing an inner
|
391
|
-
outer
|
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
|
392
439
|
include any markup in the provided block. This technique, in in conjunction with
|
393
440
|
holding state in instance variables, is an alternative to passing parameters,
|
394
441
|
which can be limiting in some situations.
|
@@ -397,11 +444,11 @@ A few use cases for deferred evaulation come to mind:
|
|
397
444
|
|
398
445
|
- Setting the page title.
|
399
446
|
- Adding a flash message to a page.
|
400
|
-
- Using
|
447
|
+
- Using templates that dynamically add static dependencies (JS and CSS) to the
|
401
448
|
page.
|
402
449
|
|
403
450
|
The last use case is particularly interesting. Imagine a `DependencyMananger`
|
404
|
-
class that can collect JS and CSS dependencies from the different
|
451
|
+
class that can collect JS and CSS dependencies from the different templates
|
405
452
|
integrated into the page, and adds them to the page's `<head>` element:
|
406
453
|
|
407
454
|
```ruby
|
@@ -502,11 +549,47 @@ Papercraft.html {
|
|
502
549
|
}
|
503
550
|
```
|
504
551
|
|
505
|
-
## JSON templating
|
506
552
|
|
507
|
-
|
508
|
-
|
509
|
-
|
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:
|
510
593
|
|
511
594
|
```ruby
|
512
595
|
Papercraft.json {
|
@@ -531,7 +614,8 @@ Papercraft.json {
|
|
531
614
|
}.render #=> "{\"foo\":{\"bar\":[null,true,123.456]}}"
|
532
615
|
```
|
533
616
|
|
534
|
-
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.
|
535
619
|
|
536
620
|
## API Reference
|
537
621
|
|
@@ -10,6 +10,7 @@ module Papercraft
|
|
10
10
|
class ExtensionProxy
|
11
11
|
|
12
12
|
# Initializes a new ExtensionProxy.
|
13
|
+
#
|
13
14
|
# @param renderer [Papercraft::Renderer] renderer to proxy to
|
14
15
|
# @param mod [Module] extension module
|
15
16
|
# @return [void]
|
@@ -18,7 +19,8 @@ module Papercraft
|
|
18
19
|
extend(mod)
|
19
20
|
end
|
20
21
|
|
21
|
-
# Proxies missing methods to the renderer
|
22
|
+
# Proxies missing methods to the renderer.
|
23
|
+
#
|
22
24
|
# @param sym [Symbol] method name
|
23
25
|
# @param *args [Array] arguments
|
24
26
|
# @param &block [Proc] block
|
data/lib/papercraft/html.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative './tags'
|
4
|
+
|
3
5
|
module Papercraft
|
4
6
|
# HTML Markup extensions
|
5
7
|
module HTML
|
8
|
+
include Tags
|
9
|
+
|
6
10
|
# Emits the p tag (overrides Object#p)
|
7
11
|
#
|
8
12
|
# @param text [String] text content of tag
|
@@ -81,5 +85,33 @@ module Papercraft
|
|
81
85
|
def emit_markdown(markdown, **opts)
|
82
86
|
emit Papercraft.markdown(markdown, **opts)
|
83
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# Escapes the given text using HTML entities.
|
92
|
+
#
|
93
|
+
# @param text [String] text
|
94
|
+
# @return [String] escaped text
|
95
|
+
def escape_text(text)
|
96
|
+
EscapeUtils.escape_html(text.to_s)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Converts a tag to its string representation. Underscores will be converted
|
100
|
+
# to dashes.
|
101
|
+
#
|
102
|
+
# @param tag [Symbol, String] tag
|
103
|
+
# @return [String] tag string
|
104
|
+
def tag_repr(tag)
|
105
|
+
tag.to_s.tr('_', '-')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Converts an attribute to its string representation. Underscores will be
|
109
|
+
# converted to dashes.
|
110
|
+
#
|
111
|
+
# @param att [Symbol, String] attribute
|
112
|
+
# @return [String] attribute string
|
113
|
+
def att_repr(att)
|
114
|
+
att.to_s.tr('_', '-')
|
115
|
+
end
|
84
116
|
end
|
85
117
|
end
|
data/lib/papercraft/json.rb
CHANGED
@@ -5,69 +5,122 @@ require 'json'
|
|
5
5
|
module Papercraft
|
6
6
|
# JSON renderer extensions
|
7
7
|
module JSON
|
8
|
-
|
9
|
-
|
8
|
+
# Initializes a JSON renderer, setting up an object stack.
|
9
|
+
def initialize(&template)
|
10
|
+
@object_stack = [nil]
|
11
|
+
super
|
10
12
|
end
|
11
13
|
|
14
|
+
# Adds an array item to the current object target. If a block is given, the
|
15
|
+
# block is evaulated against a new object target, then added to the current
|
16
|
+
# array.
|
17
|
+
#
|
18
|
+
# @param value [Object] item
|
19
|
+
# @param &block [Proc] template block
|
20
|
+
# @return [void]
|
21
|
+
def item(value = nil, &block)
|
22
|
+
verify_array_target
|
23
|
+
if block
|
24
|
+
value = enter_object(&block)
|
25
|
+
end
|
26
|
+
push_array_item(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Adds a key-value item to the current object target. If a block is given,
|
30
|
+
# the block is evaulated against a new object target, then used as the
|
31
|
+
# value.
|
32
|
+
#
|
33
|
+
# @param key [Object] key
|
34
|
+
# @param value [Object] value
|
35
|
+
# @param &block [Proc] template block
|
36
|
+
# @return [void]
|
37
|
+
def kv(key, value = nil, &block)
|
38
|
+
verify_hash_target
|
39
|
+
if block
|
40
|
+
value = enter_object(&block)
|
41
|
+
end
|
42
|
+
push_kv_item(key, value)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Intercepts method calls by adding key-value pairs to the current object
|
46
|
+
# target.
|
47
|
+
#
|
48
|
+
# @param key [Object] key
|
49
|
+
# @param value [Object] value
|
50
|
+
# @param &block [Proc] template block
|
51
|
+
# @return [void]
|
52
|
+
def method_missing(sym, value = nil, &block)
|
53
|
+
kv(sym, value, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Converts the root object target to JSON.
|
57
|
+
#
|
58
|
+
# @return [String] JSON template result
|
59
|
+
def to_s
|
60
|
+
@object_stack[0].to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Adds a new entry to the object stack and evaluates the given block.
|
66
|
+
#
|
67
|
+
# @param &block [Proc] template block
|
68
|
+
# @return [void]
|
12
69
|
def with_object(&block)
|
13
|
-
object_stack << nil
|
70
|
+
@object_stack << nil
|
14
71
|
instance_eval(&block)
|
15
72
|
end
|
16
73
|
|
74
|
+
# Verifies that the current object target is not a hash.
|
75
|
+
#
|
76
|
+
# @return [bool]
|
17
77
|
def verify_array_target
|
18
|
-
case object_stack[-1]
|
78
|
+
case @object_stack[-1]
|
19
79
|
when nil
|
20
|
-
object_stack[-1] = []
|
80
|
+
@object_stack[-1] = []
|
21
81
|
when Hash
|
22
82
|
raise "Mixing array and hash values"
|
23
83
|
end
|
24
84
|
end
|
25
85
|
|
86
|
+
# Verifies that the current object target is not an array.
|
87
|
+
#
|
88
|
+
# @return [bool]
|
26
89
|
def verify_hash_target
|
27
|
-
case object_stack[-1]
|
90
|
+
case @object_stack[-1]
|
28
91
|
when nil
|
29
|
-
object_stack[-1] = {}
|
92
|
+
@object_stack[-1] = {}
|
30
93
|
when Array
|
31
94
|
raise "Mixing array and hash values"
|
32
95
|
end
|
33
96
|
end
|
34
97
|
|
98
|
+
# Pushes an array item to the current object target.
|
99
|
+
#
|
100
|
+
# @param value [Object] item
|
101
|
+
# @return [void]
|
35
102
|
def push_array_item(value)
|
36
|
-
object_stack[-1] << value
|
103
|
+
@object_stack[-1] << value
|
37
104
|
end
|
38
105
|
|
106
|
+
# Pushes a key value into the current object target.
|
107
|
+
#
|
108
|
+
# @param key [Object] key
|
109
|
+
# @param value [Object] value
|
110
|
+
# @return [void]
|
39
111
|
def push_kv_item(key, value)
|
40
|
-
object_stack[-1][key] = value
|
112
|
+
@object_stack[-1][key] = value
|
41
113
|
end
|
42
114
|
|
115
|
+
# Adds a new object to the object stack, evaluates the given template block,
|
116
|
+
# then pops the object off the stack.
|
117
|
+
#
|
118
|
+
# @param &block [Proc] template block
|
119
|
+
# @return [void]
|
43
120
|
def enter_object(&block)
|
44
|
-
object_stack << nil
|
121
|
+
@object_stack << nil
|
45
122
|
instance_eval(&block)
|
46
|
-
object_stack.pop
|
47
|
-
end
|
48
|
-
|
49
|
-
def item(value = nil, &block)
|
50
|
-
verify_array_target
|
51
|
-
if block
|
52
|
-
value = enter_object(&block)
|
53
|
-
end
|
54
|
-
push_array_item(value)
|
55
|
-
end
|
56
|
-
|
57
|
-
def kv(key, value, &block)
|
58
|
-
verify_hash_target
|
59
|
-
if block
|
60
|
-
value = enter_object(&block)
|
61
|
-
end
|
62
|
-
push_kv_item(key, value)
|
63
|
-
end
|
64
|
-
|
65
|
-
def method_missing(sym, value = nil, &block)
|
66
|
-
kv(sym, value, &block)
|
67
|
-
end
|
68
|
-
|
69
|
-
def to_s
|
70
|
-
object_stack[0].to_json
|
71
|
-
end
|
123
|
+
@object_stack.pop
|
124
|
+
end
|
72
125
|
end
|
73
126
|
end
|