props_template 0.35.0 → 0.37.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: 8400f382bf1e777d3b2a863228bfec9486388f1f3ef77e127cbc1220a9917e32
4
- data.tar.gz: 5847aae3d52a018f4c31e437a46147882da8c6cf35d38bc282fb068bc4b3b678
3
+ metadata.gz: ecdcee486c14966bbb3259a5b213631b3cd29f14a31a7194a8e9aad1ade31398
4
+ data.tar.gz: c33d2129e1aa5ba580003c834f828bc48c820120e4ffa66f9eebfc1531fed7a0
5
5
  SHA512:
6
- metadata.gz: d730232224bfea323784f896dd51f4f00025cf922ee5e5791e767520b852b0af8b717bd787183cd02a30a985ee9821d8b19eddf362adf64225d2959ca22dfa9f
7
- data.tar.gz: 29c1a6a96ebc9fca218d7aaea1064d5b916e494dae534c11b0572c06f8d1543b8c0f5c651d1df00144fa372546a6992058d9c73dd810a8d57c78bacc2e078fb1
6
+ metadata.gz: 6f11a45290e1c10aa7b526506f305b723928e8414117a6aac637dbdbd95d09ae414cb18ff5c8d74a249cf12bf85f55af5d2d9f83f7846c2c03690fde5d4990bc
7
+ data.tar.gz: bc476b0780f7448883bd7dfe8316ac837c851d7255420eeb8496730285d68404b64dcf94a45e856acbaefa9788c76d7f1b70722a61316129e9751a16250e8d7c
data/README.md CHANGED
@@ -72,7 +72,7 @@ gem 'props_template'
72
72
  and run `bundle`.
73
73
 
