require_relative "../spec_helper" begin require 'tilt/erb' require 'tilt/string' rescue LoadError warn "tilt not installed, skipping render plugin test" else describe "render plugin" do before do app(:bare) do plugin :render, :views=>"./spec/views", :check_paths=>true route do |r| r.on "home" do view("home", :locals=>{:name => "Agent Smith", :title => "Home"}, :layout_opts=>{:locals=>{:title=>"Home"}}) end r.on "about" do render("about", :locals=>{:title => "About Roda"}) end r.on "inline" do view(:inline=>"Hello <%= name %>", :locals=>{:name => "Agent Smith"}, :layout=>nil) end r.on "path" do render(:path=>"./spec/views/about.erb", :locals=>{:title => "Path"}, :layout_opts=>{:locals=>{:title=>"Home"}}) end r.on "content" do view(:content=>'bar', :layout_opts=>{:locals=>{:title=>"Home"}}) end end end end it "default actions" do body("/about").strip.must_equal "

About Roda

" body("/home").strip.must_equal "Roda: Home\n

Home

\n

Hello Agent Smith

" body("/inline").strip.must_equal "Hello Agent Smith" body("/path").strip.must_equal "

Path

" body("/content").strip.must_equal "Roda: Home\nbar" end it "with str as engine" do app.plugin :render, :engine => "str" body("/about").strip.must_equal "

About Roda

" body("/home").strip.must_equal "Roda: Home\n

Home

\n

Hello Agent Smith

" body("/inline").strip.must_equal "Hello <%= name %>" end it "custom default layout support" do app.plugin :render, :layout => "layout-alternative" body("/home").strip.must_equal "Alternative Layout: Home\n

Home

\n

Hello Agent Smith

" end it "using hash for :layout" do app.plugin :render, :layout => {:inline=> 'a<%= yield %>b'} body("/home").strip.must_equal "a

Home

\n

Hello Agent Smith

\nb" end end describe "render plugin" do it "simple layout support" do app(:bare) do plugin :render route do |r| render(:path=>"spec/views/layout-yield.erb") do render(:path=>"spec/views/content-yield.erb") end end end body.gsub(/\n+/, "\n").must_equal "Header\nThis is the actual content.\nFooter\n" end it "should have :layout_opts=>:views plugin option respect :root app option" do app(:bare) do self.opts[:root] = 'spec' plugin :render, :layout_opts=>{:views=>"views"}, :allowed_paths=>["spec/views"] route do |r| view(:content=>'a', :layout_opts=>{:locals=>{:title=>"Home"}}) end end body.strip.must_equal "Roda: Home\na" end it "views without default layouts" do app(:bare) do plugin :render, :views=>"./spec/views", :layout=>false route do |r| view("home", :locals=>{:name=>"Agent Smith", :title=>"Home"}) end end body.strip.must_equal "

Home

\n

Hello Agent Smith

" end it "layout overrides" do app(:bare) do plugin :render, :views=>"./spec/views" route do |r| view("home", :locals=>{:name=>"Agent Smith", :title=>"Home" }, :layout=>"layout-alternative", :layout_opts=>{:locals=>{:title=>"Home"}}) end end body.strip.must_equal "Alternative Layout: Home\n

Home

\n

Hello Agent Smith

