mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
parent
801163e9f3
commit
cfdf97d495
8 changed files with 199 additions and 27 deletions
|
@ -183,13 +183,14 @@ context "Haml" do
|
|||
Sinatra.application = nil
|
||||
end
|
||||
|
||||
specify 'are empty be default' do
|
||||
specify 'default to filename and line of caller' do
|
||||
|
||||
get '/' do
|
||||
haml 'foo'
|
||||
end
|
||||
|
||||
Haml::Engine.expects(:new).with('foo', {}).returns(stub(:render => 'foo'))
|
||||
Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
|
||||
:line => (__LINE__-4)}).returns(stub(:render => 'foo'))
|
||||
|
||||
get_it '/'
|
||||
should.be.ok
|
||||
|
@ -202,7 +203,8 @@ context "Haml" do
|
|||
haml 'foo', :options => {:format => :html4}
|
||||
end
|
||||
|
||||
Haml::Engine.expects(:new).with('foo', {:format => :html4}).returns(stub(:render => 'foo'))
|
||||
Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
|
||||
:line => (__LINE__-4), :format => :html4}).returns(stub(:render => 'foo'))
|
||||
|
||||
get_it '/'
|
||||
should.be.ok
|
||||
|
@ -220,7 +222,8 @@ context "Haml" do
|
|||
haml 'foo'
|
||||
end
|
||||
|
||||
Haml::Engine.expects(:new).with('foo', {:format => :html4,
|
||||
Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
|
||||
:line => (__LINE__-4), :format => :html4,
|
||||
:escape_html => true}).returns(stub(:render => 'foo'))
|
||||
|
||||
get_it '/'
|
||||
|
|
|
@ -253,30 +253,35 @@ module Sinatra
|
|||
locals = options.delete(:locals) || locals || {}
|
||||
|
||||
# render template
|
||||
data = lookup_template(engine, template, views)
|
||||
data, options[:filename], options[:line] = lookup_template(engine, template, views)
|
||||
output = __send__("render_#{engine}", template, data, options, locals)
|
||||
|
||||
# render layout
|
||||
if layout && data = lookup_layout(engine, layout, views)
|
||||
__send__("render_#{engine}", layout, data, options, {}) { output }
|
||||
else
|
||||
output
|
||||
if layout
|
||||
data, options[:filename], options[:line] = lookup_layout(engine, layout, views)
|
||||
if data
|
||||
output = __send__("render_#{engine}", layout, data, options, {}) { output }
|
||||
end
|
||||
end
|
||||
|
||||
output
|
||||
end
|
||||
|
||||
def lookup_template(engine, template, views_dir)
|
||||
def lookup_template(engine, template, views_dir, filename = nil, line = nil)
|
||||
case template
|
||||
when Symbol
|
||||
if cached = self.class.templates[template]
|
||||
lookup_template(engine, cached, views_dir)
|
||||
lookup_template(engine, cached[:template], views_dir, cached[:filename], cached[:line])
|
||||
else
|
||||
path = ::File.join(views_dir, "#{template}.#{engine}")
|
||||
::File.read(path)
|
||||
[ ::File.read(path), path, 1 ]
|
||||
end
|
||||
when Proc
|
||||
template.call
|
||||
filename, line = self.class.caller_locations.first if filename.nil?
|
||||
[ template.call, filename, line.to_i ]
|
||||
when String
|
||||
template
|
||||
filename, line = self.class.caller_locations.first if filename.nil?
|
||||
[ template, filename, line.to_i ]
|
||||
else
|
||||
raise ArgumentError
|
||||
end
|
||||
|
@ -295,8 +300,13 @@ module Sinatra
|
|||
instance = ::ERB.new(data, nil, nil, '@_out_buf')
|
||||
locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
|
||||
|
||||
src = "#{locals_assigns.join("\n")}\n#{instance.src}"
|
||||
eval src, binding, '(__ERB__)', locals_assigns.length + 1
|
||||
filename = options.delete(:filename) || '(__ERB__)'
|
||||
line = options.delete(:line) || 1
|
||||
line -= 1 if instance.src =~ /^#coding:/
|
||||
|
||||
render_binding = binding
|
||||
eval locals_assigns.join("\n"), render_binding
|
||||
eval instance.src, render_binding, filename, line
|
||||
@_out_buf, result = original_out_buf, @_out_buf
|
||||
result
|
||||
end
|
||||
|
@ -311,9 +321,11 @@ module Sinatra
|
|||
|
||||
def render_builder(template, data, options, locals, &block)
|
||||
options = { :indent => 2 }.merge(options)
|
||||
filename = options.delete(:filename) || '<BUILDER>'
|
||||
line = options.delete(:line) || 1
|
||||
xml = ::Builder::XmlMarkup.new(options)
|
||||
if data.respond_to?(:to_str)
|
||||
eval data.to_str, binding, '<BUILDER>', 1
|
||||
eval data.to_str, binding, filename, line
|
||||
elsif data.kind_of?(Proc)
|
||||
data.call(xml)
|
||||
end
|
||||
|
@ -619,7 +631,8 @@ module Sinatra
|
|||
|
||||
# Define a named template. The block must return the template source.
|
||||
def template(name, &block)
|
||||
templates[name] = block
|
||||
filename, line = caller_locations.first
|
||||
templates[name] = { :filename => filename, :line => line, :template => block }
|
||||
end
|
||||
|
||||
# Define the layout template. The block must return the template source.
|
||||
|
@ -631,19 +644,18 @@ module Sinatra
|
|||
# when no file is specified.
|
||||
def use_in_file_templates!(file=nil)
|
||||
file ||= caller_files.first
|
||||
|
||||
begin
|
||||
data = ::IO.read(file).split(/^__END__$/)[1]
|
||||
rescue
|
||||
data = nil
|
||||
end
|
||||
app, data =
|
||||
::IO.read(file).split(/^__END__$/, 2) rescue nil
|
||||
|
||||
if data
|
||||
data.gsub!(/\r\n/, "\n")
|
||||
lines = app.count("\n") + 1
|
||||
template = nil
|
||||
data.each_line do |line|
|
||||
lines += 1
|
||||
if line =~ /^@@\s*(.*)/
|
||||
template = templates[$1.to_sym] = ''
|
||||
template = ''
|
||||
templates[$1.to_sym] = { :filename => file, :line => lines, :template => template }
|
||||
elsif template
|
||||
template << line
|
||||
end
|
||||
|
@ -902,6 +914,7 @@ module Sinatra
|
|||
send :define_method, message, &block
|
||||
end
|
||||
|
||||
public
|
||||
CALLERS_TO_IGNORE = [
|
||||
/lib\/sinatra.*\.rb$/, # all sinatra code
|
||||
/\(.*\)/, # generated code
|
||||
|
|
145
test/render_backtrace_test.rb
Normal file
145
test/render_backtrace_test.rb
Normal file
|
@ -0,0 +1,145 @@
|
|||
require File.dirname(__FILE__) + '/helper'
|
||||
|
||||
require 'sass/error'
|
||||
|
||||
class RenderBacktraceTest < Test::Unit::TestCase
|
||||
VIEWS = File.dirname(__FILE__) + '/views'
|
||||
|
||||
def assert_raise_at(filename, line, exception = RuntimeError)
|
||||
f, l = nil
|
||||
assert_raise(exception) do
|
||||
begin
|
||||
get('/')
|
||||
rescue => e
|
||||
f, l = e.backtrace.first.split(':')
|
||||
raise
|
||||
end
|
||||
end
|
||||
assert_equal(filename, f, "expected #{exception.name} in #{filename}, was #{f}")
|
||||
assert_equal(line, l.to_i, "expected #{exception.name} in #{filename} at line #{line}, was at line #{l}")
|
||||
end
|
||||
|
||||
def backtrace_app(&block)
|
||||
mock_app {
|
||||
use_in_file_templates!
|
||||
set :views, RenderBacktraceTest::VIEWS
|
||||
template :builder_template do
|
||||
'raise "error"'
|
||||
end
|
||||
template :erb_template do
|
||||
'<% raise "error" %>'
|
||||
end
|
||||
template :haml_template do
|
||||
'%h1= raise "error"'
|
||||
end
|
||||
template :sass_template do
|
||||
'+syntax-error'
|
||||
end
|
||||
get '/', &block
|
||||
}
|
||||
end
|
||||
|
||||
it "provides backtrace for Builder template" do
|
||||
backtrace_app { builder :error }
|
||||
assert_raise_at(File.join(VIEWS,'error.builder'), 2)
|
||||
end
|
||||
|
||||
it "provides backtrace for ERB template" do
|
||||
backtrace_app { erb :error }
|
||||
assert_raise_at(File.join(VIEWS,'error.erb'), 2)
|
||||
end
|
||||
|
||||
it "provides backtrace for HAML template" do
|
||||
backtrace_app { haml :error }
|
||||
assert_raise_at(File.join(VIEWS,'error.haml'), 2)
|
||||
end
|
||||
|
||||
it "provides backtrace for Sass template" do
|
||||
backtrace_app { sass :error }
|
||||
assert_raise_at(File.join(VIEWS,'error.sass'), 2, Sass::SyntaxError)
|
||||
end
|
||||
|
||||
it "provides backtrace for ERB template with locals" do
|
||||
backtrace_app { erb :error, {}, :french => true }
|
||||
assert_raise_at(File.join(VIEWS,'error.erb'), 3)
|
||||
end
|
||||
|
||||
it "provides backtrace for HAML template with locals" do
|
||||
backtrace_app { haml :error, {}, :french => true }
|
||||
assert_raise_at(File.join(VIEWS,'error.haml'), 3)
|
||||
end
|
||||
|
||||
it "provides backtrace for inline Builder string" do
|
||||
backtrace_app { builder "raise 'Ack! Thbbbt!'"}
|
||||
assert_raise_at(__FILE__, (__LINE__-1))
|
||||
end
|
||||
|
||||
it "provides backtrace for inline ERB string" do
|
||||
backtrace_app { erb "<% raise 'bidi-bidi-bidi' %>" }
|
||||
assert_raise_at(__FILE__, (__LINE__-1))
|
||||
end
|
||||
|
||||
it "provides backtrace for inline HAML string" do
|
||||
backtrace_app { haml "%h1= raise 'Lions and tigers and bears! Oh, my!'" }
|
||||
assert_raise_at(__FILE__, (__LINE__-1))
|
||||
end
|
||||
|
||||
# it "provides backtrace for inline Sass string" do
|
||||
# backtrace_app { sass '+buh-bye' }
|
||||
# assert_raise_at(__FILE__, (__LINE__-1), Sass::SyntaxError)
|
||||
# end
|
||||
|
||||
it "provides backtrace for named Builder template" do
|
||||
backtrace_app { builder :builder_template }
|
||||
assert_raise_at(__FILE__, (__LINE__-68))
|
||||
end
|
||||
|
||||
it "provides backtrace for named ERB template" do
|
||||
backtrace_app { erb :erb_template }
|
||||
assert_raise_at(__FILE__, (__LINE__-70))
|
||||
end
|
||||
|
||||
it "provides backtrace for named HAML template" do
|
||||
backtrace_app { haml :haml_template }
|
||||
assert_raise_at(__FILE__, (__LINE__-72))
|
||||
end
|
||||
|
||||
# it "provides backtrace for named Sass template" do
|
||||
# backtrace_app { sass :sass_template }
|
||||
# assert_raise_at(__FILE__, (__LINE__-74), Sass::SyntaxError)
|
||||
# end
|
||||
|
||||
it "provides backtrace for in file Builder template" do
|
||||
backtrace_app { builder :builder_in_file }
|
||||
assert_raise_at(__FILE__, (__LINE__+22))
|
||||
end
|
||||
|
||||
it "provides backtrace for in file ERB template" do
|
||||
backtrace_app { erb :erb_in_file }
|
||||
assert_raise_at(__FILE__, (__LINE__+20))
|
||||
end
|
||||
|
||||
it "provides backtrace for in file HAML template" do
|
||||
backtrace_app { haml :haml_in_file }
|
||||
assert_raise_at(__FILE__, (__LINE__+18))
|
||||
end
|
||||
|
||||
# it "provides backtrace for in file Sass template" do
|
||||
# backtrace_app { sass :sass_in_file }
|
||||
# assert_raise_at(__FILE__, (__LINE__+16), Sass::SyntaxError)
|
||||
# end
|
||||
end
|
||||
|
||||
__END__
|
||||
|
||||
@@ builder_in_file
|
||||
raise "bif"
|
||||
|
||||
@@ erb_in_file
|
||||
<% raise "bam" %>
|
||||
|
||||
@@ haml_in_file
|
||||
%h1= raise "pow"
|
||||
|
||||
@@ sass_in_file
|
||||
+blam
|
|
@ -72,8 +72,8 @@ class TemplatesTest < Test::Unit::TestCase
|
|||
mock_app {
|
||||
use_in_file_templates!
|
||||
}
|
||||
assert_equal "this is foo\n\n", @app.templates[:foo]
|
||||
assert_equal "X\n= yield\nX\n", @app.templates[:layout]
|
||||
assert_equal "this is foo\n\n", @app.templates[:foo][:template]
|
||||
assert_equal "X\n= yield\nX\n", @app.templates[:layout][:template]
|
||||
end
|
||||
|
||||
test 'use_in_file_templates simply ignores IO errors' do
|
||||
|
|
3
test/views/error.builder
Normal file
3
test/views/error.builder
Normal file
|
@ -0,0 +1,3 @@
|
|||
xml.error do
|
||||
raise "goodbye"
|
||||
end
|
3
test/views/error.erb
Normal file
3
test/views/error.erb
Normal file
|
@ -0,0 +1,3 @@
|
|||
Hello <%= 'World' %>
|
||||
<% raise 'Goodbye' unless defined?(french) && french %>
|
||||
<% raise 'Au revoir' if defined?(french) && french %>
|
3
test/views/error.haml
Normal file
3
test/views/error.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%h1 Hello From Haml
|
||||
= raise 'goodbye' unless defined?(french) && french
|
||||
= raise 'au revoir' if defined?(french) && french
|
2
test/views/error.sass
Normal file
2
test/views/error.sass
Normal file
|
@ -0,0 +1,2 @@
|
|||
#sass
|
||||
+argle-bargle
|
Loading…
Reference in a new issue