Skip to content

Commit 6609184

Browse files
authored
[EXPERIMENTAL] Add support for ERB snippets and sidecar templates (#867)
This PR adds experimental support for ERB snippets in Phlex components.
1 parent ee66d31 commit 6609184

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ group :test do
1212
gem "selenium-webdriver"
1313
end
1414

15+
gem "nokogiri"
16+
1517
group :development do
1618
gem "rubocop"
1719
gem "ruby-lsp"

config/quickdraw.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require "nokogiri"
4+
35
if ENV["COVERAGE"] == "true"
46
require "simplecov"
57

lib/phlex/sgml.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ class Phlex::SGML
55
UNSAFE_ATTRIBUTES = Set.new(%w[srcdoc sandbox http-equiv]).freeze
66
REF_ATTRIBUTES = Set.new(%w[href src action formaction lowsrc dynsrc background ping]).freeze
77

8+
ERBCompiler = ERB::Compiler.new("<>").tap do |compiler|
9+
compiler.pre_cmd = [""]
10+
compiler.put_cmd = "@_state.buffer.<<"
11+
compiler.insert_cmd = "__implicit_output__"
12+
compiler.post_cmd = ["nil"]
13+
14+
def compiler.add_insert_cmd(out, content)
15+
out.push("#{@insert_cmd}((#{content}))")
16+
end
17+
end
18+
819
autoload :Elements, "phlex/sgml/elements"
920
autoload :SafeObject, "phlex/sgml/safe_object"
1021
autoload :SafeValue, "phlex/sgml/safe_value"
@@ -29,6 +40,40 @@ def new(*a, **k, &block)
2940
super
3041
end
3142
end
43+
44+
def erb(method_name, erb = nil, locals: nil, &block)
45+
loc = caller_locations(1, 1)[0]
46+
path = loc.path.delete_suffix(".rb")
47+
file = loc.path
48+
line = loc.lineno - 1
49+
50+
unless erb
51+
method_path = "#{path}/#{method_name}.html.erb"
52+
sidecar_path = "#{path}.html.erb"
53+
54+
if File.exist?(method_path)
55+
erb = File.read(method_path)
56+
file = method_path
57+
line = 1
58+
elsif method_name == :view_template && File.exist?(sidecar_path)
59+
erb = File.read(sidecar_path)
60+
file = sidecar_path
61+
line = 1
62+
else
63+
raise Phlex::RuntimeError.new(<<~MESSAGE)
64+
No ERB template found for #{method_name}
65+
MESSAGE
66+
end
67+
end
68+
69+
code, _enc = ERBCompiler.compile(erb)
70+
71+
class_eval(<<~RUBY, file, line)
72+
def #{method_name} #{locals}
73+
#{code}
74+
end
75+
RUBY
76+
end
3277
end
3378

3479
def view_template

quickdraw/erb.test.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
class Example < Phlex::HTML
4+
def initialize
5+
@name = "Joel"
6+
end
7+
8+
erb :view_template, <<~ERB
9+
<% card do %>
10+
<h1>Hello <%= @name %></h1>
11+
<%= greeting %>
12+
<% end %>
13+
ERB
14+
15+
erb :say_bye, <<~ERB, locals: %(name:)
16+
<h2>Goodbye <%= name %></h2>
17+
ERB
18+
19+
def greeting
20+
p { "How do you do?" }
21+
end
22+
23+
def card
24+
article do
25+
yield
26+
end
27+
say_bye(name: "Joel")
28+
end
29+
end
30+
31+
test do
32+
output = Example.call
33+
34+
assert_equivalent_html output, <<~HTML
35+
<article>
36+
<h1>Hello Joel</h1>
37+
<p>How do you do?</p>
38+
</article>
39+
<h2>Goodbye Joel</h2>
40+
HTML
41+
end

0 commit comments

Comments
 (0)