props_template 1.0.2 → 1.1.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 +90 -24
- data/lib/props_template/base.rb +17 -2
- data/lib/props_template/base_with_extensions.rb +1 -5
- data/lib/props_template/searcher.rb +4 -0
- data/lib/props_template/version.rb +1 -1
- data/lib/props_template.rb +3 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3dc53f3b47d33c98b7fc04e82ef1bcc711529a30c5631627ba2bbd55eeb47ded
|
|
4
|
+
data.tar.gz: 4c13c7771bfc1ef0d2a2848b0c24d03f029f47751625f18151c23702d0a42376
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c08a1f263224c6b4902ac849d7bded7b8e22d4520f3520a82cf7eac231f645ee9109dc8ac95649e2b8f5695c369f71868687482802113712daa3b18cd261bfbe
|
|
7
|
+
data.tar.gz: 6b3b68733f7b740be73438aff7602d30919798aa2d7693bc36437b096ff1e88a4414d4e93618ca90a74627897f96bc8ec781f0e84718b79a5ac925096a829900
|
data/README.md
CHANGED
|
@@ -5,7 +5,7 @@ support for Russian-Doll caching, layouts, and can be queried by giving the
|
|
|
5
5
|
root a key path.
|
|
6
6
|
|
|
7
7
|
[](https://github.com/thoughtbot/props_template/actions/workflows/build.yml)
|
|
9
9
|
|
|
10
10
|
It's fast.
|
|
11
11
|
|
|
@@ -98,7 +98,7 @@ json.posts do
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
json.paginationPath posts_path
|
|
101
|
-
json.current
|
|
101
|
+
json.current paged_posts.current_page
|
|
102
102
|
json.total @posts.count
|
|
103
103
|
end
|
|
104
104
|
|
|
@@ -131,6 +131,12 @@ json.greetings "hello world"
|
|
|
131
131
|
|
|
132
132
|
You can also add a [layout](#layouts).
|
|
133
133
|
|
|
134
|
+
To generate `index` and `show` templates for a resource, use the generator:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
rails g props:views Post title:string body:text
|
|
138
|
+
```
|
|
139
|
+
|
|
134
140
|
## API
|
|
135
141
|
|
|
136
142
|
### json.set! or json.\<your key here\>
|
|
@@ -144,13 +150,13 @@ end
|
|
|
144
150
|
|
|
145
151
|
# or
|
|
146
152
|
|
|
147
|
-
json.authorDetails
|
|
153
|
+
json.authorDetails(with.some_option) do
|
|
148
154
|
json.firstName 'David'
|
|
149
155
|
end
|
|
150
156
|
|
|
151
157
|
# or with hash options
|
|
152
158
|
|
|
153
|
-
json.authorDetails
|
|
159
|
+
json.authorDetails({...options}) do
|
|
154
160
|
json.firstName 'David'
|
|
155
161
|
end
|
|
156
162
|
|
|
@@ -163,7 +169,7 @@ The inline form defines key and value
|
|
|
163
169
|
| Parameter | Notes |
|
|
164
170
|
| :--- | :--- |
|
|
165
171
|
| key | A json object key|
|
|
166
|
-
| value | A value or an [options]
|
|
172
|
+
| value | A value or an [options](#options) object enabled with [partial](#partials)|
|
|
167
173
|
|
|
168
174
|
```ruby
|
|
169
175
|
|
|
@@ -268,7 +274,7 @@ class ObjectCollection < SimpleDelegator
|
|
|
268
274
|
|
|
269
275
|
def member_by(attr, val)
|
|
270
276
|
find do |ele|
|
|
271
|
-
ele[attr] == val
|
|
277
|
+
ele[attr.to_sym] == val
|
|
272
278
|
end
|
|
273
279
|
end
|
|
274
280
|
end
|
|
@@ -296,7 +302,7 @@ class ApplicationRecord < ActiveRecord::Base
|
|
|
296
302
|
end
|
|
297
303
|
|
|
298
304
|
def self.member_by(attr, value)
|
|
299
|
-
find_by(Hash[attr,
|
|
305
|
+
find_by(Hash[attr, value])
|
|
300
306
|
end
|
|
301
307
|
end
|
|
302
308
|
```
|
|
@@ -309,6 +315,30 @@ json.array! Post.all do
|
|
|
309
315
|
end
|
|
310
316
|
```
|
|
311
317
|
|
|
318
|
+
#### json.child!
|
|
319
|
+
|
|
320
|
+
To build an array of heterogeneous elements, call `json.array!` without a
|
|
321
|
+
collection and add each element with `json.child!`:
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
json.items do
|
|
325
|
+
json.array! do
|
|
326
|
+
json.child! do
|
|
327
|
+
json.title "first"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
json.child! do
|
|
331
|
+
json.body "second"
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# => {"items": [{"title": "first"}, {"body": "second"}]}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
`json.child!` can be [dug](#digging) into by index, e.g. `items.1`, but not by
|
|
340
|
+
attribute.
|
|
341
|
+
|
|
312
342
|
#### **Array core extension**
|
|
313
343
|
|
|
314
344
|
For convenience, PropsTemplate includes a core\_ext that adds these methods to
|
|
@@ -321,7 +351,7 @@ data = [
|
|
|
321
351
|
{id: 2, name: 'bar'}
|
|
322
352
|
]
|
|
323
353
|
|
|
324
|
-
json.posts
|
|
354
|
+
json.posts do
|
|
325
355
|
json.array! data do
|
|
326
356
|
# ...
|
|
327
357
|
end
|
|
@@ -337,11 +367,11 @@ may still need to implement `member_by`.
|
|
|
337
367
|
Returns all deferred nodes used by the [deferment](#deferment) option.
|
|
338
368
|
|
|
339
369
|
**Note** This is a [SuperglueJS][1] specific functionality and is used in
|
|
340
|
-
`application.json.props` when first running `rails superglue:install
|
|
370
|
+
`application.json.props` when first running `rails g superglue:install`
|
|
341
371
|
|
|
342
372
|
|
|
343
373
|
```ruby
|
|
344
|
-
json.
|
|
374
|
+
json.defers json.deferred!
|
|
345
375
|
|
|
346
376
|
# => [{url: '/some_url?props_at=outer.inner', path: 'outer.inner', type: 'auto'}]
|
|
347
377
|
```
|
|
@@ -353,10 +383,12 @@ to fetch missing data in a second round trip.
|
|
|
353
383
|
Returns all fragment nodes used by the [partial fragments](#partial-fragments)
|
|
354
384
|
option.
|
|
355
385
|
|
|
356
|
-
```ruby
|
|
386
|
+
```ruby
|
|
387
|
+
json.fragments json.fragments!
|
|
388
|
+
```
|
|
357
389
|
|
|
358
390
|
**Note** This is a [SuperglueJS][1] specific functionality and is used in
|
|
359
|
-
`application.json.props` when first running `rails superglue:install
|
|
391
|
+
`application.json.props` when first running `rails g superglue:install`
|
|
360
392
|
|
|
361
393
|
## Options
|
|
362
394
|
|
|
@@ -366,13 +398,13 @@ functionality such as Partials, Deferments, and Caching
|
|
|
366
398
|
The following are equivalent:
|
|
367
399
|
|
|
368
400
|
```ruby
|
|
369
|
-
json.post(with.partial('blog_post')
|
|
401
|
+
json.post(with.partial('blog_post'))
|
|
370
402
|
```
|
|
371
403
|
|
|
372
404
|
or
|
|
373
405
|
|
|
374
406
|
```ruby
|
|
375
|
-
json.post(Props::Options.new.partial('blog_post')
|
|
407
|
+
json.post(Props::Options.new.partial('blog_post'))
|
|
376
408
|
```
|
|
377
409
|
### Hash options
|
|
378
410
|
|
|
@@ -388,7 +420,7 @@ end
|
|
|
388
420
|
### Partials
|
|
389
421
|
|
|
390
422
|
Partials are supported. The following will render the file
|
|
391
|
-
`views/posts/
|
|
423
|
+
`views/posts/_blog_post.json.props`, and set a local variable `post` assigned
|
|
392
424
|
with @post, which you can use inside the partial.
|
|
393
425
|
|
|
394
426
|
```ruby
|
|
@@ -456,12 +488,14 @@ json.profile do
|
|
|
456
488
|
end
|
|
457
489
|
```
|
|
458
490
|
|
|
459
|
-
When using fragments with Arrays, the argument
|
|
491
|
+
When using fragments with Arrays, the argument can be a String, a Symbol, or a
|
|
492
|
+
lambda that receives each item and returns the fragment name. Return `nil` to
|
|
493
|
+
skip the fragment for that item:
|
|
460
494
|
|
|
461
495
|
```ruby
|
|
462
496
|
require 'props_template/core_ext'
|
|
463
497
|
|
|
464
|
-
json.array! ['foo', 'bar'], with.partial("footer").fragment(->(x){ x
|
|
498
|
+
json.array! ['foo', 'bar'], with.partial("footer").fragment(->(x){ "footer_#{x}" })
|
|
465
499
|
```
|
|
466
500
|
|
|
467
501
|
### Caching
|
|
@@ -557,6 +591,19 @@ Finally in your `application.json.props`:
|
|
|
557
591
|
json.defers json.deferred!
|
|
558
592
|
```
|
|
559
593
|
|
|
594
|
+
To render every deferred node in place (e.g. for a request that should get
|
|
595
|
+
the full page), call `json.disable_deferments!` before the deferred nodes:
|
|
596
|
+
|
|
597
|
+
```ruby
|
|
598
|
+
json.disable_deferments!
|
|
599
|
+
|
|
600
|
+
json.dashboard(with.defer(:auto)) do
|
|
601
|
+
json.someFancyMetric 42
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# => {"dashboard": {"someFancyMetric": 42}}
|
|
605
|
+
```
|
|
606
|
+
|
|
560
607
|
#### Working with arrays
|
|
561
608
|
The default behavior for deferments is to use the index of the collection to
|
|
562
609
|
identify an element.
|
|
@@ -570,8 +617,10 @@ If you wish to use an attribute to identify the element. You must:
|
|
|
570
617
|
your collection item, and is used for `defer: :auto` to generate a keypath for
|
|
571
618
|
[SuperglueJS][1]. If you are NOT using SuperglueJS, you do not need to do this.
|
|
572
619
|
|
|
573
|
-
2. Implement `
|
|
574
|
-
by PropsTemplate
|
|
620
|
+
2. Implement `member_by(attr, value)` on the [collection](#jsonarray). This will
|
|
621
|
+
be called by PropsTemplate when [digging](#digging) with a path like
|
|
622
|
+
`posts.id=1`. Note that `attr` is passed as a String and `value` is coerced
|
|
623
|
+
with `to_i`, so only integer attribute values are supported.
|
|
575
624
|
|
|
576
625
|
For example:
|
|
577
626
|
|
|
@@ -582,7 +631,7 @@ data = [
|
|
|
582
631
|
{id: 2, name: 'bar'}
|
|
583
632
|
]
|
|
584
633
|
|
|
585
|
-
json.posts
|
|
634
|
+
json.posts do
|
|
586
635
|
json.array! data, with.id_key(:some_id) do |item|
|
|
587
636
|
# By using :key, props_template will append `json.some_id item.some_id`
|
|
588
637
|
# automatically
|
|
@@ -686,7 +735,7 @@ The above will render:
|
|
|
686
735
|
```
|
|
687
736
|
|
|
688
737
|
## Layouts
|
|
689
|
-
|
|
738
|
+
To use a layout, create an `application.json.props` in
|
|
690
739
|
`app/views/layouts`. Here's an example:
|
|
691
740
|
|
|
692
741
|
```ruby
|
|
@@ -712,6 +761,23 @@ json.flash flash.to_h
|
|
|
712
761
|
**NOTE** PropsTemplate inverts the usual Rails rendering flow. PropsTemplate
|
|
713
762
|
will render Layout first, then the template when `yield json` is used.
|
|
714
763
|
|
|
764
|
+
### Partials with layouts
|
|
765
|
+
|
|
766
|
+
Partials can also be rendered with a layout. The layout is itself a partial:
|
|
767
|
+
|
|
768
|
+
```ruby
|
|
769
|
+
# app/views/posts/_stream_message.json.props
|
|
770
|
+
json.greeting "Hello world"
|
|
771
|
+
|
|
772
|
+
json.data do
|
|
773
|
+
yield
|
|
774
|
+
end
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
```ruby
|
|
778
|
+
render partial: "comment", layout: "stream_message"
|
|
779
|
+
```
|
|
780
|
+
|
|
715
781
|
|
|
716
782
|
### Layouts in API-only Rails apps
|
|
717
783
|
|
|
@@ -747,7 +813,7 @@ Props::BaseWithExtensions.class_eval do
|
|
|
747
813
|
# json.second_value "second"
|
|
748
814
|
#
|
|
749
815
|
# -> { "firstValue" => "first", "second_value" => "second" }
|
|
750
|
-
def
|
|
816
|
+
def format_key(key)
|
|
751
817
|
key.to_s
|
|
752
818
|
end
|
|
753
819
|
end
|
|
@@ -758,7 +824,7 @@ Props::BaseWithExtensions.class_eval do
|
|
|
758
824
|
# json.second_value "second"
|
|
759
825
|
#
|
|
760
826
|
# -> { "firstValue" => "first", "secondValue" => "second" }
|
|
761
|
-
def
|
|
827
|
+
def format_key(key)
|
|
762
828
|
@key_cache ||= {}
|
|
763
829
|
@key_cache[key] ||= key.to_s.camelize(:lower)
|
|
764
830
|
@key_cache[key]
|
|
@@ -777,7 +843,7 @@ Props::BaseWithExtensions.class_eval do
|
|
|
777
843
|
# json.second_value "second"
|
|
778
844
|
#
|
|
779
845
|
# -> { "first_value" => "first", "second_value" => "second" }
|
|
780
|
-
def
|
|
846
|
+
def format_key(key)
|
|
781
847
|
@key_cache ||= {}
|
|
782
848
|
@key_cache[key] ||= key.to_s.underscore
|
|
783
849
|
@key_cache[key]
|
data/lib/props_template/base.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Props
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def format_key(key)
|
|
39
|
-
key.to_s
|
|
39
|
+
(::Symbol === key) ? key.name : key.to_s
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def set!(key, value = nil)
|
|
@@ -63,6 +63,21 @@ module Props
|
|
|
63
63
|
nil
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def set_value!(key, value)
|
|
67
|
+
if @scope == :array
|
|
68
|
+
raise InvalidScopeForObjError.new("Attempted to set! on an array! scope")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if @scope.nil?
|
|
72
|
+
@scope = :object
|
|
73
|
+
@stream.push_object
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@stream.push_value(value, format_key(key))
|
|
77
|
+
|
|
78
|
+
nil
|
|
79
|
+
end
|
|
80
|
+
|
|
66
81
|
def handle_collection_item(collection, item, index, options)
|
|
67
82
|
@item_context = item
|
|
68
83
|
|
|
@@ -124,7 +139,7 @@ module Props
|
|
|
124
139
|
[value, value]
|
|
125
140
|
end
|
|
126
141
|
|
|
127
|
-
|
|
142
|
+
set_value!(
|
|
128
143
|
key,
|
|
129
144
|
object.is_a?(Hash) ? object.fetch(attribute) : object.public_send(attribute)
|
|
130
145
|
)
|
|
@@ -27,7 +27,7 @@ module Props
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def set_content!(options = {})
|
|
30
|
-
return super if !@em.has_extensions(options)
|
|
30
|
+
return super if options.empty? || !@em.has_extensions(options)
|
|
31
31
|
|
|
32
32
|
@em.handle(options, item_context) do
|
|
33
33
|
yield
|
|
@@ -46,10 +46,6 @@ module Props
|
|
|
46
46
|
@em = ExtensionManager.new(self, prev_state[1], prev_state[2])
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def format_key(key)
|
|
50
|
-
key.to_s
|
|
51
|
-
end
|
|
52
|
-
|
|
53
49
|
def set!(key, options = {}, &block)
|
|
54
50
|
if !block && options.is_a?(Props::Options)
|
|
55
51
|
options.valid_for_set!
|
data/lib/props_template.rb
CHANGED
|
@@ -67,8 +67,10 @@ module Props
|
|
|
67
67
|
@builder.item_context = found_item
|
|
68
68
|
set!(key, found_options, &found_block)
|
|
69
69
|
end
|
|
70
|
-
|
|
70
|
+
elsif block || options.is_a?(Props::Options)
|
|
71
71
|
@builder.set!(key, options, &block)
|
|
72
|
+
else
|
|
73
|
+
@builder.set_value!(key, options)
|
|
72
74
|
end
|
|
73
75
|
end
|
|
74
76
|
|