props_template 0.13.0 → 0.14.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad98bb076e592658c137fd423771b211760ba047a1f6f394efa7a531a25eedc5
|
4
|
+
data.tar.gz: f5495c4eca8b7015ac5d18714a10a16735769128fac62b5693824b1ef0c6e505
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 629b8dc24b25dc596ee6f2bb9d03abce69e5af292121d118a83e3d53b83f38ef7ccc155e4cc79d4469a6e588c2efa3231ac02b05447ed11aa6c4642fca833d6f
|
7
|
+
data.tar.gz: 83d0aed77e8cba7cf1730307f15a6fed2b972a181a1722c0dbba85a03489c12ca8b47f89b515e48f8a776564f3c35b58c6118be9396e0eb270c48cfbe21794c9
|
data/README.md
ADDED
@@ -0,0 +1,517 @@
|
|
1
|
+
# PropsTemplate
|
2
|
+
|
3
|
+
PropsTemplate is a queryable JSON templating library inspired by JBuilder. It has support for layouts, partials, russian-doll caching, multi-fetch and can selectively render nodes in your tree without executing others.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
json.flash flash.to_h
|
9
|
+
|
10
|
+
json.menu do
|
11
|
+
# all keys will be formatted as camelCase
|
12
|
+
|
13
|
+
json.current_user do
|
14
|
+
json.email current_user.email
|
15
|
+
json.avatar current_user.avatar
|
16
|
+
json.inbox current_user.messages.count
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
json.dashboard(defer: :auto) do
|
21
|
+
sleep 5
|
22
|
+
json.complex_post_metric 500
|
23
|
+
end
|
24
|
+
|
25
|
+
json.posts do
|
26
|
+
page_num = params[:page_num]
|
27
|
+
paged_posts = @posts.page(page_num).per(20)
|
28
|
+
|
29
|
+
json.list do
|
30
|
+
json.array! paged_posts, key: :id do |post|
|
31
|
+
json.id post.id
|
32
|
+
json.description post.description
|
33
|
+
json.comments_count post.comments.count
|
34
|
+
json.edit_path edit_post_path(post)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
json.pagination_path posts_path
|
39
|
+
json.current paged_posts.current_page
|
40
|
+
json.total @posts.count
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
json.footer partial: 'shared/footer' do
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
## API
|
49
|
+
|
50
|
+
### json.set! or json.<your key here>
|
51
|
+
Defines the attribute or stucture. All keys are automatically camelized.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
json.set! :author_details, {..options...} do
|
55
|
+
json.set! :first_name, 'David'
|
56
|
+
end
|
57
|
+
|
58
|
+
or
|
59
|
+
|
60
|
+
json.author_details, {..options...} do
|
61
|
+
json.first_name, 'David'
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# => {"authorDetails": { "firstName": "David" }}
|
66
|
+
```
|
67
|
+
|
68
|
+
The inline form defines key and value
|
69
|
+
|
70
|
+
| Parameter | Notes |
|
71
|
+
| :--- | :--- |
|
72
|
+
| key | A json object key|
|
73
|
+
| value | A value |
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
json.set! :first_name, 'David'
|
77
|
+
|
78
|
+
or
|
79
|
+
|
80
|
+
json.first_name 'David'
|
81
|
+
|
82
|
+
# => { "firstName": "David" }
|
83
|
+
```
|
84
|
+
|
85
|
+
The block form defines key and structure
|
86
|
+
|
87
|
+
| Parameter | Notes |
|
88
|
+
| :--- | :--- |
|
89
|
+
| key | A json object key|
|
90
|
+
| options | Additional [options](#options)|
|
91
|
+
| block | Additional `json.set!`s or `json.array!`s|
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
json.set! :details do
|
95
|
+
...
|
96
|
+
end
|
97
|
+
|
98
|
+
or
|
99
|
+
|
100
|
+
json.details do
|
101
|
+
...
|
102
|
+
end
|
103
|
+
```
|
104
|
+
|
105
|
+
The difference between the block form and inline form is
|
106
|
+
1. The block form is an internal node. Partials, Deferement and other [options](#options) are only available on the block form.
|
107
|
+
2. The inline form is considered a leaf node, and you can only [search](#traversing) for internal nodes.
|
108
|
+
|
109
|
+
### json.array!
|
110
|
+
Generates an array of json objects.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
collection = [
|
114
|
+
{name: 'john'},
|
115
|
+
{name: 'jim'}
|
116
|
+
]
|
117
|
+
|
118
|
+
json.details do
|
119
|
+
json.array! collection, {....options...} do |person|
|
120
|
+
json.first_name person[:name]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# => {"details": [
|
125
|
+
{"firstName": 'john'},
|
126
|
+
{"firstName": 'jim'}
|
127
|
+
]}
|
128
|
+
```
|
129
|
+
|
130
|
+
| Parameter | Notes |
|
131
|
+
| :--- | :--- |
|
132
|
+
| collection | A collection that responds to `member_at` and `member_by` |
|
133
|
+
| options | Additional [options](#options)|
|
134
|
+
|
135
|
+
To support [traversing nodes](react-redux.md#traversing-nodes), any list passed to `array!` MUST implement `member_at(index)` and `member_by(attr, value)`.
|
136
|
+
|
137
|
+
For example, if you were using a delegate:
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class ObjectCollection < SimpleDelegator
|
141
|
+
def member_at(index)
|
142
|
+
at(index)
|
143
|
+
end
|
144
|
+
|
145
|
+
def member_by(attr, val)
|
146
|
+
find do |ele|
|
147
|
+
ele[attr] == val
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
```
|
152
|
+
|
153
|
+
Then in your template:
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
data = ObjectCollection.new([{id: 1, name: 'foo'}, {id: 2, name: 'bar'}])
|
157
|
+
|
158
|
+
json.array! data do
|
159
|
+
...
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
Similarly for ActiveRecord:
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
class ApplicationRecord < ActiveRecord::Base
|
167
|
+
def self.member_at(index)
|
168
|
+
offset(index).limit(1).first
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.member_by(attr, value)
|
172
|
+
find_by(Hash[attr, val])
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
Then in your template:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
json.array! Post.all do
|
181
|
+
...
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
#### **Array core extension**
|
186
|
+
|
187
|
+
For convenience, PropsTemplate includes a core\_ext that adds these methods to `Array`. For example:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
require 'props_template/core_ext'
|
191
|
+
data = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}]
|
192
|
+
|
193
|
+
json.posts
|
194
|
+
json.array! data do
|
195
|
+
...
|
196
|
+
end
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
PropsTemplate does not know what the elements are in your collection. The example above will be fine for [traversing](props-template.md#traversing_nodes) by index `\posts?bzq=posts.0`, but will raise a `NotImplementedError` if you traverse by attribute `/posts?bzq=posts.id=1`. You may still need a delegate that implements `member_by`.
|
201
|
+
|
202
|
+
### json.deferred!
|
203
|
+
Returns all deferred nodes used by the [#deferment](#deferment) option.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
json.deferred json.deferred!
|
207
|
+
```
|
208
|
+
|
209
|
+
This method is normally used in `application.json.props` when first generated by `rails breezy:install:web`
|
210
|
+
|
211
|
+
### json.fragments!
|
212
|
+
Returns all fragment nodes used by the [partial fragments](#partial-fragments) option.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
json.fragments json.fragments!
|
216
|
+
```
|
217
|
+
|
218
|
+
This method is normally used in `application.json.props` when first generated by `rails breezy:install:web`
|
219
|
+
|
220
|
+
### json.fragment_digest!
|
221
|
+
|
222
|
+
Returns the digest of the current partial name and the locals passed. Useful for [optimistic updates](#optimistic-updates).
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
# _some_partial.json.props
|
226
|
+
|
227
|
+
json.digest json.fragment_digest!
|
228
|
+
```
|
229
|
+
|
230
|
+
## Options
|
231
|
+
Functionality such as Partials, Deferements, and Caching can only be set on a block. It is normal to see empty blocks.
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
json.post(partial: 'blog_post') do
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
### Partials
|
239
|
+
|
240
|
+
Partials are supported. The following will render the file `views/posts/_blog_posts.json.props`, and set a local variable `foo` assigned with @post, which you can use inside the partial.
|
241
|
+
|
242
|
+
```ruby
|
243
|
+
json.one_post partial: ["posts/blog_post", locals: {post: @post}] do
|
244
|
+
end
|
245
|
+
```
|
246
|
+
|
247
|
+
Usage with arrays:
|
248
|
+
|
249
|
+
```ruby
|
250
|
+
# as an option on an array. The `as:` option is supported when using `array!`
|
251
|
+
json.posts do
|
252
|
+
json.array! @posts, partial: ["posts/blog_post", locals: {foo: 'bar'}, as: 'post'] do
|
253
|
+
end
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
### Partial Fragments
|
258
|
+
|
259
|
+
A fragment uses a digest to identify a rendered partial across your page state in Redux. When BreezyJS recieves a payload with a fragment, it will update every fragment with the same digest in your Redux store.
|
260
|
+
|
261
|
+
You would need use partials and add the option `fragment: true`.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
# index.json.props
|
265
|
+
json.header partial: ["profile", fragment: true] do
|
266
|
+
end
|
267
|
+
|
268
|
+
# _profile.json.props
|
269
|
+
json.profile do
|
270
|
+
json.address do
|
271
|
+
json.state "New York City"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
```
|
275
|
+
|
276
|
+
When using fragments with Arrays, the argument **MUST** be a lamda:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
require 'props_template/core_ext' #See (lists)[#Lists]
|
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:
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
# index.js.breezy
|
288
|
+
json.header partial: ["profile", fragment: 'me_header'] do
|
289
|
+
end
|
290
|
+
```
|
291
|
+
|
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
|
+
### Caching
|
329
|
+
Caching is supported on any node.
|
330
|
+
|
331
|
+
Usage:
|
332
|
+
|
333
|
+
```ruby
|
334
|
+
json.author(cache: "some_cache_key") do
|
335
|
+
json.first_name "tommy"
|
336
|
+
end
|
337
|
+
|
338
|
+
#or
|
339
|
+
|
340
|
+
json.profile(cache: "cachekey", partial: ["profile", locals: {foo: 1}]) do
|
341
|
+
end
|
342
|
+
|
343
|
+
#or nest it
|
344
|
+
|
345
|
+
json.author(cache: "some_cache_key") do
|
346
|
+
json.address(cache: "some_other_cache_key") do
|
347
|
+
json.zip 11214
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
When used with arrays, PropsTemplate will use `Rails.cache.read_multi`.
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
require 'props_template/core_ext' #See (lists)[#Lists]
|
356
|
+
|
357
|
+
opts = {
|
358
|
+
cache: ->(i){ ['a', i] }
|
359
|
+
}
|
360
|
+
json.array! [4,5], opts do |x|
|
361
|
+
json.top "hello" + x.to_s
|
362
|
+
end
|
363
|
+
|
364
|
+
#or on arrays with partials
|
365
|
+
|
366
|
+
opts = {
|
367
|
+
cache: (->(d){ ['a', d.id] }),
|
368
|
+
partial: ["blog_post", as: :blog_post]
|
369
|
+
}
|
370
|
+
json.array! @options, opts
|
371
|
+
```
|
372
|
+
|
373
|
+
### Deferment
|
374
|
+
|
375
|
+
You can defer rendering of expensive nodes in your content tree using the `defer: :auto` option. Behind the scenes PropsTemplates will no-op the block entirely, replace the value with `{}` as a placeholder.
|
376
|
+
When the client recieves the payload, BreezyJS will use the meta data to issue a `remote` dispatch to fetch the missing node and immutibly graft it at the appropriate keypath in your Redux store.
|
377
|
+
|
378
|
+
You can access what was deferred with `json.deferred!`. If you use the generators, this will be set up in `application.json.props`.
|
379
|
+
|
380
|
+
Usage:
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
json.dashboard(defer: :auto) do
|
384
|
+
sleep 10
|
385
|
+
json.some_fancy_metric 42
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
389
|
+
A manual option is also available:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
json.dashboard(defer: :manual) do
|
393
|
+
sleep 10
|
394
|
+
json.some_fancy_metric 42
|
395
|
+
end
|
396
|
+
```
|
397
|
+
|
398
|
+
Finally in your `application.json.props`:
|
399
|
+
|
400
|
+
```ruby
|
401
|
+
json.defers json.deferred!
|
402
|
+
```
|
403
|
+
|
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
|
+
#### Working with arrays
|
408
|
+
The default behavior for deferements is to use the index of the collection to identify an element. PropsTemplate will generate `?_bzq=a.b.c.0.title` in its metadata.
|
409
|
+
|
410
|
+
If you wish to use an attribute to identify the element. You must:
|
411
|
+
1. Implement `:key` to specify which attribute you want to use to uniquely identify the element in the collection. PropsTemplate will generate `?_bzq=a.b.c.some_id=some_value.title`
|
412
|
+
2. Implement `member_at`, and `member_key` on the collection to allow for BreezyJS to traverse the tree based on key value attributes.
|
413
|
+
|
414
|
+
For example:
|
415
|
+
|
416
|
+
```ruby
|
417
|
+
require 'props_template/core_ext' #See (lists)[#Lists]
|
418
|
+
|
419
|
+
data = [{id: 1, name: 'foo'}, {id: 2, name: 'bar'}]
|
420
|
+
|
421
|
+
json.posts
|
422
|
+
json.array! data, key: :some_id do |item|
|
423
|
+
json.contact(defer: :auto) do
|
424
|
+
json.address '123 example drive'
|
425
|
+
end
|
426
|
+
|
427
|
+
# json.some_id item.some_id will be appended automatically to the end of the block
|
428
|
+
end
|
429
|
+
end
|
430
|
+
```
|
431
|
+
|
432
|
+
When BreezyJS receives the response, it will automatically kick off `remote(?bzq=posts.some_id=1.contact)` and `remote(?bzq=posts.some_id=2.contact)`.
|
433
|
+
|
434
|
+
# Traversing
|
435
|
+
|
436
|
+
PropsTemplate has the ability to walk the tree you build, skipping execution of untargeted nodes. This feature is useful for partial updating your frontend state. See [traversing nodes](react-redux.md#traversing-nodes)
|
437
|
+
|
438
|
+
```ruby
|
439
|
+
traversal_path = ['data', 'details', 'personal']
|
440
|
+
|
441
|
+
json.data(search: traversal_path) do
|
442
|
+
json.details do
|
443
|
+
json.employment do
|
444
|
+
...more stuff...
|
445
|
+
end
|
446
|
+
|
447
|
+
json.personal do
|
448
|
+
json.name 'james'
|
449
|
+
json.zip_code 91210
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
json.footer do
|
455
|
+
...
|
456
|
+
end
|
457
|
+
```
|
458
|
+
|
459
|
+
PropsTemplate will will walk breath first, finds the matching key, executes the associated block, then repeats until it the node is found. The above will output the below:
|
460
|
+
|
461
|
+
```json
|
462
|
+
{
|
463
|
+
data: {
|
464
|
+
name: 'james',
|
465
|
+
zipCode: 91210
|
466
|
+
},
|
467
|
+
footer: {
|
468
|
+
....
|
469
|
+
}
|
470
|
+
}
|
471
|
+
```
|
472
|
+
|
473
|
+
Breezy's searching only works with blocks, and will NOT work with Scalars ("leaf" values). For example:
|
474
|
+
|
475
|
+
```ruby
|
476
|
+
traversal_path = ['data', 'details', 'personal', 'name'] <- not found
|
477
|
+
|
478
|
+
json.data(search: traversal_path) do
|
479
|
+
json.details do
|
480
|
+
json.personal do
|
481
|
+
json.name 'james'
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
```
|
487
|
+
|
488
|
+
## Nodes that do not exist
|
489
|
+
|
490
|
+
Nodes that are not found will not define the key where search was enabled on.
|
491
|
+
|
492
|
+
```ruby
|
493
|
+
traversal_path = ['data', 'details', 'does_not_exist']
|
494
|
+
|
495
|
+
json.data(search: traversal_path) do
|
496
|
+
json.details do
|
497
|
+
json.personal do
|
498
|
+
json.name 'james'
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
json.footer do
|
504
|
+
...
|
505
|
+
end
|
506
|
+
|
507
|
+
```
|
508
|
+
|
509
|
+
The above will render:
|
510
|
+
|
511
|
+
```
|
512
|
+
{
|
513
|
+
footer: {
|
514
|
+
...
|
515
|
+
}
|
516
|
+
}
|
517
|
+
```
|
@@ -78,23 +78,15 @@ module Props
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def handle_collection_item(collection, item, index, options)
|
81
|
-
if
|
82
|
-
member_key = collection.member_key
|
83
|
-
end
|
84
|
-
|
85
|
-
if !member_key
|
81
|
+
if !options[:key]
|
86
82
|
@traveled_path.push(index)
|
87
83
|
else
|
88
|
-
id =
|
89
|
-
item.send(member_key)
|
90
|
-
elsif item.is_a? Hash
|
91
|
-
item[member_key] || item[member_key.to_sym]
|
92
|
-
end
|
84
|
+
id, val = options[:key]
|
93
85
|
|
94
86
|
if id.nil?
|
95
87
|
@traveled_path.push(index)
|
96
88
|
else
|
97
|
-
@traveled_path.push("#{
|
89
|
+
@traveled_path.push("#{id.to_s}=#{val}")
|
98
90
|
end
|
99
91
|
end
|
100
92
|
|
@@ -110,6 +102,16 @@ module Props
|
|
110
102
|
|
111
103
|
|
112
104
|
def refine_item_options(item, options)
|
105
|
+
if key = options[:key]
|
106
|
+
val = if item.respond_to? key
|
107
|
+
item.send(key)
|
108
|
+
elsif item.is_a? Hash
|
109
|
+
item[key] || item[key.to_sym]
|
110
|
+
end
|
111
|
+
|
112
|
+
options[:key] = [options[:key], val]
|
113
|
+
end
|
114
|
+
|
113
115
|
@em.refine_options(options, item)
|
114
116
|
end
|
115
117
|
end
|
@@ -43,7 +43,7 @@ module Props
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def has_extensions(options)
|
46
|
-
options[:defer] || options[:cache] || options[:partial]
|
46
|
+
options[:defer] || options[:cache] || options[:partial] || options[:key]
|
47
47
|
end
|
48
48
|
|
49
49
|
def handle(commands, options)
|
@@ -64,6 +64,11 @@ module Props
|
|
64
64
|
else
|
65
65
|
yield
|
66
66
|
end
|
67
|
+
|
68
|
+
if options[:key]
|
69
|
+
id, val = options[:key]
|
70
|
+
base.set!(id, val)
|
71
|
+
end
|
67
72
|
end
|
68
73
|
end
|
69
74
|
end
|
@@ -35,6 +35,12 @@ module Props
|
|
35
35
|
type, rest = options[:defer]
|
36
36
|
placeholder = rest[:placeholder]
|
37
37
|
|
38
|
+
if type.to_sym == :auto && options[:key]
|
39
|
+
key, val = options[:key]
|
40
|
+
placeholder = {}
|
41
|
+
placeholder[key] = val
|
42
|
+
end
|
43
|
+
|
38
44
|
request_path = @base.context.controller.request.fullpath
|
39
45
|
path = @base.traveled_path.join('.')
|
40
46
|
uri = ::URI.parse(request_path)
|
@@ -18,9 +18,9 @@ module Props
|
|
18
18
|
def render_props_template(view, template, path, locals)
|
19
19
|
layout_locals = locals.dup
|
20
20
|
layout_locals.delete(:json)
|
21
|
+
layout_locals[:virtual_path_of_template] = template.virtual_path
|
21
22
|
|
22
|
-
layout = resolve_props_layout(path, layout_locals, [formats.first])
|
23
|
-
|
23
|
+
layout = resolve_props_layout(path, layout_locals.keys, [formats.first])
|
24
24
|
body = layout.render(view, layout_locals) do |json|
|
25
25
|
locals[:json] = json
|
26
26
|
template.render(view, locals)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: props_template
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Johny Ho
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -72,6 +72,7 @@ executables: []
|
|
72
72
|
extensions: []
|
73
73
|
extra_rdoc_files: []
|
74
74
|
files:
|
75
|
+
- README.md
|
75
76
|
- lib/props_template.rb
|
76
77
|
- lib/props_template/base.rb
|
77
78
|
- lib/props_template/base_with_extensions.rb
|