Skip to content

Commit c405c1d

Browse files
authored
Support selective rendering in compiled components (#935)
Input ```ruby def view_template h1 { "Hello" } fragment :name do h2 { "World" } end end ``` Output ```ruby def view_template __phlex_state__ = @_state __phlex_buffer__ = __phlex_state__.buffer __phlex_me__ = self __phlex_should_render__ = __phlex_state__.should_render?; nil __phlex_buffer__ << "<h1>Hello</h1>" if __phlex_should_render__; nil; fragment :name do __phlex_original_should_render__ = __phlex_should_render__ __phlex_should_render__ = __phlex_state__.should_render?; __phlex_buffer__ << "<h2>World</h2>" if __phlex_should_render__; nil; __phlex_should_render__ = __phlex_original_should_render__ end end ```
2 parents 210e0b7 + 11b775d commit c405c1d

File tree

4 files changed

+86
-12
lines changed

4 files changed

+86
-12
lines changed

lib/phlex/compiler/method_compiler.rb

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@ def compile(node)
1414
result.body&.body&.unshift(
1515
proc do |f|
1616
f.statement do
17-
f.push "__phlex_buffer__ = @_state.buffer; nil"
17+
f.push "__phlex_state__ = @_state"
1818
end
1919
f.statement do
20-
f.push "__phlex_me__ = self; nil"
20+
f.push "__phlex_buffer__ = __phlex_state__.buffer"
21+
end
22+
f.statement do
23+
f.push "__phlex_me__ = self"
24+
end
25+
f.statement do
26+
f.push "__phlex_should_render__ = __phlex_state__.should_render?; nil"
2127
end
2228
end
2329
)
@@ -38,6 +44,8 @@ def visit_call_node(node)
3844
return compile_doctype_helper(node)
3945
elsif plain_helper?(node)
4046
return compile_plain_helper(node)
47+
elsif fragment_helper?(node)
48+
return compile_fragment_helper(node)
4149
end
4250
end
4351

@@ -110,16 +118,20 @@ def visit_phlex_block(node)
110118

111119
def visit_block_node(node)
112120
node.copy(
113-
body: [
114-
statement("if __phlex_me__ == self"),
115-
visit(node.body),
116-
statement("else"),
117-
[[node.body]],
118-
statement("end"),
119-
]
121+
body: compile_block_body_node(node.body)
120122
)
121123
end
122124

125+
def compile_block_body_node(node)
126+
[
127+
statement("if __phlex_me__ == self;"),
128+
visit(node),
129+
statement("else;"),
130+
[[node]],
131+
statement("end;"),
132+
]
133+
end
134+
123135
def compile_void_element(node, tag)
124136
[
125137
[
@@ -165,6 +177,23 @@ def compile_plain_helper(node)
165177
end
166178
end
167179

180+
def compile_fragment_helper(node)
181+
node.copy(
182+
block: compile_fragment_helper_block(node.block)
183+
)
184+
end
185+
186+
def compile_fragment_helper_block(node)
187+
node.copy(
188+
body: [
189+
statement("__phlex_original_should_render__ = __phlex_should_render__"),
190+
statement("__phlex_should_render__ = __phlex_state__.should_render?;"),
191+
visit(node.body),
192+
statement("__phlex_should_render__ = __phlex_original_should_render__"),
193+
]
194+
)
195+
end
196+
168197
private def ensure_new_line
169198
proc(&:ensure_new_line)
170199
end
@@ -204,7 +233,7 @@ def compile_plain_helper(node)
204233

205234
proc do |f|
206235
f.statement do
207-
f.push "__phlex_buffer__ << \"#{new_buffer.gsub('"', '\\"')}\"; nil"
236+
f.push "__phlex_buffer__ << \"#{new_buffer.gsub('"', '\\"')}\" if __phlex_should_render__; nil;"
208237
end
209238
end
210239
end
@@ -260,6 +289,10 @@ def compile_plain_helper(node)
260289
node.name == :plain && own_method_without_scope?(node)
261290
end
262291

292+
private def fragment_helper?(node)
293+
node.name == :fragment && own_method_without_scope?(node)
294+
end
295+
263296
ALLOWED_OWNERS = [Phlex::SGML, Phlex::HTML, Phlex::SVG]
264297
private def own_method_without_scope?(node)
265298
ALLOWED_OWNERS.include?(@component.instance_method(node.name).owner)

lib/phlex/compiler/verbatim_formatter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def push(value)
2222
end
2323

2424
def emit(node)
25+
return unless node
2526
source_map = @source_map
2627
current_line = @current_line
2728
start_line = node.start_line
@@ -33,7 +34,7 @@ def emit(node)
3334
i += 1
3435
end
3536

36-
push node.slice if node
37+
push node.slice
3738
end
3839

3940
def visit_alias_global_variable_node(node)

lib/phlex/sgml.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,9 @@ def json_escape(string)
460460
end
461461

462462
private def __render_attributes__(attributes)
463-
@_state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
463+
state = @_state
464+
return unless state.should_render?
465+
state.buffer << (Phlex::ATTRIBUTE_CACHE[attributes] ||= Phlex::SGML::Attributes.generate_attributes(attributes))
464466
end
465467

466468
private_class_method def self.method_added(method_name)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# frozen_string_literal: true
2+
3+
class ExampleComponent < Phlex::HTML
4+
def view_template(&)
5+
div(&)
6+
end
7+
end
8+
9+
class StandardElementExample < Phlex::HTML
10+
def initialize(execution_checker = -> {})
11+
@execution_checker = execution_checker
12+
end
13+
14+
def view_template
15+
doctype
16+
div {
17+
comment { h1(id: "target") }
18+
h1 { "Before" }
19+
img(src: "before.jpg")
20+
render ExampleComponent.new { "Should not render" }
21+
whitespace
22+
comment { "This is a comment" }
23+
fragment("target") do
24+
h1(id: "target") {
25+
plain "Hello"
26+
strong { "World" }
27+
img(src: "image.jpg")
28+
}
29+
end
30+
@execution_checker.call
31+
strong { "Here" }
32+
fragment("image") do
33+
img(id: "image", src: "after.jpg")
34+
end
35+
h1(id: "target") { "After" }
36+
}
37+
end
38+
end

0 commit comments

Comments
 (0)