Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created April 26, 2015 03:28
Show Gist options
  • Save JoshCheek/2b30b052560337522f94 to your computer and use it in GitHub Desktop.
Save JoshCheek/2b30b052560337522f94 to your computer and use it in GitHub Desktop.
A shitty ERB parser. Procedural vs OO. I like procedural better.
class EmbeddedRuby
def initialize(erb)
@code = Parse.new(erb).code
end
def render(context)
context.instance_eval @code
end
end
class EmbeddedRuby
class Parse
def initialize(erb)
@erb = erb
@parts = ["_out = String.new\n"]
@current = Template.new
end
def code
@code ||= begin
@erb.scan(/<%=?|%>|./m).each do |token|
case token
when '<%=' then begin_code true
when '<%' then begin_code false
when '%>' then end_code
else add_char token
end
end
finalize
end
end
private
def begin_code(do_embed_result)
@parts << @current
@current = Code.new(do_embed_result)
end
def end_code
@parts << @current
@current = Template.new
end
def add_char(char)
@current << char
end
def finalize
@parts << @current
@parts << "_out\n"
@parts.join
end
end
end
class EmbeddedRuby
class Template
def initialize
@text = ''
end
def <<(text)
@text << text
end
def to_s
"_out << #{@text.inspect}\n"
end
end
end
class EmbeddedRuby
class Code
def initialize(embed_result)
@text = ''
@embed_result = embed_result
end
def <<(text)
@text << text
end
def to_s
code = ''
code << '_out << (' if @embed_result
code << @text
code << ')' if @embed_result
code << "\n"
code
end
end
end
erb = EmbeddedRuby.new <<-CODE
<ul>
<% @names.each do |name| %>
<li><%= name %></li>
<% end %>
</ul>
CODE
@names = %w[name1 name2 name3]
puts erb.render(self)
# >> <ul>
# >>
# >> <li>name1</li>
# >>
# >> <li>name2</li>
# >>
# >> <li>name3</li>
# >>
# >> </ul>
class EmbeddedRuby
def initialize(erb)
@code = self.class.parse erb
end
def render(context)
context.instance_eval @code
end
def self.parse(erb)
code = "_out = String.new\n"
type, text = :template, ''
erb.scan(/<%=?|%>|./m).each do |token|
case token
when '<%=' then code << code_for(type, text)
type, text = :calculation, ''
when '<%' then code << code_for(type, text)
type, text = :control, ''
when '%>' then code << code_for(type, text)
type, text = :template, ''
when // then text << token
end
end
code << code_for(type, text) << "_out\n"
end
def self.code_for(type, text)
case type
when :calculation then "_out << (#{text})\n"
when :control then "#{text}\n"
when :template then "_out << #{text.inspect}\n"
else raise "Unknown type: #{type.inspect}"
end
end
end
@names = %w[name1 name2 name3]
puts EmbeddedRuby.new(<<-CODE).render(self)
<ul>
<% @names.each do |name| %>
<li><%= name %></li>
<% end %>
</ul>
CODE
# >> <ul>
# >>
# >> <li>name1</li>
# >>
# >> <li>name2</li>
# >>
# >> <li>name3</li>
# >>
# >> </ul>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment