Skip to content

Commit eb008cb

Browse files
authored
Prop options (#60)
* Add support for Props::Options object. This addresses #56 by adding an options object. For simplicity and backward compatibility we inherit from Hash. Here's an example of usage: ``` json.note Props::Options.new .partial(“club_notes/notes”, locals: {club_note: @club_note} .fragment(“club_note_#{@club_note.id}”) ``` We also retain backward compat with the old syntax here: ``` json.note( partial: [ "club_notes/note", fragment: "club_note_#{@club_note.id}", locals: { club_note: @club_note } ] ) do end ``` * Add view helper props_options A small shorthand for Props::Options.new. Now we can do ``` json.note props_options .partial(“club_notes/notes”, locals: {club_note: @club_note} .fragment(“club_note_#{@club_note.id}”) ``` * Use with helper method I like that its a bit shorter.
1 parent 51bb252 commit eb008cb

19 files changed

+434
-63
lines changed

README.md

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ json.menu do
3636
end
3737
end
3838

39-
json.dashboard(defer: :auto) do
39+
json.dashboard(with.defer(:auto)) do
4040
sleep 5
4141
json.complexPostMetric 500
4242
end
@@ -46,7 +46,7 @@ json.posts do
4646
paged_posts = @posts.page(page_num).per(20)
4747

4848
json.list do
49-
json.array! paged_posts, key: :id do |post|
49+
json.array! paged_posts, with.id_key(:id) do |post|
5050
json.id post.id
5151
json.description post.description
5252
json.commentsCount post.comments.count
@@ -59,8 +59,7 @@ json.posts do
5959
json.total @posts.count
6060
end
6161

62-
json.footer partial: 'shared/footer' do
63-
end
62+
json.footer with.partial('shared/footer')
6463
```
6564

6665
## Installation
@@ -96,13 +95,13 @@ You can also add a [layout](#layouts).
9695
Defines the attribute or structure. All keys are not formatted by default. See [Change Key Format](#change-key-format) to change this behavior.
9796

9897
```ruby
99-
json.set! :authorDetails, {...options} do
98+
json.set! :authorDetails, with.some_option do
10099
json.set! :firstName, 'David'
101100
end
102101

103102
# or
104103

105-
json.authorDetails, {...options} do
104+
json.authorDetails, with.some_option do
106105
json.firstName 'David'
107106
end
108107

@@ -115,7 +114,7 @@ The inline form defines key and value
115114
| Parameter | Notes |
116115
| :--- | :--- |
117116
| key | A json object key|
118-
| value | A value |
117+
| value | A value or an [options][#options] object enabled with [partial](#partials)|
119118

120119
```ruby
121120

@@ -133,7 +132,7 @@ The block form defines key and structure
133132
| Parameter | Notes |
134133
| :--- | :--- |
135134
| key | A json object key|
136-
| options | Additional [options](#options)|
135+
| options | Additional hash [options](#options) or a [options](#options) object|
137136
| block | Additional `json.set!`s or `json.array!`s|
138137

139138
```ruby
@@ -149,11 +148,12 @@ end
149148
```
150149

151150
The difference between the block form and inline form is
152-
1. The block form is an internal node. Functionality such as Partials,
153-
Deferment and other [options](#options) are only available on the
154-
block form.
155-
2. The inline form is considered a leaf node, and you can only [dig](#digging)
156-
for internal nodes.
151+
152+
1. Passing options as a [hash](#hash-options) is only available for the
153+
block form (also called an internal node)
154+
2. The inline form is considered a leaf node, if you need features like
155+
partials you may use [options](#options) objects only.
156+
3. Only internal nodes may dug for [digging](#digging)
157157

158158
### json.extract!
159159
Extracts attributes from object or hash in 1 line
@@ -189,7 +189,7 @@ Generates an array of json objects.
189189
collection = [ {name: 'john'}, {name: 'jim'} ]
190190

191191
json.details do
192-
json.array! collection, {...options} do |person|
192+
json.array! collection, with.some_option do |person|
193193
json.firstName person[:name]
194194
end
195195
end
@@ -306,11 +306,29 @@ option.
306306
`application.json.props` when first running `rails superglue:install:web`
307307

308308
## Options
309-
Options Functionality such as Partials, Deferments, and Caching can only be
310-
set on a block. It is normal to see empty blocks.
309+
310+
PropsTemplate provides a `with` helper to build a `Props::Option` object that enable
311+
functionality such as Partials, Deferments, and Caching
312+
313+
The following are equivalent:
314+
315+
```ruby
316+
json.post(with.partial('blog_post')
317+
```
318+
319+
or
320+
321+
```ruby
322+
json.post(Props::Options.new.partial('blog_post')
323+
```
324+
### Hash options
325+
326+
Previously, a normal hash was supported for internal nodes as long as an empty
327+
block accompanied the option. This option is still supported, but its
328+
recommended to use the `with` option builder.
311329

312330
```ruby
313-
json.post(partial: 'blog_post') do
331+
json.post(partial: 'blog_post', locals: {post: @post}) do
314332
end
315333
```
316334

@@ -321,8 +339,7 @@ Partials are supported. The following will render the file
321339
with @post, which you can use inside the partial.
322340

323341
```ruby
324-
json.one_post partial: ["posts/blog_post", locals: {post: @post}] do
325-
end
342+
json.one_post(with.partial("posts/blog_post", locals: {post: @post}))
326343
```
327344

328345
Usage with arrays:
@@ -332,8 +349,7 @@ Usage with arrays:
332349
# Without `as:` option you can use blog_post variable (name is based on partial's name) inside partial
333350

334351
json.posts do
335-
json.array! @posts, partial: ["posts/blog_post", locals: {foo: 'bar'}, as: 'post'] do
336-
end
352+
json.array! @posts, with.partial("posts/blog_post", locals: {foo: 'bar'}, as: 'post')
337353
end
338354
```
339355

@@ -344,16 +360,14 @@ cause performance problems. It's best used for things like a shared header or fo
344360
Do:
345361

346362
```ruby
347-
json.partial! partial: "header", locals: {user: @user} do
348-
end
363+
json.partial! with.partial("header", locals: {user: @user})
349364
```
350365

351366
or
352367

353368
```ruby
354369
json.posts do
355-
json.array! @posts, partial: ["posts/blog_post", locals: {post: @post}] do
356-
end
370+
json.array! @posts, with.partial("posts/blog_post", locals: {post: @post})
357371
end
358372
```
359373

@@ -374,8 +388,7 @@ update cross cutting concerns like a header bar.
374388

375389
```ruby
376390
# index.json.props
377-
json.header partial: ["profile", fragment: "header"] do
378-
end
391+
json.header with.partial("profile").fragment("header")
379392

380393
# _profile.json.props
381394
json.profile do
@@ -390,8 +403,7 @@ When using fragments with Arrays, the argument **MUST** be a lamda:
390403
```ruby
391404
require 'props_template/core_ext'
392405

393-
json.array! ['foo', 'bar'], partial: ["footer", fragment: ->(x){ x == 'foo'}] do
394-
end
406+
json.array! ['foo', 'bar'], with.partial("footer").fragment(->(x){ x == 'foo'})
395407
```
396408

397409
### Caching
@@ -402,19 +414,18 @@ use [push_json](http://www.ohler.com/oj/doc/Oj/StringWriter.html#push_json-insta
402414
Usage:
403415

404416
```ruby
405-
json.author(cache: "some_cache_key") do
417+
json.author(with.cache("some_cache_key")) do
406418
json.firstName "tommy"
407419
end
408420

409421
# or
410422

411-
json.profile(cache: "cachekey", partial: ["profile", locals: {foo: 1}]) do
412-
end
423+
json.profile(with.cache("cachekey").partial("profile", locals: {foo: 1}))
413424

414425
# or nest it
415426

416-
json.author(cache: "some_cache_key") do
417-
json.address(cache: "some_other_cache_key") do
427+
json.author(with.cache("some_cache_key")) do
428+
json.address(with.cache("some_other_cache_key")) do
418429
json.zip 11214
419430
end
420431
end
@@ -425,15 +436,15 @@ When used with arrays, PropsTemplate will use `Rails.cache.read_multi`.
425436
```ruby
426437
require 'props_template/core_ext'
427438

428-
opts = { cache: ->(i){ ['a', i] } }
439+
opts = with.cache(->(i){ ['a', i] } )
429440

430441
json.array! [4,5], opts do |x|
431442
json.top "hello" + x.to_s
432443
end
433444

434445
# or on arrays with partials
435446

436-
opts = { cache: (->(d){ ['a', d.id] }), partial: ["blog_post", as: :blog_post] }
447+
opts = with.cache(->(d){ ['a', d.id] }).partial("blog_post", as: :blog_post)
437448

438449
json.array! @options, opts do
439450
end
@@ -457,15 +468,15 @@ store.
457468
Usage:
458469

459470
```ruby
460-
json.dashboard(defer: :manual) do
471+
json.dashboard(with.defer(:manual)) do
461472
sleep 10
462473
json.someFancyMetric 42
463474
end
464475

465476

466477
# or you can explicitly pass a placeholder
467478

468-
json.dashboard(defer: [:manual, placeholder: {}]) do
479+
json.dashboard(with.defer(:manual, placeholder: {})) do
469480
sleep 10
470481
json.someFancyMetric 42
471482
end
@@ -476,7 +487,7 @@ A auto option is available:
476487
**Note** This is a [SuperglueJS][1] specific functionality.
477488

478489
```ruby
479-
json.dashboard(defer: :auto) do
490+
json.dashboard(with.defer(:auto)) do
480491
sleep 10
481492
json.someFancyMetric 42
482493
end
@@ -514,11 +525,11 @@ data = [
514525
]
515526

516527
json.posts
517-
json.array! data, key: :some_id do |item|
528+
json.array! data, with.id_key(:some_id) do |item|
518529
# By using :key, props_template will append `json.some_id item.some_id`
519530
# automatically
520531

521-
json.contact(defer: :auto) do
532+
json.contact(with.defer(:auto)) do
522533
json.address '123 example drive'
523534
end
524535
end

lib/props_template.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
require "active_support/core_ext/array"
1010
require "props_template/searcher"
1111
require "props_template/handler"
12+
require "props_template/options"
13+
require "props_template/helper"
1214
require "props_template/version"
1315

1416
require "active_support"
@@ -28,7 +30,7 @@ class << self
2830
:deferred!,
2931
:fragments!,
3032
:disable_deferments!,
31-
:set_block_content!,
33+
:set_content!,
3234
:traveled_path!,
3335
:fragment_context!,
3436
to: :builder!

lib/props_template/base.rb

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def initialize(encoder = nil)
1414
@scope = nil
1515
end
1616

17-
def set_block_content!(options = {})
17+
def set_content!(options = {})
1818
@scope = nil
1919
yield
2020
if @scope.nil?
@@ -26,7 +26,7 @@ def set_block_content!(options = {})
2626
def handle_set_block(key, options)
2727
key = format_key(key)
2828
@stream.push_key(key)
29-
set_block_content!(options) do
29+
set_content!(options) do
3030
yield
3131
end
3232
end
@@ -64,7 +64,7 @@ def refine_item_options(item, options)
6464
end
6565

6666
def handle_collection_item(collection, item, index, options)
67-
set_block_content!(options) do
67+
set_content!(options) do
6868
yield
6969
end
7070
end
@@ -97,13 +97,20 @@ def array!(collection = nil, options = {})
9797
raise InvalidScopeForArrayError.new("array! expects exclusive use of this block")
9898
end
9999

100-
if collection.nil?
101-
@child_index = nil
102-
yield
103-
else
104-
handle_collection(collection, options) do |item, index|
105-
yield item, index
100+
if block_given?
101+
if collection.nil?
102+
@child_index = nil
103+
yield
104+
else
105+
handle_collection(collection, options) do |item, index|
106+
yield item, index
107+
end
106108
end
109+
elsif options.is_a?(Props::Options)
110+
options.valid_for_set!
111+
handle_collection(collection, options) {}
112+
else
113+
raise ArgumentError.new("array! requires a block when no Props::Options object is given")
107114
end
108115

109116
@scope = :array
@@ -147,7 +154,7 @@ def child!(options = {})
147154
child_index += 1
148155

149156
# this changes the scope to nil so child in a child will break
150-
set_block_content!(options) do
157+
set_content!(options) do
151158
yield
152159
end
153160

lib/props_template/base_with_extensions.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def traveled_path!
2626
@traveled_path.join(".")
2727
end
2828

29-
def set_block_content!(options = {})
29+
def set_content!(options = {})
3030
return super if !@em.has_extensions(options)
3131

3232
@em.handle(options) do
@@ -51,11 +51,16 @@ def format_key(key)
5151
end
5252

5353
def set!(key, options = {}, &block)
54-
if block
54+
if block || options.is_a?(Props::Options)
5555
options = @em.refine_options(options)
5656
end
5757

58-
super
58+
if !block && options.is_a?(Props::Options)
59+
options.valid_for_set!
60+
super {}
61+
else
62+
super
63+
end
5964
end
6065

6166
def handle_set_block(key, options)

lib/props_template/extension_manager.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def handle(options)
5353
@fragment.handle(options)
5454
else
5555
handle_cache(options) do
56-
base.set_block_content! do
56+
base.set_content! do
5757
if options[:partial]
5858
@fragment.handle(options)
5959
@partialer.handle(options)

lib/props_template/extensions/partial_renderer.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,3 @@ def refine_options(options, item = nil)
167167
end
168168
end
169169
end
170-

lib/props_template/helper.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module Props
2+
module Helper
3+
def with
4+
Props::Options.new
5+
end
6+
end
7+
end

0 commit comments

Comments
 (0)