Skip to content

Commit 177e35e

Browse files
committed
Add support for fragment context
This commit was missing from the 2.0 release and includes several related improvements: 1. Adds fragmentContext to allow digging using props_at to find a node, then considering what fragment it was found in, returns a `found_path` that can be used in a graft response's `path`. We'll need to replace the generated files in superglue_rails 2. Modifies `fragments!` to return paths that are relative to the data being presented. So instead of `data.a.deep.path.in.page` it returns. `data.a.deep.path.in.response` with the latter being much shorter.
1 parent 8c805dd commit 177e35e

File tree

6 files changed

+163
-18
lines changed

6 files changed

+163
-18
lines changed

lib/props_template.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ class << self
3030
:disable_deferments!,
3131
:set_block_content!,
3232
:traveled_path!,
33+
:fragment_context!,
3334
to: :builder!
3435

3536
def initialize(context = nil, options = {})
3637
@builder = BaseWithExtensions.new(self, context, options)
3738
@context = context
39+
@fragment_context = nil
40+
@found_path = []
3841
end
3942

4043
def set!(key, options = {}, &block)
@@ -48,7 +51,9 @@ def set!(key, options = {}, &block)
4851
options.delete(:dig)
4952

5053
@builder.set!(key, options, &block)
51-
found_block, found_options = @builder.found!
54+
found_block, found_path, found_options, fragment_context = @builder.found!
55+
@found_path = found_path || []
56+
@fragment_context = fragment_context
5257
@builder = prev_builder
5358

5459
if found_block
@@ -59,6 +64,14 @@ def set!(key, options = {}, &block)
5964
end
6065
end
6166

67+
def found_path!
68+
@found_path.join(".")
69+
end
70+
71+
def fragment_context!
72+
@fragment_context
73+
end
74+
6275
def builder!
6376
@builder
6477
end

lib/props_template/base_with_extensions.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,16 @@ def set!(key, options = {}, &block)
5555
options = @em.refine_options(options)
5656
end
5757

58-
super(key, options, &block)
58+
super
5959
end
6060

6161
def handle_set_block(key, options)
62-
@traveled_path.push(key)
6362
n = 1
6463
if (suffix = options[:path_suffix])
6564
n += suffix.length
6665
@traveled_path.push(suffix)
66+
else
67+
@traveled_path.push(key)
6768
end
6869

6970
super

lib/props_template/extensions/fragment.rb

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,28 @@ def initialize(base, fragments = [])
77
@fragments = fragments
88
end
99

10-
def handle(options)
10+
def self.fragment_name_from_options(options)
1111
return if !options[:partial]
12-
_partial_name, partial_opts = options[:partial]
12+
13+
_, partial_opts = [*options[:partial]]
14+
return unless partial_opts
15+
1316
fragment = partial_opts[:fragment]
1417

1518
if String === fragment || Symbol === fragment
16-
key = fragment.to_s
17-
path = @base.traveled_path.join(".")
18-
@name =key
19+
fragment.to_s
20+
end
21+
end
22+
23+
def handle(options)
24+
fragment_name = self.class.fragment_name_from_options(options)
25+
path = @base.traveled_path
26+
.map { |item| item.is_a?(Array) ? item[0] : item }
27+
.join(".")
1928

29+
if fragment_name
2030
@fragments.push(
21-
{id: key, path: path}
31+
{id: fragment_name, path: path}
2232
)
2333
end
2434
end

lib/props_template/searcher.rb

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(builder, path = [], context = nil)
1111
@builder = builder
1212
@traveled_path = []
1313
@partialer = Partialer.new(self, context, builder)
14+
@fragment_name = nil
1415
end
1516

1617
def deferred!
@@ -24,12 +25,19 @@ def fragments!
2425
def found!
2526
pass_opts = @found_options.clone || {}
2627
pass_opts.delete(:defer)
27-
traveled_path = @traveled_path[1..] || []
28+
traveled_path = @traveled_path || []
2829
if !traveled_path.empty?
2930
pass_opts[:path_suffix] = traveled_path
3031
end
3132

32-
[@found_block, pass_opts]
33+
fragment_name = Fragment.fragment_name_from_options(pass_opts)
34+
if fragment_name
35+
@fragment_name = fragment_name
36+
@traveled_path = []
37+
end
38+
39+
fragment_context = @fragment_name
40+
[@found_block, @traveled_path, pass_opts, fragment_context]
3341
end
3442

3543
def set_block_content!(*args)
@@ -50,6 +58,12 @@ def set!(key, options = {}, &block)
5058

5159
@depth += 1
5260
if options[:partial]
61+
fragment_name = Fragment.fragment_name_from_options(options)
62+
if fragment_name
63+
@fragment_name = fragment_name
64+
@traveled_path = []
65+
end
66+
5367
@partialer.handle(options)
5468
else
5569
yield
@@ -94,6 +108,11 @@ def array!(collection = nil, options = {}, &block)
94108