74
74
  Optionally add the [core ext](#array-core-extension) to an initializer if you
75
- want to [dig](#traversing) into your templates.
75
+ want to [dig](#digging) into your templates.
76
76
 
77
77
  ```ruby
78
78
  require 'props_template/core_ext'
@@ -93,8 +93,7 @@ You can also add a [layout](#layouts).
93
93
 
94
94
  ### json.set! or json.\<your key here\>
95
95
 
96
- Defines the attribute or structure. All keys are automatically camelized lower
97
- by default. See [Change Key Format](#change-key-format) to change this behavior.
96
+ Defines the attribute or structure. All keys are not formatted by default. See [Change Key Format](#change-key-format) to change this behavior.
98
97
 
99
98
  ```ruby
100
99
  json.set! :authorDetails, {...options} do
@@ -153,9 +152,36 @@ The difference between the block form and inline form is
153
152
  1. The block form is an internal node. Functionality such as Partials,
154
153
  Deferment and other [options](#options) are only available on the
155
154
  block form.
156
- 2. The inline form is considered a leaf node, and you can only [search](#traversing)
155
+ 2. The inline form is considered a leaf node, and you can only [dig](#digging)
157
156
  for internal nodes.
158
157
 
158
+ ### json.extract!
159
+ Extracts attributes from object or hash in 1 line
160
+
161
+ ```ruby
162
+ # without extract!
163
+ json.id user.id
164
+ json.email user.email
165
+ json.firstName user.first_name
166
+
167
+ # with extract!
168
+ json.extract! user, :id, :email, :first_name
169
+
170
+ # => {"id" => 1, "email" => "email@gmail.com", "first_name" => "user"}
171
+
172
+ # with extract! with key transformation
173
+ json.extract! user, :id, [:first_name, :firstName], [:last_name, :lastName]
174
+
175
+ # => {"id" => 1, "firstName" => "user", "lastName" => "last"}
176
+ ```
177
+
178
+ The inline form defines object and attributes
179
+
180
+ | Parameter | Notes |
181
+ | :--- | :--- |
182
+ | object | An object |
183
+ | attributes | A list of attributes |
184
+
159
185
  ### json.array!
160
186
  Generates an array of json objects.
161
187
 
@@ -176,7 +202,7 @@ end
176
202
  | collection | A collection that responds to `member_at` and `member_by` |
177
203
  | options | Additional [options](#options)|
178
204
 
179
- To support [traversing nodes](#traversing), any list passed
205
+ To support [digging](#digging), any list passed
180
206
  to `array!` MUST implement `member_at(index)` and `member_by(attr, value)`.
181
207
 
182
208
  For example, if you were using a delegate:
@@ -250,7 +276,7 @@ end
250
276
  ```
251
277
 
252
278
  PropsTemplate does not know what the elements are in your collection. The
253
- example above will be fine for [traversing](#traversing)
279
+ example above will be fine for [digging](#digging)
254
280
  by index, but will raise a `NotImplementedError` if you query by attribute. You
255
281
  may still need to implement `member_by`.
256
282
 
@@ -291,7 +317,7 @@ end
291
317
  ### Partials
292
318
 
293
319
  Partials are supported. The following will render the file
294
- `views/posts/_blog_posts.json.props`, and set a local variable `foo` assigned
320
+ `views/posts/_blog_posts.json.props`, and set a local variable `post` assigned
295
321
  with @post, which you can use inside the partial.
296
322
 
297
323
  ```ruby
@@ -303,6 +329,7 @@ Usage with arrays:
303
329
 
304
330
  ```ruby
305
331
  # The `as:` option is supported when using `array!`
332
+ # Without `as:` option you can use blog_post variable (name is based on partial's name) inside partial
306
333
 
307
334
  json.posts do
308
335
  json.array! @posts, partial: ["posts/blog_post", locals: {foo: 'bar'}, as: 'post'] do
@@ -310,6 +337,35 @@ json.posts do
310
337
  end
311
338
  ```
312
339
 
340
+ Rendering partials without a key is also supported using `json.partial!`, but use
341
+ sparingly! `json.partial!` is not optimized for collection rendering and may
342
+ cause performance problems. Its best used for things like a shared header or footer.
343
+
344
+ Do:
345
+
346
+ ```ruby
347
+ json.partial! partial: "header", locals: {user: @user} do
348
+ end
349
+ ```
350
+
351
+ or
352
+
353
+ ```ruby
354
+ json.posts do
355
+ json.array! @posts, partial: ["posts/blog_post", locals: {post: @post}] do
356
+ end
357
+ end
358
+ ```
359
+
360
+ Do NOT:
361
+
362
+ ```
363
+ @post.each do |post|
364
+ json.partial! partial: "post", locals: {post: @post} do
365
+ end
366
+ end
367
+ ```
368
+
313
369
  ### Partial Fragments
314
370
  **Note** This is a [SuperglueJS][1] specific functionality.
315
371
 
@@ -391,7 +447,7 @@ entirely and replace the value with a placeholder. A common use case would be
391
447
  tabbed content that does not load until you click the tab.
392
448
 
393
449
  When your client receives the payload, you may issue a second request to the
394
- same endpoint to fetch any missing nodes. See [traversing nodes](#traversing)
450
+ same endpoint to fetch any missing nodes. See [digging](#digging)
395
451
 
396
452
  There is also an `defer: :auto` option that you can use with [SuperglueJS][1]. [SuperglueJS][1]
397
453
  will use the metadata from `json.deferred!` to issue a `remote` dispatch to fetch
@@ -446,7 +502,7 @@ your collection item, and is used for `defer: :auto` to generate a keypath for
446
502
  [SuperglueJS][1]. If you are NOT using SuperglueJS, you do not need to do this.
447
503
 
448
504
  2. Implement `member_at`, on the [collection](#jsonarray). This will be called
449
- by PropsTemplate to when [searching nodes](#traversing)
505
+ by PropsTemplate to when [digging](#digging)
450
506
 
451
507
  For example:
452
508
 
@@ -472,7 +528,7 @@ end
472
528
  If you are using [SuperglueJS][1], SuperglueJS will, it will automatically kick off
473
529
  `remote(?props_at=posts.some_id=1.contact)` and `remote(?props_at=posts.some_id=2.contact)`.
474
530
 
475
- ## Traversing
531
+ ## Digging
476
532
 
477
533
  PropsTemplate has the ability to walk the tree you build, skipping execution of
478
534
  untargeted nodes. This feature is useful for selectively updating your frontend
@@ -481,7 +537,7 @@ state.
481
537
  ```ruby
482
538
  traversal_path = ['data', 'details', 'personal']
483
539
 
484
- json.data(search: traversal_path) do
540
+ json.data(dig: traversal_path) do
485
541
  json.details do
486
542
  json.employment do
487
543
  ...more stuff
@@ -515,13 +571,13 @@ The above will output:
515
571
  }
516
572
  ```
517
573
 
518
- Searching only works with blocks, and will NOT work with Scalars
574
+ Digging only works with blocks, and will NOT work with Scalars
519
575
  ("leaf" values). For example:
520
576
 
521
577
  ```ruby
522
578
  traversal_path = ['data', 'details', 'personal', 'name'] <- not found
523
579
 
524
- json.data(search: traversal_path) do
580
+ json.data(dig: traversal_path) do
525
581
  json.details do
526
582
  json.personal do
527
583
  json.name 'james'
@@ -532,12 +588,12 @@ end
532
588
 
533
589
  ## Nodes that do not exist
534
590
 
535
- Nodes that are not found will remove the branch where search was enabled on.
591
+ Nodes that are not found will remove the branch where digging was enabled on.
536
592
 
537
593
  ```ruby
538
594
  traversal_path = ['data', 'details', 'does_not_exist']
539
595
 
540
- json.data(search: traversal_path) do
596
+ json.data(dig: traversal_path) do
541
597
  json.details do
542
598
  json.personal do
543
599
  json.name 'james'
@@ -585,15 +641,60 @@ json.flash flash.to_h
585
641
  will render Layout first, then the template when `yield json` is used.
586
642
 
587
643
  ## Change key format
588
- By default, keys are not formatted. If you want to change this behavior,
589
- override it in an initializer:
644
+ By default, keys are not formatted. This is intentional. By being explicity with your keys,
645
+ it makes your views quicker and more easily diggable when working in Javascript land.
646
+
647
+ If you must change this behavior, override it in an initializer and cache the value:
590
648
 
591
649
  ```ruby
650
+ # default behavior
592
651
  Props::BaseWithExtensions.class_eval do
652
+ # json.firstValue "first"
653
+ # json.second_value "second"
654
+ #
655
+ # -> { "firstValue" => "first", "second_value" => "second" }
593
656
  def key_format(key)
594
657
  key.to_s
595
658
  end
596
659
  end
660
+
661
+ # camelCased behavior
662
+ Props::BaseWithExtensions.class_eval do
663
+ # json.firstValue "first"
664
+ # json.second_value "second"
665
+ #
666
+ # -> { "firstValue" => "first", "secondValue" => "second" }
667
+ def key_format(key)
668
+ @key_cache ||= {}
669
+ @key_cache[key] ||= key.to_s.camelize(:lower)
670
+ @key_cache[key]
671
+ end
672
+
673
+ def result!
674
+ result = super
675
+ @key_cache = {}
676
+ result
677
+ end
678
+ end
679
+
680
+ # snake_cased behavior
681
+ Props::BaseWithExtensions.class_eval do
682
+ # json.firstValue "first"
683
+ # json.second_value "second"
684
+ #
685
+ # -> { "first_value" => "first", "second_value" => "second" }
686
+ def key_format(key)
687
+ @key_cache ||= {}
688
+ @key_cache[key] ||= key.to_s.underscore
689
+ @key_cache[key]
690
+ end
691
+
692
+ def result!
693
+ result = super
694
+ @key_cache = {}
695
+ result
696
+ end
697
+ end
597
698
  ```
598
699
 
599
700
  ## Escape mode
@@ -105,6 +105,32 @@ module Props
105
105
  nil
106
106
  end
107
107
 
108
+ def partial!(**options)
109
+ @context.render options
110
+ end
111
+
112
+ # json.id item.id
113
+ # json.value item.value
114
+ #
115
+ # json.extract! item, :id, :value
116
+ #
117
+ # with key transformation
118
+ # json.extract! item, :id, [:first_name, :firstName]
119
+ def extract!(object, *values)
120
+ values.each do |value|
121
+ key, attribute = if value.is_a?(Array)
122
+ [value[1], value[0]]
123
+ else
124
+ [value, value]
125
+ end
126
+
127
+ set!(
128
+ key,
129
+ object.is_a?(Hash) ? object.fetch(attribute) : object.public_send(attribute)
130
+ )
131
+ end
132
+ end
133
+
108
134
  def result!
109
135
  if @scope.nil?
110
136
  @stream.push_object
@@ -82,7 +82,7 @@ module Props
82
82
  pass_opts[:locals] ||= {}
83
83
  pass_opts[:partial] = partial
84
84
  pass_opts[:formats] = [:json]
85
- pass_opts.delete(:handlers)
85
+ pass_opts[:handlers] = [:props]
86
86
 
87
87
  if !(String === partial)
88
88
  raise ArgumentError.new(INVALID_PARTIAL_MESSAGE % partial.inspect)
@@ -1,3 +1,3 @@
1
1
  module Props
2
- VERSION = "0.35.0".freeze
2
+ VERSION = "0.37.0".freeze
3
3
  end
@@ -22,6 +22,8 @@ module Props
22
22
  self.template_lookup_options = {handlers: [:props]}
23
23
 
24
24
  delegate :result!, :array!,
25
+ :partial!,
26
+ :extract!,
25
27
  :deferred!,
26
28
  :fragments!,
27
29
  :set_block_content!,
@@ -34,11 +36,15 @@ module Props
34
36
  end
35
37
 
36
38
  def set!(key, options = {}, &block)
37
- if block && options[:search] && !@builder.is_a?(Searcher)
39
+ if block && (options[:search] || options[:dig]) && !@builder.is_a?(Searcher)
40
+ search = options[:search] || options[:dig]
38
41
 
39
42
  prev_builder = @builder
40
- @builder = Searcher.new(self, options[:search], @context)
43
+ @builder = Searcher.new(self, search, @context)
44
+
41
45
  options.delete(:search)
46
+ options.delete(:dig)
47
+
42
48
  @builder.set!(key, options, &block)
43
49
  found_block, found_options = @builder.found!
44
50
  @builder = prev_builder
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: props_template
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.35.0
4
+ version: 0.37.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johny Ho
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-07-20 00:00:00.000000000 Z
10
+ date: 2024-12-31 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,40 +15,52 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 6.1.0
18
+ version: '7.0'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9.0'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
23
25
  requirements:
24
26
  - - ">="
25
27
  - !ruby/object:Gem::Version
26
- version: 6.1.0
28
+ version: '7.0'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9.0'
27
32
  - !ruby/object:Gem::Dependency
28
33
  name: actionview
29
34
  requirement: !ruby/object:Gem::Requirement
30
35
  requirements:
31
36
  - - ">="
32
37
  - !ruby/object:Gem::Version
33
- version: 6.1.0
38
+ version: '7.0'
39
+ - - "<"
40
+ - !ruby/object:Gem::Version
41
+ version: '9.0'
34
42
  type: :runtime
35
43
  prerelease: false
36
44
  version_requirements: !ruby/object:Gem::Requirement
37
45
  requirements:
38
46
  - - ">="
39
47
  - !ruby/object:Gem::Version
40
- version: 6.1.0
48
+ version: '7.0'
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '9.0'
41
52
  - !ruby/object:Gem::Dependency
42
53
  name: oj
43
54
  requirement: !ruby/object:Gem::Requirement
44
55
  requirements:
45
- - - ">="
56
+ - - "~>"
46
57
  - !ruby/object:Gem::Version
47
58
  version: '3.9'
48
59
  type: :runtime
49
60
  prerelease: false
50
61
  version_requirements: !ruby/object:Gem::Requirement
51
62
  requirements:
52
- - - ">="
63
+ - - "~>"
53
64
  - !ruby/object:Gem::Version
54
65
  version: '3.9'
55
66
  description: PropsTemplate is a direct-to-Oj, JBuilder-like DSL for building JSON.
@@ -80,7 +91,6 @@ homepage: https://github.com/thoughtbot/props_template/
80
91
  licenses:
81
92
  - MIT
82
93
  metadata: {}
83
- post_install_message:
84
94
  rdoc_options: []
85
95
  require_paths:
86
96
  - lib
@@ -95,8 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
95
105
  - !ruby/object:Gem::Version
96
106
  version: '0'
97
107
  requirements: []
98
- rubygems_version: 3.1.6
99
- signing_key:
108
+ rubygems_version: 3.6.2
100
109
  specification_version: 4
101
110
  summary: A fast JSON builder
102
111
  test_files: []