props_template 0.14.0 → 0.20.0
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 +4 -4
- data/README.md +205 -140
- data/lib/props_template.rb +3 -9
- data/lib/props_template/base.rb +33 -24
- data/lib/props_template/base_with_extensions.rb +18 -19
- data/lib/props_template/debug_writer.rb +55 -0
- data/lib/props_template/extension_manager.rb +27 -31
- data/lib/props_template/extensions/cache.rb +2 -21
- data/lib/props_template/extensions/deferment.rb +11 -3
- data/lib/props_template/extensions/fragment.rb +4 -28
- data/lib/props_template/extensions/partial_renderer.rb +79 -33
- data/lib/props_template/railtie.rb +0 -8
- data/lib/props_template/searcher.rb +0 -3
- data/lib/props_template/version.rb +3 -0
- data/spec/layout_spec.rb +18 -10
- metadata +15 -26
- data/lib/props_template/key_formatter.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cf7de015844e77efdf93e02272159080835cad2e0f6022e610690727a05cfb5
|
4
|
+
data.tar.gz: 350d1a534d30c625b7adbe3c95d6ac33c2a831205c970c404bb2c9eeb22dfd69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 533ff82a0dac43e211d9a5800b71ef9ec59a4e8d34d16214a3799ea7398e9746a1d0b823db55bfd4b272ea9a48a9e685a3f49fc437f8b7a7aa1ed49ef2d531c8
|
7
|
+
data.tar.gz: 0fc9fc3e0b1d898c1870c286d14dcd59e8a6679d428c026dcf2c6107ef8e281f40d0e08838f625e1a39b321199510920fe2e3a1b9ff20a08990106220fffc949
|
data/README.md
CHANGED
@@ -1,8 +1,29 @@
|
|
1
1
|
# PropsTemplate
|
2
2
|
|
3
|
-
PropsTemplate is a
|
3
|
+
PropsTemplate is a direct-to-Oj, JBuilder-like DSL for building JSON. It has
|
4
|
+
support for Russian-Doll caching, layouts, and can be queried by giving the
|
5
|
+
root a key path.
|
4
6
|
|
5
|
-
|
7
|
+
[](https://circleci.com/gh/thoughtbot/props_template)
|
9
|
+
|
10
|
+
It's fast.
|
11
|
+
|
12
|
+
PropsTemplate bypasses the steps of hash building and serializing
|
13
|
+
that other libraries perform by using Oj's `StringWriter` in `rails` mode.
|
14
|
+
|
15
|
+

|
16
|
+
|
17
|
+
Caching is fast too.
|
18
|
+
|
19
|
+
While other libraries spend time unmarshaling,
|
20
|
+
merging hashes, and serializing to JSON; PropsTemplate simply takes
|
21
|
+
the cached string and uses Oj's [push_json](http://www.ohler.com/oj/doc/Oj/StringWriter.html#push_json-instance_method).
|
22
|
+
|
23
|
+
## Example:
|
24
|
+
|
25
|
+
PropsTemplate is very similar to JBuilder, and selectively retains some
|
26
|
+
conveniences and magic.
|
6
27
|
|
7
28
|
```ruby
|
8
29
|
json.flash flash.to_h
|
@@ -40,25 +61,33 @@ json.posts do
|
|
40
61
|
json.total @posts.count
|
41
62
|
end
|
42
63
|
|
43
|
-
|
44
64
|
json.footer partial: 'shared/footer' do
|
45
65
|
end
|
46
66
|
```
|
47
67
|
|
68
|
+
## Installation
|
69
|
+
|
70
|
+
```
|
71
|
+
gem 'props_template'
|
72
|
+
```
|
73
|
+
|
74
|
+
and run `bundle`
|
75
|
+
|
48
76
|
## API
|
49
77
|
|
50
|
-
### json.set! or json
|
51
|
-
|
78
|
+
### json.set! or json.\<your key here\>
|
79
|
+
|
80
|
+
Defines the attribute or structure. All keys are automatically camelized lower.
|
52
81
|
|
53
82
|
```ruby
|
54
|
-
json.set! :author_details, {
|
83
|
+
json.set! :author_details, {...options} do
|
55
84
|
json.set! :first_name, 'David'
|
56
85
|
end
|
57
86
|
|
58
87
|
or
|
59
88
|
|
60
|
-
json.author_details, {
|
61
|
-
json.first_name
|
89
|
+
json.author_details, {...options} do
|
90
|
+
json.first_name 'David'
|
62
91
|
end
|
63
92
|
|
64
93
|
|
@@ -73,6 +102,7 @@ The inline form defines key and value
|
|
73
102
|
| value | A value |
|
74
103
|
|
75
104
|
```ruby
|
105
|
+
|
76
106
|
json.set! :first_name, 'David'
|
77
107
|
|
78
108
|
or
|
@@ -92,39 +122,36 @@ The block form defines key and structure
|
|
92
122
|
|
93
123
|
```ruby
|
94
124
|
json.set! :details do
|
95
|
-
|
125
|
+
...
|
96
126
|
end
|
97
127
|
|
98
128
|
or
|
99
129
|
|
100
130
|
json.details do
|
101
|
-
|
131
|
+
...
|
102
132
|
end
|
103
133
|
```
|
104
134
|
|
105
135
|
The difference between the block form and inline form is
|
106
|
-
1. The block form is an internal node.
|
107
|
-
|
136
|
+
1. The block form is an internal node. Functionality such as Partials,
|
137
|
+
Deferment and other [options](#options) are only available on the
|
138
|
+
block form.
|
139
|
+
2. The inline form is considered a leaf node, and you can only [search](#traversing)
|
140
|
+
for internal nodes.
|
108
141
|
|
109
142
|
### json.array!
|
110
143
|
Generates an array of json objects.
|
111
144
|
|
112
145
|
```ruby
|
113
|
-
collection = [
|
114
|
-
{name: 'john'},
|
115
|
-
{name: 'jim'}
|
116
|
-
]
|
146
|
+
collection = [ {name: 'john'}, {name: 'jim'} ]
|
117
147
|
|
118
148
|
json.details do
|
119
|
-
json.array! collection, {
|
149
|
+
json.array! collection, {...options} do |person|
|
120
150
|
json.first_name person[:name]
|
121
151
|
end
|
122
152
|
end
|
123
153
|
|
124
|
-
# => {"details": [
|
125
|
-
{"firstName": 'john'},
|
126
|
-
{"firstName": 'jim'}
|
127
|
-
]}
|
154
|
+
# => {"details": [{"firstName": 'john'}, {"firstName": 'jim'} ]}
|
128
155
|
```
|
129
156
|
|
130
157
|
| Parameter | Notes |
|
@@ -132,7 +159,8 @@ end
|
|
132
159
|
| collection | A collection that responds to `member_at` and `member_by` |
|
133
160
|
| options | Additional [options](#options)|
|
134
161
|
|
135
|
-
To support [traversing nodes](
|
162
|
+
To support [traversing nodes](#traversing), any list passed
|
163
|
+
to `array!` MUST implement `member_at(index)` and `member_by(attr, value)`.
|
136
164
|
|
137
165
|
For example, if you were using a delegate:
|
138
166
|
|
@@ -153,7 +181,10 @@ end
|
|
153
181
|
Then in your template:
|
154
182
|
|
155
183
|
```ruby
|
156
|
-
data = ObjectCollection.new([
|
184
|
+
data = ObjectCollection.new([
|
185
|
+
{id: 1, name: 'foo'},
|
186
|
+
{id: 2, name: 'bar'}
|
187
|
+
])
|
157
188
|
|
158
189
|
json.array! data do
|
159
190
|
...
|
@@ -184,11 +215,15 @@ end
|
|
184
215
|
|
185
216
|
#### **Array core extension**
|
186
217
|
|
187
|
-
For convenience, PropsTemplate includes a core\_ext that adds these methods to
|
218
|
+
For convenience, PropsTemplate includes a core\_ext that adds these methods to
|
219
|
+
`Array`. For example:
|
188
220
|
|
189
221
|
```ruby
|
190
222
|
require 'props_template/core_ext'
|
191
|
-
data = [
|
223
|
+
data = [
|
224
|
+
{id: 1, name: 'foo'},
|
225
|
+
{id: 2, name: 'bar'}
|
226
|
+
]
|
192
227
|
|
193
228
|
json.posts
|
194
229
|
json.array! data do
|
@@ -197,38 +232,39 @@ json.posts
|
|
197
232
|
end
|
198
233
|
```
|
199
234
|
|
200
|
-
PropsTemplate does not know what the elements are in your collection. The
|
235
|
+
PropsTemplate does not know what the elements are in your collection. The
|
236
|
+
example above will be fine for [traversing](#traversing)
|
237
|
+
by index, but will raise a `NotImplementedError` if you query by attribute. You
|
238
|
+
may still need to implement `member_by`.
|
201
239
|
|
202
240
|
### json.deferred!
|
203
|
-
Returns all deferred nodes used by the [
|
241
|
+
Returns all deferred nodes used by the [deferment](#deferment) option.
|
204
242
|
|
205
|
-
|
206
|
-
json.
|
207
|
-
```
|
208
|
-
|
209
|
-
This method is normally used in `application.json.props` when first generated by `rails breezy:install:web`
|
243
|
+
**Note** This is a [BreezyJS][1] specific functionality and is used in
|
244
|
+
`application.json.props` when first running `rails breezy:install:web`
|
210
245
|
|
211
|
-
### json.fragments!
|
212
|
-
Returns all fragment nodes used by the [partial fragments](#partial-fragments) option.
|
213
246
|
|
214
247
|
```ruby
|
215
|
-
json.
|
216
|
-
```
|
248
|
+
json.deferred json.deferred!
|
217
249
|
|
218
|
-
|
250
|
+
# => [{url: '/some_url?bzq=outer.inner', path: 'outer.inner', type: 'auto'}]
|
251
|
+
```
|
219
252
|
|
220
|
-
|
253
|
+
This method provides metadata about deferred nodes to the frontend ([BreezyJS][1])
|
254
|
+
to fetch missing data in a second round trip.
|
221
255
|
|
222
|
-
|
256
|
+
### json.fragments!
|
257
|
+
Returns all fragment nodes used by the [partial fragments](#partial-fragments)
|
258
|
+
option.
|
223
259
|
|
224
|
-
```ruby
|
225
|
-
# _some_partial.json.props
|
260
|
+
```ruby json.fragments json.fragments! ```
|
226
261
|
|
227
|
-
|
228
|
-
|
262
|
+
**Note** This is a [BreezyJS][1] specific functionality and is used in
|
263
|
+
`application.json.props` when first running `rails breezy:install:web`
|
229
264
|
|
230
265
|
## Options
|
231
|
-
Functionality such as Partials, Deferements, and Caching can only be
|
266
|
+
Options Functionality such as Partials, Deferements, and Caching can only be
|
267
|
+
set on a block. It is normal to see empty blocks.
|
232
268
|
|
233
269
|
```ruby
|
234
270
|
json.post(partial: 'blog_post') do
|
@@ -237,7 +273,9 @@ end
|
|
237
273
|
|
238
274
|
### Partials
|
239
275
|
|
240
|
-
Partials are supported. The following will render the file
|
276
|
+
Partials are supported. The following will render the file
|
277
|
+
`views/posts/_blog_posts.json.props`, and set a local variable `foo` assigned
|
278
|
+
with @post, which you can use inside the partial.
|
241
279
|
|
242
280
|
```ruby
|
243
281
|
json.one_post partial: ["posts/blog_post", locals: {post: @post}] do
|
@@ -247,7 +285,8 @@ end
|
|
247
285
|
Usage with arrays:
|
248
286
|
|
249
287
|
```ruby
|
250
|
-
#
|
288
|
+
# The `as:` option is supported when using `array!`
|
289
|
+
|
251
290
|
json.posts do
|
252
291
|
json.array! @posts, partial: ["posts/blog_post", locals: {foo: 'bar'}, as: 'post'] do
|
253
292
|
end
|
@@ -255,14 +294,14 @@ end
|
|
255
294
|
```
|
256
295
|
|
257
296
|
### Partial Fragments
|
297
|
+
**Note** This is a [BreezyJS][1] specific functionality.
|
258
298
|
|
259
|
-
A fragment
|
260
|
-
|
261
|
-
You would need use partials and add the option `fragment: true`.
|
299
|
+
A fragment identifies a partial output across multiple pages. It can be used to
|
300
|
+
update cross cutting concerns like a header bar.
|
262
301
|
|
263
302
|
```ruby
|
264
303
|
# index.json.props
|
265
|
-
json.header partial: ["profile", fragment:
|
304
|
+
json.header partial: ["profile", fragment: "header"] do
|
266
305
|
end
|
267
306
|
|
268
307
|
# _profile.json.props
|
@@ -276,57 +315,16 @@ end
|
|
276
315
|
When using fragments with Arrays, the argument **MUST** be a lamda:
|
277
316
|
|
278
317
|
```ruby
|
279
|
-
require 'props_template/core_ext'
|
280
|
-
|
281
|
-
json.array! ['foo', 'bar'], partial: ["footer", fragment: ->(x){ x == 'foo'}]
|
282
|
-
```
|
283
|
-
|
284
|
-
PropsTemplate creates a name for the partial using a digest of your locals, partial name, and globalId (to_json as fallback if there is no globalId) on objects that you pass. You may override this behavior and use a custom identifier:
|
318
|
+
require 'props_template/core_ext'
|
285
319
|
|
286
|
-
|
287
|
-
# index.js.breezy
|
288
|
-
json.header partial: ["profile", fragment: 'me_header'] do
|
320
|
+
json.array! ['foo', 'bar'], partial: ["footer", fragment: ->(x){ x == 'foo'}] do
|
289
321
|
end
|
290
322
|
```
|
291
323
|
|
292
|
-
#### Optimisitc Updates
|
293
|
-
Breezy uses the digest generated by `fragment: true` to uniquely identify a partial across the redux state. If you need to optimistically update a fragment, use `json.fragment_digest!` to obtain the identifier in your partial, and `updateFragments` from BreezyJS.
|
294
|
-
|
295
|
-
For example:
|
296
|
-
|
297
|
-
```ruby
|
298
|
-
# _header.js.props
|
299
|
-
json.fragment_digest json.fragment_digest!
|
300
|
-
```
|
301
|
-
|
302
|
-
And in your reducer
|
303
|
-
|
304
|
-
```javacript
|
305
|
-
import {updateFragments} from 'jho406/Breezy';
|
306
|
-
|
307
|
-
switch(action.type) {
|
308
|
-
case SOME_ACTION: {
|
309
|
-
const {
|
310
|
-
fragmentDigest,
|
311
|
-
prevNode // <- the content of the _header.js.props
|
312
|
-
} = action.payload
|
313
|
-
|
314
|
-
const nextNode = {
|
315
|
-
...prevNode,
|
316
|
-
foo: 'bar'
|
317
|
-
}
|
318
|
-
|
319
|
-
return updateFragments(state, {
|
320
|
-
[fragmentDigest]: nextNode
|
321
|
-
})
|
322
|
-
}
|
323
|
-
default:
|
324
|
-
return state
|
325
|
-
}
|
326
|
-
```
|
327
|
-
|
328
324
|
### Caching
|
329
|
-
Caching is supported on
|
325
|
+
Caching is supported on internal nodes only. This limitation is what makes it
|
326
|
+
possible to for props_template to forgo marshalling/unmarshalling and simply
|
327
|
+
use [push_json](http://www.ohler.com/oj/doc/Oj/StringWriter.html#push_json-instance_method).
|
330
328
|
|
331
329
|
Usage:
|
332
330
|
|
@@ -352,44 +350,60 @@ end
|
|
352
350
|
When used with arrays, PropsTemplate will use `Rails.cache.read_multi`.
|
353
351
|
|
354
352
|
```ruby
|
355
|
-
require 'props_template/core_ext'
|
353
|
+
require 'props_template/core_ext'
|
354
|
+
|
355
|
+
opts = { cache: ->(i){ ['a', i] } }
|
356
356
|
|
357
|
-
opts = {
|
358
|
-
cache: ->(i){ ['a', i] }
|
359
|
-
}
|
360
357
|
json.array! [4,5], opts do |x|
|
361
358
|
json.top "hello" + x.to_s
|
362
359
|
end
|
363
360
|
|
364
361
|
#or on arrays with partials
|
365
362
|
|
366
|
-
opts = {
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
json.array! @options, opts
|
363
|
+
opts = { cache: (->(d){ ['a', d.id] }), partial: ["blog_post", as: :blog_post] }
|
364
|
+
|
365
|
+
json.array! @options, opts do
|
366
|
+
end
|
371
367
|
```
|
372
368
|
|
373
369
|
### Deferment
|
374
370
|
|
375
|
-
You can defer rendering of expensive nodes in your content tree using the
|
376
|
-
|
371
|
+
You can defer rendering of expensive nodes in your content tree using the
|
372
|
+
`defer: :manual` option. Behind the scenes PropsTemplates will no-op the block
|
373
|
+
entirely and replace the value with a placeholder. A common use case would be
|
374
|
+
tabbed content that does not load until you click the tab.
|
375
|
+
|
376
|
+
When your client receives the payload, you may issue a second request to the
|
377
|
+
same endpoint to fetch any missing nodes. See [traversing nodes](#traversing)
|
377
378
|
|
378
|
-
|
379
|
+
There is also an `defer: :auto` option that you can use with [BreezyJS][1]. [BreezyJS][1]
|
380
|
+
will use the metadata from `json.deferred!` to issue a `remote` dispatch to fetch
|
381
|
+
the missing node and immutably graft it at the appropriate keypath in your Redux
|
382
|
+
store.
|
379
383
|
|
380
384
|
Usage:
|
381
385
|
|
382
386
|
```ruby
|
383
|
-
json.dashboard(defer: :
|
387
|
+
json.dashboard(defer: :manual) do
|
388
|
+
sleep 10
|
389
|
+
json.some_fancy_metric 42
|
390
|
+
end
|
391
|
+
|
392
|
+
|
393
|
+
# or you can explicitly pass a placeholder
|
394
|
+
|
395
|
+
json.dashboard(defer: [:manual, placeholder: {}]) do
|
384
396
|
sleep 10
|
385
397
|
json.some_fancy_metric 42
|
386
398
|
end
|
387
399
|
```
|
388
400
|
|
389
|
-
A
|
401
|
+
A auto option is available:
|
402
|
+
|
403
|
+
**Note** This is a [BreezyJS][1] specific functionality.
|
390
404
|
|
391
405
|
```ruby
|
392
|
-
json.dashboard(defer: :
|
406
|
+
json.dashboard(defer: :auto) do
|
393
407
|
sleep 10
|
394
408
|
json.some_fancy_metric 42
|
395
409
|
end
|
@@ -401,39 +415,51 @@ Finally in your `application.json.props`:
|
|
401
415
|
json.defers json.deferred!
|
402
416
|
```
|
403
417
|
|
404
|
-
|
405
|
-
If `:manual` is used, PropsTemplate will no-op the block and will not populate `json.deferred!`. Its up to you to [traverse](props-template.md#traversing_nodes) to fetch the node seperately. A common usecase would be tab content that does not load until you click the tab.
|
406
|
-
|
407
418
|
#### Working with arrays
|
408
|
-
The default behavior for deferements is to use the index of the collection to
|
419
|
+
The default behavior for deferements is to use the index of the collection to
|
420
|
+
identify an element.
|
421
|
+
|
422
|
+
**Note** If you are using this library with [BreezyJS][1], the `:auto` options will
|
423
|
+
generate `?_bzq=a.b.c.0.title` for `json.deferred!`.
|
409
424
|
|
410
425
|
If you wish to use an attribute to identify the element. You must:
|
411
|
-
|
412
|
-
|
426
|
+
|
427
|
+
1. Use the `:key` option on `json.array!`. This key refers to an attribute on
|
428
|
+
your collection item, and is used for `defer: :auto` to generate a keypath for
|
429
|
+
[BreezyJS][1]. If you are NOT using BreezyJS, you do not need to do this.
|
430
|
+
|
431
|
+
2. Implement `member_at`, on the [collection](#jsonarray). This will be called
|
432
|
+
by PropsTemplate to when [searching nodes](#traversing)
|
413
433
|
|
414
434
|
For example:
|
415
435
|
|
416
436
|
```ruby
|
417
|
-
require 'props_template/core_ext'
|
418
|
-
|
419
|
-
|
437
|
+
require 'props_template/core_ext'
|
438
|
+
data = [
|
439
|
+
{id: 1, name: 'foo'},
|
440
|
+
{id: 2, name: 'bar'}
|
441
|
+
]
|
420
442
|
|
421
443
|
json.posts
|
422
444
|
json.array! data, key: :some_id do |item|
|
445
|
+
# By using :key, props_template will append `json.some_id item.some_id`
|
446
|
+
# automatically
|
447
|
+
|
423
448
|
json.contact(defer: :auto) do
|
424
449
|
json.address '123 example drive'
|
425
450
|
end
|
426
|
-
|
427
|
-
# json.some_id item.some_id will be appended automatically to the end of the block
|
428
451
|
end
|
429
452
|
end
|
430
453
|
```
|
431
454
|
|
432
|
-
|
455
|
+
If you are using [BreezyJS][1], BreezyJS will, it will automatically kick off
|
456
|
+
`remote(?bzq=posts.some_id=1.contact)` and `remote(?bzq=posts.some_id=2.contact)`.
|
433
457
|
|
434
|
-
|
458
|
+
## Traversing
|
435
459
|
|
436
|
-
PropsTemplate has the ability to walk the tree you build, skipping execution of
|
460
|
+
PropsTemplate has the ability to walk the tree you build, skipping execution of
|
461
|
+
untargeted nodes. This feature is useful for selectively updating your frontend
|
462
|
+
state.
|
437
463
|
|
438
464
|
```ruby
|
439
465
|
traversal_path = ['data', 'details', 'personal']
|
@@ -441,7 +467,7 @@ traversal_path = ['data', 'details', 'personal']
|
|
441
467
|
json.data(search: traversal_path) do
|
442
468
|
json.details do
|
443
469
|
json.employment do
|
444
|
-
...more stuff
|
470
|
+
...more stuff
|
445
471
|
end
|
446
472
|
|
447
473
|
json.personal do
|
@@ -452,25 +478,28 @@ json.data(search: traversal_path) do
|
|
452
478
|
end
|
453
479
|
|
454
480
|
json.footer do
|
455
|
-
|
481
|
+
...
|
456
482
|
end
|
457
483
|
```
|
458
484
|
|
459
|
-
PropsTemplate will
|
485
|
+
PropsTemplate will walk depth first, walking only when it finds a matching key,
|
486
|
+
then executes the associated block, and repeats until it the node is found.
|
487
|
+
The above will output:
|
460
488
|
|
461
489
|
```json
|
462
490
|
{
|
463
|
-
data: {
|
464
|
-
name: 'james',
|
465
|
-
zipCode: 91210
|
491
|
+
"data": {
|
492
|
+
"name": 'james',
|
493
|
+
"zipCode": 91210
|
466
494
|
},
|
467
|
-
footer: {
|
468
|
-
|
495
|
+
"footer": {
|
496
|
+
...
|
469
497
|
}
|
470
498
|
}
|
471
499
|
```
|
472
500
|
|
473
|
-
|
501
|
+
Searching only works with blocks, and will NOT work with Scalars
|
502
|
+
("leaf" values). For example:
|
474
503
|
|
475
504
|
```ruby
|
476
505
|
traversal_path = ['data', 'details', 'personal', 'name'] <- not found
|
@@ -482,12 +511,11 @@ json.data(search: traversal_path) do
|
|
482
511
|
end
|
483
512
|
end
|
484
513
|
end
|
485
|
-
|
486
514
|
```
|
487
515
|
|
488
516
|
## Nodes that do not exist
|
489
517
|
|
490
|
-
Nodes that are not found will
|
518
|
+
Nodes that are not found will remove the branch where search was enabled on.
|
491
519
|
|
492
520
|
```ruby
|
493
521
|
traversal_path = ['data', 'details', 'does_not_exist']
|
@@ -501,17 +529,54 @@ json.data(search: traversal_path) do
|
|
501
529
|
end
|
502
530
|
|
503
531
|
json.footer do
|
504
|
-
|
532
|
+
...
|
505
533
|
end
|
506
|
-
|
507
534
|
```
|
508
535
|
|
509
536
|
The above will render:
|
510
537
|
|
511
|
-
```
|
538
|
+
```json
|
512
539
|
{
|
513
|
-
footer: {
|
540
|
+
"footer": {
|
514
541
|
...
|
515
542
|
}
|
516
543
|
}
|
517
544
|
```
|
545
|
+
|
546
|
+
## Layouts
|
547
|
+
A single layout is supported. To use, create an `application.json.props` in
|
548
|
+
`app/views/layouts`. Here's an example:
|
549
|
+
|
550
|
+
```ruby
|
551
|
+
json.data do
|
552
|
+
# template runs here.
|
553
|
+
yield json
|
554
|
+
end
|
555
|
+
|
556
|
+
json.header do
|
557
|
+
json.greeting "Hello"
|
558
|
+
end
|
559
|
+
|
560
|
+
json.footer do
|
561
|
+
json.greeting "Hello"
|
562
|
+
end
|
563
|
+
|
564
|
+
json.flash flash.to_h
|
565
|
+
```
|
566
|
+
|
567
|
+
**NOTE** PropsTemplate inverts the usual Rails rendering flow. PropsTemplate
|
568
|
+
will render Layout first, then the template when `yield json` is used.
|
569
|
+
|
570
|
+
## Contributing
|
571
|
+
|
572
|
+
See the [CONTRIBUTING] document. Thank you, [contributors]!
|
573
|
+
|
574
|
+
[CONTRIBUTING]: CONTRIBUTING.md
|
575
|
+
[contributors]: https://github.com/thoughtbot/props_template/graphs/contributors
|
576
|
+
|
577
|
+
## Special Thanks
|
578
|
+
|
579
|
+
Thanks to [turbostreamer](https://github.com/malomalo/turbostreamer) for the
|
580
|
+
inspiration.
|
581
|
+
|
582
|
+
[1]: https://github.com/thoughtbot/breezy
|