" end it ":layout=>true/false/string/hash/not-present respects plugin layout switch and template" do app(:bare) do plugin :render, :views=>"./spec/views", :layout_opts=>{:template=>'layout-yield'} route do |r| opts = {:content=>'bar'} opts[:layout] = true if r.path == '/' opts[:layout] = false if r.path == '/f' opts[:layout] = 'layout' if r.path == '/s' opts[:layout] = {:template=>'layout'} if r.path == '/h' opts[:layout_opts] = {:locals=>{:title=>'a'}} view(opts) end end body.gsub("\n", '').must_equal "HeaderbarFooter" body('/a').gsub("\n", '').must_equal "HeaderbarFooter" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render body.gsub("\n", '').must_equal "HeaderbarFooter" body('/a').gsub("\n", '').must_equal "HeaderbarFooter" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render, :layout=>true body.gsub("\n", '').must_equal "HeaderbarFooter" body('/a').gsub("\n", '').must_equal "HeaderbarFooter" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render, :layout=>'layout-alternative' body.gsub("\n", '').must_equal "Alternative Layout: abar" body('/a').gsub("\n", '').must_equal "Alternative Layout: abar" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render, :layout=>nil body.gsub("\n", '').must_equal "HeaderbarFooter" body('/a').gsub("\n", '').must_equal "bar" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render, :layout=>false body.gsub("\n", '').must_equal "HeaderbarFooter" body('/a').gsub("\n", '').must_equal "bar" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" app.plugin :render, :layout_opts=>{:template=>'layout-alternative', :locals=>{:title=>'a'}} body.gsub("\n", '').must_equal "Alternative Layout: abar" body('/a').gsub("\n", '').must_equal "bar" body('/f').gsub("\n", '').must_equal "bar" body('/s').gsub("\n", '').must_equal "Roda: abar" body('/h').gsub("\n", '').must_equal "Roda: abar" end it "app :root option affects :views default" do app app.plugin :render app.render_opts[:views].must_equal File.join(Dir.pwd, 'views') app.opts[:root] = '/foo' app.plugin :render # Work around for Windows app.render_opts[:views].sub(/\A\w:/, '').must_equal '/foo/views' app.opts[:root] = '/foo/bar' app.plugin :render app.render_opts[:views].sub(/\A\w:/, '').must_equal '/foo/bar/views' app.opts[:root] = nil app.plugin :render app.render_opts[:views].must_equal File.join(Dir.pwd, 'views') app.plugin :render, :views=>'bar' app.render_opts[:views].must_equal File.join(Dir.pwd, 'bar') end it "inline layouts and inline views" do app(:render) do view({:inline=>'bar'}, :layout=>{:inline=>'Foo: <%= yield %>'}) end body.strip.must_equal "Foo: bar" end it "inline renders with opts" do app(:render) do render({:inline=>'<%= bar %>'}, {:engine=>'str'}) end body.strip.must_equal '<%= bar %>' end it "template renders with :template opts" do app(:bare) do plugin :render, :views => "./spec/views" route do render(:template=>"about", :locals=>{:title => "About Roda"}) end end body.strip.must_equal "

About Roda

