From: merch-redmine@... Date: 2017-08-25T23:44:45+00:00 Subject: [ruby-core:82472] [Ruby trunk Feature#13839] String Interpolation Statements Issue #13839 has been updated by jeremyevans0 (Jeremy Evans). It might be better to compare this to Erubi, the current default ERB template processor in Rails and Tilt. While this approach is fast for small strings, it's actually slower for large strings (probably due to the use of `+=` instead of `<<`). Additionally, in most cases when you are using templates, you want to cache the resulting ruby code so that repeated rendering does not need to reparse the template input. When you start caching the template object, the performance advantage disappears for small strings, and the performance disadvantage becomes larger for large strings. I don't think ruby should include this this in core or stdlib. I think it would be best to have it as an gem, possibly integrating with Tilt. But I'll admit I'm biased in this respect, as I'm the maintainer of Erubi. Here's example benchmark code: ~~~ ruby class Template attr_reader :input def initialize(input) @input = input end def output "output = %\0" + @input.gsub("{%", "\0\n").gsub("%}", "\noutput += %\0") + "\0" end def render(binding) eval(output, binding) end end require 'erubi' class ErubiTemplate attr_reader :input def initialize(input) @input = input @src = Erubi::Engine.new(input).src end def render(binding) eval(@src, binding) end end template_input = <<'END' {% if true %} Hello #{"World"} {% end %} END erubi_input = <<'END' <% if true %> Hello <%= "World" %> <% end %> END require 'benchmark/ips' Benchmark.ips do |x| x.report("Template-small"){Template.new(template_input).render(binding)} x.report("Erubi-small"){ErubiTemplate.new(erubi_input).render(binding)} x.compare! end template = Template.new(template_input) erubi = ErubiTemplate.new(erubi_input) Benchmark.ips do |x| x.report("Template-small-cache"){template.render(binding)} x.report("Erubi-small-cache"){erubi.render(binding)} x.compare! end template_input *= 10000 erubi_input *= 10000 Benchmark.ips do |x| x.report("Template-large"){Template.new(template_input).render(binding)} x.report("Erubi-large"){ErubiTemplate.new(erubi_input).render(binding)} x.compare! end template = Template.new(template_input) erubi = ErubiTemplate.new(erubi_input) Benchmark.ips do |x| x.report("Template-large-cache"){template.render(binding)} x.report("Erubi-large-cache"){erubi.render(binding)} x.compare! end ~~~ and results (slightly reformatted for easier viewing): ~~~ Calculating ------------------------------------- Template-small 19.271k (_ 0.7%) i/s - 97.515k in 5.060508s Erubi-small 9.123k (_ 0.4%) i/s - 46.163k in 5.060080s Comparison: Template-small: 19270.8 i/s Erubi-small: 9123.2 i/s - 2.11x slower Calculating ------------------------------------- Template-small-cache 19.962k (_ 0.5%) i/s - 100.980k in 5.058694s Erubi-small-cache 21.568k (_ 0.5%) i/s - 108.900k in 5.049226s Comparison: Erubi-small-cache: 21568.2 i/s Template-small-cache: 19962.1 i/s - 1.08x slower Calculating ------------------------------------- Template-large 0.451 (_ 0.0%) i/s - 3.000 in 6.658039s Erubi-large 0.693 (_ 0.0%) i/s - 4.000 in 5.771604s Comparison: Erubi-large: 0.7 i/s Template-large: 0.5 i/s - 1.54x slower Calculating ------------------------------------- Template-large-cache 0.449 (_ 0.0%) i/s - 3.000 in 6.682826s Erubi-large-cache 3.366 (_ 0.0%) i/s - 17.000 in 5.062812s Comparison: Erubi-large-cache: 3.4 i/s Template-large-cache: 0.4 i/s - 7.50x slower ~~~ ---------------------------------------- Feature #13839: String Interpolation Statements https://bugs.ruby-lang.org/issues/13839#change-66283 * Author: se8 (S��bastien Durand) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- Hello! Here is a KISS implementation of a template engine in Ruby: ~~~ ruby class Template attr_reader :input def initialize(input) @input = input end def output "output = %\0" + @input.gsub("{%", "\0\n").gsub("%}", "\noutput += %\0") + "\0" end def render(binding) eval(output, binding) end end ~~~ Usage: ~~~ text {% if true %} Hello #{'World'} {% end %} Template.new('...').render(binding) ~~~ It's kind of a hack on top of Ruby string interpolation, so it's hell fast (~4 times faster than ERB). Could it be a good idea to implement this kind of statements directly in Ruby string interpolation? Maybe a syntax like that: ~~~ text "%{3.times do}Hello #{'World'}%{end}" ~~~ So Ruby would have a fast minimal native template engine, with #{expressions} and %{statements}: ~~~ text eval(File.read("..."), binding) ~~~ -- https://bugs.ruby-lang.org/ Unsubscribe: