├── VERSION ├── .gitignore ├── views ├── testview3.nlt ├── testview.nlt ├── layout.nlt ├── layout3.nlt ├── testview4.nlt ├── layout2.nlt ├── testview6.nlt ├── testview2.nlt ├── testview7.nlt ├── example.nlt ├── bigtemplate.nlt └── testview5.nlt ├── Makefile ├── example.rb ├── pkg ├── gemspec_template.rb └── build_gemspec.rb ├── bench.rb ├── nolate.gemspec ├── LICENSE ├── test.rb ├── README └── lib └── nolate.rb /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.2 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg/*.gem 2 | -------------------------------------------------------------------------------- /views/testview3.nlt: -------------------------------------------------------------------------------- 1 | <%= @x %> 2 | -------------------------------------------------------------------------------- /views/testview.nlt: -------------------------------------------------------------------------------- 1 | test <%= 2+2 %> view 2 | -------------------------------------------------------------------------------- /views/layout.nlt: -------------------------------------------------------------------------------- 1 | Header 2 | <%#content%> 3 | Footer 4 | -------------------------------------------------------------------------------- /views/layout3.nlt: -------------------------------------------------------------------------------- 1 | Other Header 2 | <%#content%> 3 | Other Footer 4 | -------------------------------------------------------------------------------- /views/testview4.nlt: -------------------------------------------------------------------------------- 1 | <% (1..4).each{|x| %> 2 | Number <%= x %> 3 | <% } %> 4 | -------------------------------------------------------------------------------- /views/layout2.nlt: -------------------------------------------------------------------------------- 1 | Header 2 | <%#content%> 3 | <%= 4 | nolate("nested call") 5 | %> 6 | Footer 7 | -------------------------------------------------------------------------------- /views/testview6.nlt: -------------------------------------------------------------------------------- 1 | <% y = [1,2,3].map {|x| %> 2 | <%= x*x %> 3 | <% } %> 4 | <%= y.inspect %> 5 | -------------------------------------------------------------------------------- /views/testview2.nlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= 4 | # 2+2 = 4 5 | 2+2 6 | %> 7 | 8 | 9 | -------------------------------------------------------------------------------- /views/testview7.nlt: -------------------------------------------------------------------------------- 1 | zap 2 | <% #comment one 3 | x = 2+2 4 | #comment two %> 5 | <%= x %> 6 | ciao 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | ruby test.rb 3 | 4 | gem: 5 | ruby pkg/build_gemspec.rb 6 | gem build nolate.gemspec 7 | mv -f *.gem pkg 8 | -------------------------------------------------------------------------------- /example.rb: -------------------------------------------------------------------------------- 1 | load 'lib/nolate.rb' 2 | 3 | hash = {:title => "Hello World!"} 4 | @ivar = "Instance Variable Content" 5 | puts nlt(:example,hash) # Check views/example.nlt for more info 6 | -------------------------------------------------------------------------------- /views/example.nlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test <%#title%> 5 | 6 | 7 | 8 |

List of items

9 | <% (0..5).each{|x| %> 10 |
  • <%=x%>
  • 11 | <% } %> 12 | 13 |

    14 | You can access the substitutions hash from Ruby code inside the template 15 | using the __sub variable name: 16 | 17 | :title is set to -> <%= __sub[:title].inspect %> 18 |

    19 | 20 |

    21 | Another way to pass values to the template is via instance variables that 22 | are accessible without any special convention: 23 | 24 | @ivar is set to <%= @ivar.inspect %> 25 |

    26 | 27 |

    28 | Example of conditional: 29 | <% if false %> 30 | Ruby is lame 31 | <% else %> 32 | Ruby is a cool programming language 33 | <% end %> 34 |

    35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /pkg/gemspec_template.rb: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.specification_version = 2 if s.respond_to? :specification_version= 5 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 6 | 7 | s.name = %q{nolate} 8 | s.version = %version% 9 | s.summary = %q{Ruby Template System} 10 | s.description = %q{Pure Ruby template engine optimized for speed} 11 | s.authors = ["Salvatore Sanfilippo","Michel Martens","Bruno Michel","Emmanuel Oga"] 12 | s.date = %q{2011-04-05} 13 | s.email = %q{antirez@gmail.com} 14 | s.extra_rdoc_files = ["LICENSE"] 15 | s.homepage = %q{http://github.com/antirez/nolaote} 16 | s.require_paths = ["lib"] 17 | s.rubygems_version = %q{1.5.0} 18 | 19 | s.files = %files% 20 | end 21 | -------------------------------------------------------------------------------- /views/bigtemplate.nlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Long template with all the features Yeah!. 2 + 2 is: <%= # 2+2 = 4 4 | 2+2 5 | %> 6 | 7 | <%= @x %> 8 | 9 | <% @x %> 10 | 11 | <%#periquin%> 12 | 13 | <% (1..2).each{|x| %> 14 | Number <%= x %> 15 | <% } %> 16 | 17 | 18 | 19 | 20 | Long template with all the features Yeah!. 2 + 2 is: <%= # 2+2 = 4 21 | 2+2 22 | %> 23 | 24 | <%= @x %> 25 | 26 | <% @x %> 27 | 28 | <%#periquin%> 29 | 30 | <% (1..2).each{|x| %> 31 | Number <%= x %> 32 | <% } %> 33 | 34 | 35 | 36 | 37 | Long template with all the features Yeah!. 2 + 2 is: <%= # 2+2 = 4 38 | 2+2 39 | %> 40 | 41 | <%= @x %> 42 | 43 | <% @x %> 44 | 45 | <%#periquin%> 46 | 47 | <% (1..2).each{|x| %> 48 | Number <%= x %> 49 | <% } %> 50 | 51 | 52 | -------------------------------------------------------------------------------- /views/testview5.nlt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Test <%#title%> 5 | 6 | 7 | 8 |

    List of items

    9 | <% (0..5).each{|x| %> 10 |
  • <%=x%>
  • 11 | <% } %> 12 | 13 |

    14 | You can access the substitutions hash from Ruby code inside the template 15 | using the __sub variable name: 16 | 17 | :title is set to -> <%= __sub[:title].inspect %> 18 |

    19 | 20 | <% 21 | # Example 22 | # Error 23 | 4+4 24 | %> 25 | 26 |

    27 | Another way to pass values to the template is via instance variables that 28 | are accessible without any special convention: 29 | 30 | @ivar is set to <%= @ivar.inspect %> 31 |

    32 | 33 |

    34 | Example of conditional: 35 | <% if false %> 36 | Ruby is lame 37 | <% else %> 38 | Ruby is a cool programming language 39 | <% end %> 40 |

    41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /pkg/build_gemspec.rb: -------------------------------------------------------------------------------- 1 | class GemIt 2 | def initialize 3 | @template = File.read("pkg/gemspec_template.rb") 4 | end 5 | def list_files(path,parent=nil) 6 | files = [] 7 | dir = Dir.open(path) 8 | dir.to_a.each{|f| 9 | next if f[0..0] == "." 10 | if File.directory? f 11 | files += list_files("#{path}/#{f}","#{f}") 12 | else 13 | files << (parent ? "#{parent}/#{f}" : f) 14 | end 15 | } 16 | files 17 | end 18 | def gemspec 19 | files = GemIt.new.list_files(".").sort 20 | version = File.read("VERSION").chomp 21 | gemspec = @template 22 | gemspec.sub!("%version%",version.inspect) 23 | gemspec.sub!("%files%",files.inspect) 24 | end 25 | end 26 | 27 | gemspec = GemIt.new.gemspec 28 | f = File.open("nolate.gemspec","w").write(gemspec) 29 | -------------------------------------------------------------------------------- /bench.rb: -------------------------------------------------------------------------------- 1 | load "lib/nolate.rb" 2 | 3 | def bench(descr, times) 4 | start = Time.now.to_f 5 | times.times { yield } 6 | elapsed = Time.now.to_f - start 7 | reqsec = times / elapsed 8 | puts "#{descr.ljust(25)}: #{(reqsec).to_i.to_s.rjust(10)} requests/second" 9 | $template = "" 10 | end 11 | 12 | TEMPLATE = <<-TEMPLATE * 3 13 | 14 | 15 | Long template with all the features Yeah!. 2 + 2 is: <%= # 2+2 = 4 16 | 2+2 17 | %> 18 | 19 | <%= @x %> 20 | 21 | <% @x %> 22 | 23 | <%#periquin%> 24 | 25 | <% (1..2).each{|x| %> 26 | Number <%= x %> 27 | <% } %> 28 | 29 | 30 | TEMPLATE 31 | 32 | TIMES = 30_000 33 | 34 | bench("empty template" , TIMES) { nolate("") } 35 | bench("small constant template" , TIMES) { nolate("nosub") } 36 | bench("simple substitution" , TIMES) { nolate("simple <%= 'sub' %>") } 37 | bench("hash substitution" , TIMES) { nolate("hash sub <%#x%>") } 38 | bench("testview2 file template" , TIMES) { nlt(:testview2) } 39 | bench("big template .nlt", TIMES/5) { @x = 1; nlt(:bigtemplate, :x => 1) } 40 | bench("big template inline", TIMES/10) { @x = 1; nolate(TEMPLATE, :x => 1) } 41 | -------------------------------------------------------------------------------- /nolate.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.specification_version = 2 if s.respond_to? :specification_version= 5 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 6 | 7 | s.name = %q{nolate} 8 | s.version = "0.0.2" 9 | s.summary = %q{Ruby Template System} 10 | s.description = %q{Pure Ruby template engine optimized for speed} 11 | s.authors = ["Salvatore Sanfilippo","Michel Martens","Bruno Michel","Emmanuel Oga"] 12 | s.date = %q{2011-04-05} 13 | s.email = %q{antirez@gmail.com} 14 | s.extra_rdoc_files = ["LICENSE"] 15 | s.homepage = %q{http://github.com/antirez/nolaote} 16 | s.require_paths = ["lib"] 17 | s.rubygems_version = %q{1.5.0} 18 | 19 | s.files = ["LICENSE", "Makefile", "README", "VERSION", "bench.rb", "example.rb", "lib/nolate.rb", "nolate.gemspec", "pkg/build_gemspec.rb", "pkg/gemspec_template.rb", "pkg/nolate-0.0.2.gem", "test.rb", "views/bigtemplate.nlt", "views/example.nlt", "views/layout.nlt", "views/layout2.nlt", "views/layout3.nlt", "views/testview.nlt", "views/testview2.nlt", "views/testview3.nlt", "views/testview4.nlt", "views/testview5.nlt", "views/testview6.nlt", "views/testview7.nlt"] 20 | end 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Salvatore Sanfilippo 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /test.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | load 'lib/nolate.rb' 3 | 4 | class MyExampleClass 5 | def method_one 6 | @x = "Hello" 7 | nolate("<%= @x %>") 8 | end 9 | 10 | def method_two 11 | @x = "World" 12 | nlt(:testview3) 13 | end 14 | end 15 | 16 | class NolateTest < Test::Unit::TestCase 17 | def test_basic 18 | assert_equal("",nolate("")) 19 | assert_equal("nosub",nolate("nosub")) 20 | assert_equal("simple sub",nolate("simple <%= 'sub' %>")) 21 | assert_equal("hash sub 1",nolate("hash sub <%#x%>",{:x => 1})) 22 | assert_equal("just eval",nolate("just ev<% 'sub' %>al")) 23 | assert_equal("test 4 view\n",nlt(:testview)) 24 | assert_equal("test 4 view\n",nlt("testview.nlt")) 25 | assert_equal("\n\n4\n\n\n",nlt(:testview2)) 26 | assert_equal("3",nolate("<%x=2%><%=x+1%>")) 27 | assert_equal("Hello",MyExampleClass.new.method_one) 28 | assert_equal("World\n",MyExampleClass.new.method_two) 29 | assert_equal("\n1\n\n4\n\n9\n\n[1, 4, 9]\n",nlt(:testview6)) 30 | assert_equal("zap\n\n4\nciao\n",nlt(:testview7)) 31 | end 32 | 33 | def test_iter 34 | assert_equal(<<-OUTPUT, nlt(:testview4)) 35 | Number 1 36 | Number 2 37 | Number 3 38 | Number 4 39 | 40 | OUTPUT 41 | end 42 | 43 | def test_layout 44 | nlt_set_layout(:layout) 45 | assert_equal("Header\n2+2=4\nFooter\n",nolate("2+2=<%= 2+2 %>")) 46 | nlt_set_layout(:layout2) 47 | assert_equal("Header\nciao\nnested call\nFooter\n",nolate("ciao")) 48 | nlt_set_layout(:layout) 49 | assert_equal("2+2=4",nolate("2+2=<%= 2+2 %>",{},{:layout => false})) 50 | assert_equal("Other Header\n2+2=4\nOther Footer\n", 51 | nolate("2+2=<%= 2+2 %>",{},{:layout => :layout3})) 52 | end 53 | 54 | def test_error_lines 55 | # Make sure that compiled template and template have the code in the 56 | # same lines, so that eval() will report good error traces. 57 | text = File.read("views/testview5.nlt").to_a 58 | compiled = nlt_compile(File.read("views/testview5.nlt")).split("\n") 59 | assert(text[3] =~ /title/ && compiled[3] =~ /title/) 60 | assert(text[8] =~ /each/ && compiled[8] =~ /each/) 61 | assert(text[22] =~ /4\+4/ && compiled[22] =~ /4\+4/) 62 | assert(text[29] =~ /ivar/ && compiled[29] =~ /ivar/) 63 | assert(text[36] =~ /else/ && compiled[36] =~ /else/) 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Nolate - NO LAme TEmplate system 2 | 3 | OVERVIEW 4 | -------- 5 | 6 | Nolate is a template system similar to erb that is designed for not consuming 7 | some huge amount of CPU without a good reason. It is written in pure Ruby. 8 | Nolate is distributed under a two clause BSD license for max freedom. 9 | 10 | Templates can include code inside <%= %> that is evaluated with eval() and 11 | substituted to the string. 12 | 13 | Also occurrences of <%#something%> are substituted with the value of the 14 | hash field :something of an additional hash passed to nolate() or nlt() 15 | functions. 16 | 17 | IS IT SUPER FAST? 18 | ----------------- 19 | 20 | Fast enough that a small template substitution is not measurable by the 21 | apache benchmark against a sample Sinatra application. 22 | Surprisingly this is not as common as it should be. 23 | 24 | HOW TO USE IT? 25 | -------------- 26 | 27 | Nolate consists of just a few functions. The first is nolate() that is the core 28 | of the template system: 29 | 30 | nolate("The sum is <%= 2+2 %> Also substitute <%#myfield%> ok", 31 | {:myfield => 10}) 32 | 33 | The array is optional, if you don't have <%#%> style substitutions you can 34 | simply omit it. 35 | 36 | The second function is nlt(), and is used to also handle loading the template 37 | from a 'views' directory. The above example can be translated using nlt() 38 | just creating a file 'views/index.nlt' with the following content: 39 | 40 | The sum is <%= 2+2 %> Also substitute <%#myfield%> ok 41 | 42 | Then we can use nlt() calling: 43 | 44 | nlt("index.nlt", {:myfield => "something"}) 45 | 46 | the nlt() funciton will take care to cache templates into memory. 47 | In order to flush this templates to force reloading just call the third and 48 | final provided function: 49 | 50 | nlt_flush_templates() 51 | 52 | It is possible to pass the template name as a symbol like in: 53 | 54 | nlt(:index) 55 | 56 | nlt will add ".nlt" at the end of the name when it is a symbol. 57 | 58 | SUPPORTED SUBSTITUTIONS 59 | ----------------------- 60 | 61 | <%= somecode %> Evaluates somecode and substitutes the return value. 62 | <% somecode %> Evaluates somecode. 63 | <%#filed_name%> Substituted the value of the specified filed of the hash 64 | passed to nolate() or nlt() as optional argument. 65 | 66 | It is possible to interleave template and Ruby code together like in the 67 | following example: 68 | 69 | <% (0..5).each{|x| %> 70 |
  • <%=x%>
  • 71 | <% } %> 72 | 73 | USING LAYOUTS 74 | ------------- 75 | 76 | Nolate suppots the concept of layout. You can specify to use a layout using 77 | the following call: 78 | 79 | nlt_set_layout(:layout) 80 | 81 | The above call specifies to use "views/layout.nlt" as template. 82 | 83 | The layout is a normal nolate template that contains somewhere a 84 | substitution in the form of: <%#content%>. When a layout is set what happens 85 | is that all the calls to nolate() or nlt() are processed, then the result 86 | is substituted to the layout template. 87 | 88 | The layout template can also include any other standard nolate substitution 89 | and code evaluation. 90 | 91 | It is possible to change layout at runtime, or to disable the layout setting 92 | it to nil. 93 | 94 | CONTRIBUTE 95 | ---------- 96 | 97 | Please send me pull requests: http://github.com/antirez/nolate 98 | 99 | So far thank you to: 100 | 101 | @soveran 102 | @brmichel 103 | @JEG2 104 | Emmanuel Oga (https://github.com/EmmanuelOga) 105 | @czarneckid 106 | 107 | It is not too late... 108 | Salvatore 109 | -------------------------------------------------------------------------------- /lib/nolate.rb: -------------------------------------------------------------------------------- 1 | # NOLATE, A NO LAme TEmplate system 2 | # 3 | # Please read the README file for more information 4 | # 5 | # Copyright (c) 2011, Salvatore Sanfilippo 6 | # 7 | # All rights reserved. 8 | # 9 | # Redistribution and use in source and binary forms, with or without 10 | # modification, are permitted provided that the following conditions are 11 | # met: 12 | # 13 | # * Redistributions of source code must retain the above copyright 14 | # notice, this list of conditions and the following disclaimer. 15 | # 16 | # * Redistributions in binary form must reproduce the above copyright 17 | # notice, this list of conditions and the following disclaimer in the 18 | # documentation and/or other materials provided with the distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | # 32 | 33 | def nlt_empty_binding(__sub_) 34 | __sub = __sub_ 35 | return binding() 36 | end 37 | 38 | def nlt_templates 39 | $nolate_templates ||= {} 40 | end 41 | 42 | def nlt_flush_templates 43 | $nolate_templates = {} 44 | end 45 | 46 | def nlt_set_layout(layout) 47 | $nolate_layout = layout 48 | end 49 | 50 | def nlt_parse(str) 51 | i = -1 # I wish I had map.with_index in 1.8 :( 52 | prev_was_eval = false # Previous token was an :eval? 53 | str.split(/<%(.*?)%>/m).map do |s| 54 | i, first_char = i + 1, s[0..0] 55 | newlines = s.count("\n") 56 | if i % 2 == 0 57 | j = 0 58 | if prev_was_eval and s != "\n" and s != "\r\n" 59 | j += 1 if s[j..j] == "\r" 60 | j += 1 if s[j..j] == "\n" 61 | end 62 | prev_was_eval = false 63 | [:verb, s[j..-1].inspect, newlines] 64 | elsif first_char == "=" 65 | prev_was_eval = false 66 | [:evalo, s[1..-1], newlines] 67 | elsif first_char == "#" 68 | prev_was_eval = false 69 | [:sub, s[1..-1].to_sym, newlines] 70 | else 71 | prev_was_eval = true 72 | [:eval, s, 0] 73 | end 74 | end 75 | end 76 | 77 | def nlt_compile(template) 78 | s = "__=[]; " 79 | nlt_parse(template).each do |action, param, newlines| 80 | if action == :evalo or action == :eval 81 | if param[(-1..-1)] != "\n" and param.index("#") 82 | lines = param.split("\n") 83 | if lines[-1] =~ /^ *#/ 84 | lines[-1] = "" 85 | param = lines.join("\n") 86 | end 87 | end 88 | end 89 | case action 90 | when :evalo then s << "@__ = (#{param}); __<< @__.to_s; @__; " 91 | when :eval then s << "#{param}; " 92 | when :sub then s << "__<< __sub[#{param.to_sym.inspect}]; @__; " 93 | when :verb then s << "__<<#{param}; @__; " 94 | end 95 | s << "\n"*newlines 96 | end 97 | s << "\n__.join" 98 | end 99 | 100 | def nlt_eval(code, sub = {}, opt = {}, file="evaluated_string") 101 | # Make sure that nested calls will not substitute the layout 102 | saved = @nolate_no_layout 103 | @nolate_no_layout = true 104 | content = eval(code, nlt_empty_binding(sub), file, 1) 105 | @nolate_no_layout = saved 106 | 107 | # And... make sure that the layout will not trigger an infinite recursion 108 | # substituting itself forever. 109 | if $nolate_layout and !@nolate_no_layout and !(opt[:layout] == false) 110 | saved = $nolate_layout 111 | if !opt[:layout] 112 | use = $nolate_layout 113 | else 114 | use = opt[:layout] 115 | end 116 | $nolate_layout = nil 117 | content = nlt(use,{:content => content}) 118 | $nolate_layout = saved 119 | end 120 | content 121 | end 122 | 123 | def nlt(viewname, sub={}, opt={}) 124 | viewname = "#{viewname}.nlt" if viewname.is_a?(Symbol) 125 | unless nlt_templates[viewname] 126 | filename = "views/#{viewname}" 127 | raise "NOLATE error: no template at #{filename}" \ 128 | unless File.exists?(filename) 129 | nlt_templates[viewname] = nlt_compile(File.read(filename)) 130 | end 131 | nlt_eval(nlt_templates[viewname], sub, opt, "views/#{viewname}") 132 | end 133 | 134 | def nolate(str, sub={}, opt={}) 135 | nlt_eval(nlt_compile(str), sub, opt) 136 | end 137 | --------------------------------------------------------------------------------