diff --git a/bench.rb b/bench.rb index 0ed105f..dc3191e 100644 --- a/bench.rb +++ b/bench.rb @@ -1,3 +1,5 @@ +load "nolate.rb" + def bench(descr, times) start = Time.now.to_f times.times { yield } @@ -29,23 +31,9 @@ def bench(descr, times) TIMES = 30_000 -if ARGV[0] == "n" - load 'nolate.rb' - puts("nolate") - bench("empty template" , TIMES) { nolate("") } - bench("small constant template" , TIMES) { nolate("nosub") } - bench("simple substitution" , TIMES) { nolate("simple <%= 'sub' %>") } - bench("hash substitution" , TIMES) { nolate("hash sub <%#x%>") } - bench("testview2 file template" , TIMES) { nlt(:testview2) } - bench("big template (#{TEMPLATE.length} bytes)", TIMES) { @x = 1; nolate(TEMPLATE, :x => 1) } -else - load 'nolatep.rb' - include Nolatep - puts("\nnolate with 'parser'") - t = nlt_parse("") ; bench("empty template" , TIMES) { nlt_eval(t) } - t = nlt_parse("nosub") ; bench("small constant template" , TIMES) { nlt_eval(t) } - t = nlt_parse("simple <%= 'sub' %>") ; bench("simple substitution" , TIMES) { nlt_eval(t) } - t = nlt_parse("hash sub <%#x%>") ; bench("hash substitution" , TIMES) { nlt_eval(t) } - bench("testview2 file template" , TIMES) { Nolatep.nlt(:testview2) } - t = nlt_parse(TEMPLATE) ; bench("big template (#{TEMPLATE.length} bytes)", TIMES) { @x = 1; nlt_eval(t, :x => 1) } -end +bench("empty template" , TIMES) { nolate("") } +bench("small constant template" , TIMES) { nolate("nosub") } +bench("simple substitution" , TIMES) { nolate("simple <%= 'sub' %>") } +bench("hash substitution" , TIMES) { nolate("hash sub <%#x%>") } +bench("testview2 file template" , TIMES) { nlt(:testview2) } +bench("big template (#{TEMPLATE.length} bytes)", TIMES) { @x = 1; nolate(TEMPLATE, :x => 1) } diff --git a/nolate.rb b/nolate.rb index 9a8f537..5b0cabf 100644 --- a/nolate.rb +++ b/nolate.rb @@ -28,71 +28,56 @@ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# -$nlt_templates = {} - -def nolate_empty_binding +def nlt_empty_binding return binding() end -def nolate_compile(__template,__sub={}) - __i = 0 - __l = __template.length - __result = '__nolate_html=""'+"\n" - while __i < __l - # Find start: <% - __start = __template.index("<%",__i) - # Emit everything from the last index to the start as a plain string. - if __start != 0 or !__start - __s = __start ? __start-1 : -1 - __content = __template[(__i..__s)] - __content.chop! if __content[-1..-1] == "\n" - __content.chop! if __content[-1..-1] == "\r" - __result << "__nolate_html += "+__content.inspect+"\n" - break if !__start - end - # Find stop: %> - __i = __start+2 - __stop = __template.index("%>",__i) - __stop = __l+1 if !__stop # Implicit %> at end of string... - __i = __stop+2 # In the next iteration we start immediately after %> - # Now we have the string to interpolate, <% ... %> - # What we need to do is to check the first character to understand - # The kind of interpolation to perform: - # <%= ... %> means to eval the expression and substitute the result - # <%#foo%> means to substitute with sub[:foo] - if __template[__start+2] == 61 or __template[__start+2] == '=' - __inter = __template[(__start+3)..(__stop-1)] - __result << "__nolate_html += (\n"+__inter+"\n).to_s\n" - elsif __template[__start+2] == 35 or __template[__start+2] == '#' - __inter = __template[(__start+3)..(__stop-1)] - __result << "__nolate_html += __sub["+(__inter.to_sym.inspect)+"].to_s\n" - else - __inter = __template[(__start+2)..(__stop-1)] - __result << __inter+"\n" +def nlt_templates + @templates ||= {} +end + +def nlt_flush_templates + @templates = {} +end + +def nlt_parse(str) + i = -1 # I wish I had map.with_index in 1.8 :( + str.split(/<%(.*?)%>/m).map do |s| + i, first_char = i + 1, s[0..0] + if i % 2 == 0 then [s.inspect] + elsif first_char == "=" then [:evalo, s[1..-1]] + elsif first_char == "#" then [:sub, s[1..-1].to_sym] + else [:eval, s] end end - __result << '__nolate_html'+"\n" - return __result end -def nolate(__template,__sub={}) - compiled = nolate_compile(__template,__sub) - return eval(compiled) +def nlt_eval(template, sub = {}, b = nlt_empty_binding) + s = "__=[]\n" + template.each do |action, param| + case action + when :evalo then s << "__<<(#{param}).to_s\n" + when :eval then s << "#{param}\n" + when :sub then s << "__<<#{sub[param].to_s.inspect}\n" + else s << "__<<#{action}\n" + end + end + eval(s << "__.join", b, __FILE__, __LINE__) end -def nlt(viewname,sub={}) - viewname = viewname.to_s+".nlt" if viewname.is_a?(Symbol) - if !$nlt_templates[viewname] - filename = "views/"+viewname - if !File.exists?(filename) - raise "NOLATE error: no template at #{filename}" - end - $nlt_templates[viewname] = File.read(filename) +def nlt(viewname, sub={}, b = nlt_empty_binding) + viewname = "#{viewname}.nlt" if viewname.is_a?(Symbol) + unless nlt_templates[viewname] + filename = "views/#{viewname}" + raise "NOLATE error: no template at #{filename}" \ + unless File.exists?(filename) + nlt_templates[viewname] = nlt_parse(File.read(filename).chomp) end - nolate($nlt_templates[viewname],sub) + nlt_eval(nlt_templates[viewname], sub, b) end -def nlt_flush_templates - $nlt_templates = {} +def nolate(str, sub={}) + nlt_eval(nlt_parse(str), sub, binding) end diff --git a/nolatep.rb b/nolatep.rb deleted file mode 100644 index 967d054..0000000 --- a/nolatep.rb +++ /dev/null @@ -1,85 +0,0 @@ -# NOLATE, A NO LAme TEmplate system -# -# Please read the README file for more information -# -# Copyright (c) 2011, Salvatore Sanfilippo -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -module Nolatep - extend self - - def nlt_empty_binding - return binding() - end - - def nlt_templates - @templates ||= {} - end - - def nlt_flush_templates - @templates = {} - end - - def nlt_parse(str) - i = -1 # I wish I had map.with_index in 1.8 :( - str.split(/<%(.*?)%>/m).map do |s| - i, first_char = i + 1, s[0..0] - if i % 2 == 0 then [s.inspect] - elsif first_char == "=" then [:evalo, s[1..-1]] - elsif first_char == "#" then [:sub, s[1..-1].to_sym] - else [:eval, s] - end - end - end - - def nlt_eval(template, sub = {}, b = nlt_empty_binding) - s = "__=[]\n" - template.each do |action, param| - case action - when :evalo then s << "__<<(#{param}).to_s\n" - when :eval then s << "#{param}\n" - when :sub then s << "__<<#{sub[param].to_s.inspect}\n" - else s << "__<<#{action}\n" - end - end - eval(s << "__.join", b, __FILE__, __LINE__) - end - - def nlt(viewname, sub={}, b = nlt_empty_binding) - viewname = "#{viewname}.nlt" if viewname.is_a?(Symbol) - unless nlt_templates[viewname] - filename = "views/#{viewname}" - raise "NOLATE error: no template at #{filename}" unless File.exists?(filename) - nlt_templates[viewname] = nlt_parse(File.read(filename).chomp) - end - nlt_eval(nlt_templates[viewname], sub, b) - end - - def nolate(str, sub={}) - nlt_eval(nlt_parse(str), sub, binding) - end -end diff --git a/test.rb b/test.rb index ef12ba7..a61e3ae 100644 --- a/test.rb +++ b/test.rb @@ -1,7 +1,6 @@ require 'test/unit' load 'nolate.rb' - class MyExampleClass def method_one @x = "Hello" @@ -23,9 +22,22 @@ def test_basic assert_equal(nolate("just ev<% 'sub' %>al"),"just eval") assert_equal(nlt(:testview),"test 4 view") assert_equal(nlt("testview.nlt"),"test 4 view") - assert_equal(nlt(:testview2),"\n4\n\n") + assert_equal(nlt(:testview2),"\n\n4\n\n") assert_equal(nolate("<%x=2%><%=x+1%>"),"3") assert_equal(MyExampleClass.new.method_one,"Hello") assert_equal(MyExampleClass.new.method_two,"World") end + + def test_iter + assert_equal(<<-OUTPUT, nlt(:testview4)) + +Number 1 + +Number 2 + +Number 3 + +Number 4 +OUTPUT + end end diff --git a/testp.rb b/testp.rb deleted file mode 100644 index b6a1381..0000000 --- a/testp.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'test/unit' -load 'nolatep.rb' -class Object; include Nolatep; end - -class MyExampleClass - def method_one - @x = "Hello" - nolate("<%= @x %>") - end - - def method_two - @x = "World" - nlt(:testview3) - end -end - -class NolateTest < Test::Unit::TestCase - def test_basic - assert_equal(nolate(""),"") - assert_equal(nolate("nosub"),"nosub") - assert_equal(nolate("simple <%= 'sub' %>"),"simple sub") - assert_equal(nolate("hash sub <%#x%>",{:x => 1}),"hash sub 1") - assert_equal(nolate("just ev<% 'sub' %>al"),"just eval") - assert_equal(nlt(:testview),"test 4 view") - assert_equal(nlt("testview.nlt"),"test 4 view") - assert_equal(nlt(:testview2),"\n\n4\n\n") - assert_equal(nolate("<%x=2%><%=x+1%>"),"3") - assert_equal(MyExampleClass.new.method_one,"Hello") - assert_equal(MyExampleClass.new.method_two,"World") - end - - def test_iter - assert_equal(<<-OUTPUT, nlt(:testview4)) - -Number 1 - -Number 2 - -Number 3 - -Number 4 -OUTPUT - end -end