" end it "template renders with :template_class opts" do app(:render) do @a = 1 render(:inline=>'i#{@a}', :template_class=>::Tilt[:str]) end body.must_equal "i1" end it "can specify engine-specific options via :engine_opts" do app(:bare) do plugin :render, :engine_opts=>{'a.erb'=>{:outvar=>'@a'}} route do |r| @a = nil r.is('a') do render(:inline=>'<%= @a.class.name %>', :engine=>'a.erb') end render(:inline=>'<%= @a.class.name %>') end end body('/a').must_equal "String" body.must_equal "NilClass" end it "template cache respects :template_opts" do c = Class.new do def initialize(path, _, opts) @path = path @opts = opts end def render(*) "#{@path}-#{@opts[:foo]}" end end app(:render) do |r| r.is "a" do render(:inline=>"i", :template_class=>c, :template_opts=>{:foo=>'a'}) end r.is "b" do render(:inline=>"i", :template_class=>c, :template_opts=>{:foo=>'b'}) end end body('/a').must_equal "i-a" body('/b').must_equal "i-b" end it "template cache respects :template_block" do c = Class.new do def initialize(path, *, &block) @path = path @block = block end def render(*) "#{@path}-#{@block.call}" end end proca = proc{'a'} procb = proc{'b'} app(:render) do |r| r.is "a" do render(:path=>"i", :template_class=>c, :template_block=>proca) end r.is "b" do render(:path=>"i", :template_class=>c, :template_block=>procb) end end body('/a').must_equal "i-a" body('/b').must_equal "i-b" end it "template cache respects :locals" do template = '<%= @a ? b : c %>' app(:render) do |r| r.is "a" do @a = true render(:inline=>template.dup, :locals=>{:b=>1}) end r.is "b" do @a = true render(:inline=>template.dup, :locals=>{:b=>2, :c=>4}) end r.is "c" do @a = nil render(:inline=>template.dup, :locals=>{:c=>3}) end end body('/a').must_equal "1" body('/b').must_equal "2" body('/c').must_equal "3" end it "Default to :explicit_cache=>true in development mode" do with_rack_env('development') do app(:render){} end app.render_opts[:explicit_cache].must_equal true app(:render){} app.render_opts[:explicit_cache].must_equal false end it "Support :cache=>false plugin option to disable template caching by default, except :cache=>true method option is given" do app(:bare) do plugin :render, :views=>"./spec/views", :cache=>false route do |r| @a = 'a' r.is('a'){render('iv', :cache=>false, :cache_key=>:a)} r.is('b'){render('iv', :cache=>true, :cache_key=>:a)} render('iv', :cache_key=>:a) end end body('/a').strip.must_equal "a" app.render_opts[:cache][:a].must_be_nil body('/b').strip.must_equal "a" app.render_opts[:cache][:a].wont_be_nil body('/c').strip.must_equal "a" app.render_opts[:cache][:a].wont_be_nil end it "Support :cache=>false option to disable template caching" do app(:bare) do plugin :render, :views=>"./spec/views" route do |r| @a = 'a' r.is('a'){render('iv', :cache=>false)} render('iv') end end body('/a').strip.must_equal "a" app.render_opts[:cache][File.expand_path('spec/views/iv.erb')].must_be_nil body('/b').strip.must_equal "a" app.render_opts[:cache][File.expand_path('spec/views/iv.erb')].wont_equal nil end it "Support :cache=>false option to disable template caching even when :cache_key is given" do app(:bare) do plugin :render, :views=>"./spec/views" route do |r| @a = 'a' r.is('a'){render('iv', :cache=>false, :cache_key=>:foo)} render('iv', :cache_key=>:foo) end end body('/a').strip.must_equal "a" app.render_opts[:cache][:foo].must_be_nil body('/b').strip.must_equal "a" app.render_opts[:cache][:foo].wont_equal nil end it "Support :explicit_cache option to disable caching by default, but still allow caching on a per-call basis" do app(:bare) do plugin :render, :views=>"./spec/views", :explicit_cache=>true route do |r| @a = 'a' r.is('a'){render('iv')} render('iv', :cache=>true) end end body('/a').strip.must_equal "a" app.render_opts[:cache][File.expand_path('spec/views/iv.erb')].must_be_nil body('/b').strip.must_equal "a" app.render_opts[:cache][File.expand_path('spec/views/iv.erb')].wont_equal nil end it "Support :explicit_cache plugin option with :cache_key render option" do app(:bare) do plugin :render, :views=>"./spec/views", :explicit_cache=>true route do |r| @a = 'a' r.is('a'){render('iv', :cache_key=>:foo)} render('iv', :cache=>true, :cache_key=>:foo) end end body('/a').strip.must_equal "a" app.render_opts[:cache][:foo].must_be_nil body('/b').strip.must_equal "a" app.render_opts[:cache][:foo].wont_equal nil end it "Support :cache=>true option to enable template caching when :template_block is used" do c = Class.new do def initialize(path, *, &block) @path = path @block = block end def render(*) "#{@path}-#{@block.call}" end end proca = proc{'a'} app(:bare) do plugin :render, :views=>"./spec/views" route do |r| @a = 'a' r.is('a'){render(:path=>'iv', :template_class=>c, :template_block=>proca)} render(:path=>'iv', :template_class=>c, :template_block=>proca, :cache=>true) end end body('/a').strip.must_equal "iv-a" app.render_opts[:cache][['iv', c, nil, nil, proca]].must_be_nil body('/b').strip.must_equal "iv-a" app.render_opts[:cache][['iv', c, nil, nil, proca]].wont_equal nil end it "Support :cache_key option to force the key used when caching, unless :cache=>false option is used" do app(:bare) do plugin :render, :views=>"./spec/views" route do |r| @a = 'a' r.is('a'){render('iv', :cache_key=>:a)} r.is('about'){render('about', :cache_key=>:a, :cache=>false, :locals=>{:title=>'a'})} render('about', :cache_key=>:a) end end body('/a').strip.must_equal "a" body('/b').strip.must_equal "a" body('/about').strip.must_equal "

a