95109
@depth += 1
96110
if pass_opts[:partial]
111+
fragment_name = Fragment.fragment_name_from_options(pass_opts)
112+
if fragment_name
113+
@fragment_name = fragment_name
114+
@traveled_path = []
115+
end
97116
# todo: what happens when cached: true is passed?
98117
# would there be any problems with not using the collection_rende?
99118
@partialer.handle(pass_opts)
@@ -105,7 +124,7 @@ def array!(collection = nil, options = {}, &block)
105124
end
106125
end
107126

108-
def child!(options={}, &block)
127+
def child!(options = {}, &block)
109128
return if @found_block
110129

111130
child_index = @child_index || -1
@@ -128,7 +147,7 @@ def child!(options={}, &block)
128147
@depth -= 1
129148
end
130149

131-
@child_index = child_index
150+
@child_index = child_index
132151
end
133152
end
134153
end

spec/extensions/prop_template_search_spec.rb

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
foo: "bar"
2626
})
2727
end
28-
28+
2929
it "aliases dig to search and finds the correct node and merges it to the top" do
3030
json = render(<<~PROPS)
3131
json.data(search: ['data', 'comment', 'details']) do
@@ -447,6 +447,106 @@
447447
})
448448
end
449449

450+
context "fragment context" do
451+
it "renders the fragment context" do
452+
json = render(<<~PROPS)
453+
json.data(dig: ['data', 'comment']) do
454+
json.comment(partial: ['comment', fragment: 'foobar']) do
455+
end
456+
end
457+
json.fragments json.fragments!
458+
json.fragmentContext json.fragment_context!
459+
json.path json.traveled_path!
460+
PROPS
461+
462+
expect(json).to eql_json({
463+
data: {
464+
title: "some title",
465+
details: {
466+
body: "hello world"
467+
}
468+
},
469+
fragments: [
470+
{id: "foobar", path: "data"}
471+
],
472+
fragmentContext: "foobar",
473+
path: ""
474+
})
475+
end
476+
477+
it "renders the fragment context with a grafting path relative to the key" do
478+
json = render(<<~PROPS)
479+
json.data(dig: ['data', 'comment', 'details']) do
480+
json.comment(partial: ['comment', fragment: 'foobar']) do
481+
end
482+
end
483+
json.fragments json.fragments!
484+
json.fragmentContext json.fragment_context!
485+
json.path json.found_path!
486+
PROPS
487+
488+
expect(json).to eql_json({
489+
data: {
490+
body: "hello world"
491+
},
492+
fragments: [],
493+
fragmentContext: "foobar",
494+
path: "details"
495+
})
496+
end
497+
498+
it "renders the fragment context for an array item relative to the key" do
499+
json = render(<<~PROPS)
500+
json.data(dig: ['data','comment', 0]) do
501+
json.comment do
502+
json.array! [0], {partial: ['comment', fragment: ->(item){ "foo-"+item.to_s }]} do
503+
end
504+
end
505+
end
506+
json.fragments json.fragments!
507+
json.fragment_context json.fragment_context!
508+
json.path json.found_path!
509+
PROPS
510+
511+
expect(json).to eql_json({
512+
data: {
513+
title: "some title",
514+
details: {
515+
body: "hello world"
516+
}
517+
},
518+
fragments: [
519+
{id: "foo-0", path: "data"}
520+
],
521+
fragment_context: "foo-0",
522+
path: ""
523+
})
524+
end
525+
526+
it "renders the fragment context for an array item relative to the key" do
527+
json = render(<<~PROPS)
528+
json.data(dig: ['data','comment', 0, 'details']) do
529+
json.comment do
530+
json.array! [0], {partial: ['comment', fragment: ->(item){ "foo-"+item.to_s }]} do
531+
end
532+
end
533+
end
534+
json.fragments json.fragments!
535+
json.fragment_context json.fragment_context!
536+
json.path json.found_path!
537+
PROPS
538+
539+
expect(json).to eql_json({
540+
data: {
541+
body: "hello world"
542+
},
543+
fragments: [],
544+
fragment_context: "foo-0",
545+
path: "details"
546+
})
547+
end
548+
end
549+
450550
it "passes the found child obj options back to the parent" do
451551
json = render(<<~PROPS)
452552
json.data(dig: ['data', 'comment']) do

spec/searcher_spec.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ def member_by(key, value)
2424
end
2525
end
2626

27-
found_block, found_options = json.found!
27+
found_block, found_path, found_options = json.found!
2828
expect(found_block).to eql(target_proc)
29+
expect(found_path).to eql ["outer", "inner", "hit"]
2930
expect(found_options).to eql({
3031
some_options: 1,
31-
path_suffix: ["inner", "hit"]
32+
path_suffix: ["outer", "inner", "hit"]
3233
})
3334
end
3435

@@ -134,8 +135,9 @@ def member_by(key, value)
134135
end
135136
end
136137

137-
found_block, found_options = json.found!
138-
expect(found_options).to eql({some_options: 1, path_suffix: [1]})
138+
found_block, found_path, found_options = json.found!
139+
expect(found_options).to eql({some_options: 1, path_suffix: ["outer", 1]})
140+
expect(found_path).to eql(["outer", 1])
139141
expect(found_block.call).to eql("world")
140142
end
141143

0 commit comments

Comments
 (0)