" end it "Support :scope option to override object in which to evaluate the template" do app(:bare) do plugin :render, :views=>"./spec/views" route do |r| render(:inline=>'<%= first %>-<%= last %>', :scope=>[1,2]) end end body.must_equal "1-2" end it "should dup render_opts when subclassing" do c = Class.new(Roda) c.plugin :render, :foo=>:bar sc = Class.new(c) c.render_opts.wont_be_same_as(sc.render_opts) c.render_opts[:foo].must_equal :bar end it "should use a copy of superclass's cache when subclassing" do c = Class.new(Roda) c.plugin :render c.render_opts[:cache][:foo] = 1 sc = Class.new(c) c.render_opts.wont_be_same_as(sc.render_opts) c.render_opts[:cache].wont_be_same_as(sc.render_opts[:cache]) sc.render_opts[:cache][:foo].must_equal 1 end it "should not modifying existing cache if loading the plugin a separate time" do c = Class.new(Roda) c.plugin :render cache = c.render_opts[:cache] c.render_opts[:explicit_cache].must_equal false c.plugin :render c.render_opts[:cache].must_be_same_as cache c.render_opts[:explicit_cache].must_equal false c.plugin :render, :cache=>false c.render_opts[:cache].must_be_same_as cache c.render_opts[:explicit_cache].must_equal true c.plugin :render c.render_opts[:cache].must_be_same_as cache c.render_opts[:explicit_cache].must_equal true end it "render plugin call should not override existing options" do c = Class.new(Roda) c.plugin :render, :layout=>:foo c.plugin :render c.render_opts[:layout].must_equal :foo end it "should not use cache by default in subclass if not caching by default in superclass" do app(:bare) do plugin :render, :views=>"./spec/views", :cache=>false route do |r| view(:inline=>"Hello <%= name %>", :cache_key=>:a, :locals=>{:name => "Agent Smith"}, :layout=>nil) end end body("/inline").strip.must_equal "Hello Agent Smith" Class.new(app).render_opts[:cache][:a].must_be_nil end it "with :check_paths=>true plugin option used" do render_opts = {} app(:bare) do plugin :render, :views=>"./spec/views", :check_paths=>true route do |r| r.get 'a' do render("a", render_opts) end r.get 'c' do render("about/_test", :locals=>{:title=>'a'}) end render("b", render_opts) end end body.strip.must_equal "b" req("/a") req("/c") app.plugin :render, :allowed_paths=>[] proc{req}.must_raise Roda::RodaError proc{req("/a")}.must_raise Roda::RodaError proc{req("/c")}.must_raise Roda::RodaError app.plugin :render, :allowed_paths=>['spec/views/about'] proc{req}.must_raise Roda::RodaError proc{req("/a")}.must_raise Roda::RodaError req("/c") app.plugin :render, :allowed_paths=>%w'spec/views/about spec/views/b' body.strip.must_equal "b" proc{req("/a")}.must_raise Roda::RodaError req("/c") render_opts[:check_paths] = true app.plugin :render, :check_paths=>false body.strip.must_equal "b" proc{req("/a")}.must_raise Roda::RodaError req("/c") render_opts.delete(:check_paths) app.plugin :render body.strip.must_equal "b" req("/a") req("/c") render_opts[:check_paths] = true body.strip.must_equal "b" proc{req("/a")}.must_raise Roda::RodaError req("/c") end it "with a cache_class set" do app(:bare) do test_cache = Class.new(Roda::RodaCache) do def [](key) super end def []=(key, value) super end def test_method end end plugin :render, :views=>"./spec/views", :cache=>true, :cache_class=>test_cache route do |r| view(:inline=>"foo", :layout=>nil) end end body("/inline").strip.must_equal "foo" Class.new(app).render_opts[:cache].must_respond_to :test_method end end end begin require 'tilt' require 'tilt/erubi' rescue LoadError warn "tilt 2 or erubi not installed, skipping render :escape=>true test" else describe ":render plugin :escape option" do before do if defined?(Tilt::ErubiTemplate) && ::Tilt['erb'] != Tilt::ErubiTemplate # Set erubi as default erb template handler Tilt.register(Tilt::ErubiTemplate, 'erb') end end it "should escape inside <%= %> and not inside <%== %>, and handle postfix conditionals" do app(:bare) do plugin :render, :escape=>:erubi route do |r| render(:inline=>'<%= "<>" %> <%== "<>" %><%= "<>" if false %>') end end body.must_equal '<> <>' end it "should allow for per-branch escaping via set_view options" do app(:bare) do plugin :render, :escape=>:erubi plugin :view_options route do |r| set_view_options :template_opts=>{:escape=>false} r.is 'a' do set_view_options :template_opts=>{:engine_class=>render_opts[:template_opts][:engine_class]} render(:inline=>'<%= "<>" %>') end render(:inline=>'<%= "<>" %>') end end body('/a').must_equal '<>' body.must_equal '<>' end end end