sinatra-sinatra 0.9.1.2 → 0.9.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007 Blake Mizerany
1
+ Copyright (c) 2007, 2008, 2009 Blake Mizerany
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
@@ -109,18 +109,41 @@ directory. To use a different views directory:
109
109
 
110
110
  set :views, File.dirname(__FILE__) + '/templates'
111
111
 
112
+ One important thing to remember is that you always have to reference
113
+ templates with symbols, even if they're in a subdirectory (in this
114
+ case use <tt>:'subdir/template'</tt>). Rendering methods will render
115
+ any strings passed to them directly.
116
+
112
117
  === Haml Templates
113
118
 
114
119
  The haml gem/library is required to render HAML templates:
115
120
 
121
+ ## You'll need to require haml in your app
122
+ require 'haml'
123
+
116
124
  get '/' do
117
125
  haml :index
118
126
  end
119
127
 
120
128
  Renders <tt>./views/index.haml</tt>.
121
129
 
130
+ {Haml's options}[http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml.html]
131
+ can be set globally through Sinatra's configurations,
132
+ see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
133
+ and overridden on an individual basis.
134
+
135
+ set :haml, {:format => :html5 } # default Haml format is :xhtml
136
+
137
+ get '/' do
138
+ haml :index, :haml_options => {:format => :html4 } # overridden
139
+ end
140
+
141
+
122
142
  === Erb Templates
123
143
 
144
+ ## You'll need to require erb in your app
145
+ require 'erb'
146
+
124
147
  get '/' do
125
148
  erb :index
126
149
  end
@@ -131,6 +154,9 @@ Renders <tt>./views/index.erb</tt>
131
154
 
132
155
  The builder gem/library is required to render builder templates:
133
156
 
157
+ ## You'll need to require builder in your app
158
+ require 'builder'
159
+
134
160
  get '/' do
135
161
  content_type 'application/xml', :charset => 'utf-8'
136
162
  builder :index
@@ -142,6 +168,9 @@ Renders <tt>./views/index.builder</tt>.
142
168
 
143
169
  The sass gem/library is required to render Sass templates:
144
170
 
171
+ ## You'll need to require haml or sass in your app
172
+ require 'sass'
173
+
145
174
  get '/stylesheet.css' do
146
175
  content_type 'text/css', :charset => 'utf-8'
147
176
  sass :stylesheet
@@ -149,6 +178,19 @@ The sass gem/library is required to render Sass templates:
149
178
 
150
179
  Renders <tt>./views/stylesheet.sass</tt>.
151
180
 
181
+ {Sass' options}[http://haml.hamptoncatlin.com/docs/rdoc/classes/Sass.html]
182
+ can be set globally through Sinatra's configurations,
183
+ see {Options and Configurations}[http://www.sinatrarb.com/configuration.html],
184
+ and overridden on an individual basis.
185
+
186
+ set :sass, {:style => :compact } # default Sass style is :nested
187
+
188
+ get '/stylesheet.css' do
189
+ content_type 'text/css', :charset => 'utf-8'
190
+ sass :stylesheet, :sass_options => {:style => :expanded } # overridden
191
+ end
192
+
193
+
152
194
  === Inline Templates
153
195
 
154
196
  get '/' do
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake/clean'
2
2
  require 'rake/testtask'
3
3
  require 'fileutils'
4
4
 
5
- task :default => [:test]
5
+ task :default => [:test, :compat]
6
6
  task :spec => :test
7
7
 
8
8
  # SPECS ===============================================================
@@ -12,8 +12,20 @@ Rake::TestTask.new(:test) do |t|
12
12
  t.ruby_opts = ['-rubygems'] if defined? Gem
13
13
  end
14
14
 
15
- desc 'Run compatibility specs (requires test/spec)'
16
- task :compat do |t|
15
+ desc "Run < 0.9.x compatibility specs"
16
+ task :compat do
17
+ begin
18
+ require 'mocha'
19
+ rescue LoadError
20
+ puts 'WARN: skipping compat tests. mocha gem required.'
21
+ next
22
+ end
23
+
24
+ if ! system('specrb --help &>/dev/null')
25
+ puts 'WARN: skipping compat tests. test-spec gem required.'
26
+ next
27
+ end
28
+
17
29
  pattern = ENV['TEST'] || '.*'
18
30
  sh "specrb --testcase '#{pattern}' -Ilib:test compat/*_test.rb"
19
31
  end
@@ -183,13 +183,14 @@ context "Haml" do
183
183
  Sinatra.application = nil
184
184
  end
185
185
 
186
- specify 'are empty be default' do
186
+ specify 'default to filename and line of caller' do
187
187
 
188
188
  get '/' do
189
189
  haml 'foo'
190
190
  end
191
191
 
192
- Haml::Engine.expects(:new).with('foo', {}).returns(stub(:render => 'foo'))
192
+ Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
193
+ :line => (__LINE__-4)}).returns(stub(:render => 'foo'))
193
194
 
194
195
  get_it '/'
195
196
  should.be.ok
@@ -202,7 +203,8 @@ context "Haml" do
202
203
  haml 'foo', :options => {:format => :html4}
203
204
  end
204
205
 
205
- Haml::Engine.expects(:new).with('foo', {:format => :html4}).returns(stub(:render => 'foo'))
206
+ Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
207
+ :line => (__LINE__-4), :format => :html4}).returns(stub(:render => 'foo'))
206
208
 
207
209
  get_it '/'
208
210
  should.be.ok
@@ -220,7 +222,8 @@ context "Haml" do
220
222
  haml 'foo'
221
223
  end
222
224
 
223
- Haml::Engine.expects(:new).with('foo', {:format => :html4,
225
+ Haml::Engine.expects(:new).with('foo', {:filename => __FILE__,
226
+ :line => (__LINE__-4), :format => :html4,
224
227
  :escape_html => true}).returns(stub(:render => 'foo'))
225
228
 
226
229
  get_it '/'
@@ -1,6 +1,10 @@
1
1
  require 'rubygems'
2
2
  require 'mocha'
3
3
 
4
+ require 'haml'
5
+ require 'sass'
6
+ require 'builder'
7
+
4
8
  # disable warnings in compat specs.
5
9
  $VERBOSE = nil
6
10
 
@@ -52,6 +52,16 @@ context "Sass" do
52
52
  body.should.equal "#sass {\n background_color: #FFF; }\n"
53
53
  end
54
54
 
55
+ it "passes :sass option to the Sass engine" do
56
+ get '/' do
57
+ sass "#sass\n :background-color #FFF\n :color #000\n", :sass => {:style => :compact}
58
+ end
59
+
60
+ get_it '/'
61
+ should.be.ok
62
+ body.should.equal "#sass { background-color: #FFF; color: #000; }\n"
63
+ end
64
+
55
65
  end
56
66
 
57
67
  end
@@ -3,9 +3,10 @@ require 'time'
3
3
  require 'uri'
4
4
  require 'rack'
5
5
  require 'rack/builder'
6
+ require 'sinatra/showexceptions'
6
7
 
7
8
  module Sinatra
8
- VERSION = '0.9.1.1'
9
+ VERSION = '0.9.1.3'
9
10
 
10
11
  # The request object. See Rack::Request for more info:
11
12
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
@@ -32,16 +33,6 @@ module Sinatra
32
33
  # http://rack.rubyforge.org/doc/classes/Rack/Response.html
33
34
  # http://rack.rubyforge.org/doc/classes/Rack/Response/Helpers.html
34
35
  class Response < Rack::Response
35
- def initialize
36
- @status, @body = 200, []
37
- @header = Rack::Utils::HeaderHash.new({'Content-Type' => 'text/html'})
38
- end
39
-
40
- def write(str)
41
- @body << str.to_s
42
- str
43
- end
44
-
45
36
  def finish
46
37
  @body = block if block_given?
47
38
  if [204, 304].include?(status.to_i)
@@ -231,103 +222,110 @@ module Sinatra
231
222
  # :locals A hash with local variables that should be available
232
223
  # in the template
233
224
  module Templates
234
- def erb(template, options={})
235
- require 'erb' unless defined? ::ERB
236
- render :erb, template, options
225
+ def erb(template, options={}, locals={})
226
+ render :erb, template, options, locals
237
227
  end
238
228
 
239
- def haml(template, options={})
240
- require 'haml' unless defined? ::Haml::Engine
241
- options[:options] ||= self.class.haml if self.class.respond_to? :haml
242
- render :haml, template, options
229
+ def haml(template, options={}, locals={})
230
+ render :haml, template, options, locals
243
231
  end
244
232
 
245
- def sass(template, options={}, &block)
246
- require 'sass' unless defined? ::Sass::Engine
233
+ def sass(template, options={}, locals={})
247
234
  options[:layout] = false
248
- render :sass, template, options
235
+ render :sass, template, options, locals
249
236
  end
250
237
 
251
- def builder(template=nil, options={}, &block)
252
- require 'builder' unless defined? ::Builder
238
+ def builder(template=nil, options={}, locals={}, &block)
253
239
  options, template = template, nil if template.is_a?(Hash)
254
240
  template = lambda { block } if template.nil?
255
- render :builder, template, options
241
+ render :builder, template, options, locals
256
242
  end
257
243
 
258
244
  private
259
- def render(engine, template, options={}) #:nodoc:
260
- data = lookup_template(engine, template, options)
261
- output = __send__("render_#{engine}", template, data, options)
262
- layout, data = lookup_layout(engine, options)
245
+ def render(engine, template, options={}, locals={})
246
+ # merge app-level options
247
+ options = self.class.send(engine).merge(options) if self.class.respond_to?(engine)
248
+
249
+ # extract generic options
250
+ layout = options.delete(:layout)
251
+ layout = :layout if layout.nil? || layout == true
252
+ views = options.delete(:views) || self.class.views || "./views"
253
+ locals = options.delete(:locals) || locals || {}
254
+
255
+ # render template
256
+ data, options[:filename], options[:line] = lookup_template(engine, template, views)
257
+ output = __send__("render_#{engine}", template, data, options, locals)
258
+
259
+ # render layout
263
260
  if layout
264
- __send__("render_#{engine}", layout, data, options) { output }
265
- else
266
- output
261
+ data, options[:filename], options[:line] = lookup_layout(engine, layout, views)
262
+ if data
263
+ output = __send__("render_#{engine}", layout, data, options, {}) { output }
264
+ end
267
265
  end
266
+
267
+ output
268
268
  end
269
269
 
270
- def lookup_template(engine, template, options={})
270
+ def lookup_template(engine, template, views_dir, filename = nil, line = nil)
271
271
  case template
272
272
  when Symbol
273
273
  if cached = self.class.templates[template]
274
- lookup_template(engine, cached, options)
274
+ lookup_template(engine, cached[:template], views_dir, cached[:filename], cached[:line])
275
275
  else
276
- ::File.read(template_path(engine, template, options))
276
+ path = ::File.join(views_dir, "#{template}.#{engine}")
277
+ [ ::File.read(path), path, 1 ]
277
278
  end
278
279
  when Proc
279
- template.call
280
+ filename, line = self.class.caller_locations.first if filename.nil?
281
+ [ template.call, filename, line.to_i ]
280
282
  when String
281
- template
283
+ filename, line = self.class.caller_locations.first if filename.nil?
284
+ [ template, filename, line.to_i ]
282
285
  else
283
286
  raise ArgumentError
284
287
  end
285
288
  end
286
289
 
287
- def lookup_layout(engine, options)
288
- return if options[:layout] == false
289
- options.delete(:layout) if options[:layout] == true
290
- template = options[:layout] || :layout
291
- data = lookup_template(engine, template, options)
292
- [template, data]
290
+ def lookup_layout(engine, template, views_dir)
291
+ lookup_template(engine, template, views_dir)
293
292
  rescue Errno::ENOENT
294
293
  nil
295
294
  end
296
295
 
297
- def template_path(engine, template, options={})
298
- views_dir =
299
- options[:views_directory] || self.options.views || "./views"
300
- "#{views_dir}/#{template}.#{engine}"
301
- end
302
-
303
- def render_erb(template, data, options, &block)
296
+ def render_erb(template, data, options, locals, &block)
304
297
  original_out_buf = @_out_buf
305
298
  data = data.call if data.kind_of? Proc
306
299
 
307
300
  instance = ::ERB.new(data, nil, nil, '@_out_buf')
308
- locals = options[:locals] || {}
309
301
  locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
310
302
 
311
- src = "#{locals_assigns.join("\n")}\n#{instance.src}"
312
- eval src, binding, '(__ERB__)', locals_assigns.length + 1
303
+ filename = options.delete(:filename) || '(__ERB__)'
304
+ line = options.delete(:line) || 1
305
+ line -= 1 if instance.src =~ /^#coding:/
306
+
307
+ render_binding = binding
308
+ eval locals_assigns.join("\n"), render_binding
309
+ eval instance.src, render_binding, filename, line
313
310
  @_out_buf, result = original_out_buf, @_out_buf
314
311
  result
315
312
  end
316
313
 
317
- def render_haml(template, data, options, &block)
318
- engine = ::Haml::Engine.new(data, options[:options] || {})
319
- engine.render(self, options[:locals] || {}, &block)
314
+ def render_haml(template, data, options, locals, &block)
315
+ ::Haml::Engine.new(data, options).render(self, locals, &block)
320
316
  end
321
317
 
322
- def render_sass(template, data, options, &block)
323
- engine = ::Sass::Engine.new(data, options[:sass] || {})
324
- engine.render
318
+ def render_sass(template, data, options, locals, &block)
319
+ ::Sass::Engine.new(data, options).render
325
320
  end
326
321
 
327
- def render_builder(template, data, options, &block)
328
- xml = ::Builder::XmlMarkup.new(:indent => 2)
322
+ def render_builder(template, data, options, locals, &block)
323
+ options = { :indent => 2 }.merge(options)
324
+ filename = options.delete(:filename) || '<BUILDER>'
325
+ line = options.delete(:line) || 1
326
+ xml = ::Builder::XmlMarkup.new(options)
329
327
  if data.respond_to?(:to_str)
330
- eval data.to_str, binding, '<BUILDER>', 1
328
+ eval data.to_str, binding, filename, line
331
329
  elsif data.kind_of?(Proc)
332
330
  data.call(xml)
333
331
  end
@@ -409,7 +407,13 @@ module Sinatra
409
407
  private
410
408
  # Run before filters and then locate and run a matching route.
411
409
  def route!
412
- @params = nested_params(@request.params)
410
+ # enable nested params in Rack < 1.0; allow indifferent access
411
+ @params =
412
+ if Rack::Utils.respond_to?(:parse_nested_query)
413
+ indifferent_params(@request.params)
414
+ else
415
+ nested_params(@request.params)
416
+ end
413
417
 
414
418
  # before filters
415
419
  self.class.filters.each { |block| instance_eval(&block) }
@@ -443,24 +447,45 @@ module Sinatra
443
447
  catch(:pass) do
444
448
  conditions.each { |cond|
445
449
  throw :pass if instance_eval(&cond) == false }
446
- throw :halt, instance_eval(&block)
450
+ route_eval(&block)
447
451
  end
448
452
  end
449
453
  end
450
454
  end
451
455
 
452
- # No matching route found or all routes passed -- forward downstream
453
- # when running as middleware; 404 when running as normal app.
456
+ route_missing
457
+ end
458
+
459
+ # Run a route block and throw :halt with the result.
460
+ def route_eval(&block)
461
+ throw :halt, instance_eval(&block)
462
+ end
463
+
464
+ # No matching route was found or all routes passed. The default
465
+ # implementation is to forward the request downstream when running
466
+ # as middleware (@app is non-nil); when no downstream app is set, raise
467
+ # a NotFound exception. Subclasses can override this method to perform
468
+ # custom route miss logic.
469
+ def route_missing
454
470
  if @app
455
- # Call bypassed method before forward to catch behavior that should
456
- # happen even if no routes are hit.
457
- bypassed if respond_to?(:bypassed)
458
471
  forward
459
472
  else
460
473
  raise NotFound
461
474
  end
462
475
  end
463
476
 
477
+ # Enable string or symbol key access to the nested params hash.
478
+ def indifferent_params(params)
479
+ params = indifferent_hash.merge(params)
480
+ params.each do |key, value|
481
+ next unless value.is_a?(Hash)
482
+ params[key] = indifferent_params(value)
483
+ end
484
+ end
485
+
486
+ # Recursively replace the params hash with a nested indifferent
487
+ # hash. Rack 1.0 has a built in implementation of this method - remove
488
+ # this once Rack 1.0 is required.
464
489
  def nested_params(params)
465
490
  return indifferent_hash.merge(params) if !params.keys.join.include?('[')
466
491
  params.inject indifferent_hash do |res, (key,val)|
@@ -532,7 +557,7 @@ module Sinatra
532
557
  @env['sinatra.error'] = boom
533
558
 
534
559
  dump_errors!(boom) if options.dump_errors?
535
- raise boom if options.raise_errors?
560
+ raise boom if options.raise_errors? || options.show_exceptions?
536
561
 
537
562
  @response.status = 500
538
563
  error_block! boom.class, Exception
@@ -624,7 +649,8 @@ module Sinatra
624
649
 
625
650
  # Define a named template. The block must return the template source.
626
651
  def template(name, &block)
627
- templates[name] = block
652
+ filename, line = caller_locations.first
653
+ templates[name] = { :filename => filename, :line => line, :template => block }
628
654
  end
629
655
 
630
656
  # Define the layout template. The block must return the template source.
@@ -636,12 +662,18 @@ module Sinatra
636
662
  # when no file is specified.
637
663
  def use_in_file_templates!(file=nil)
638
664
  file ||= caller_files.first
639
- if data = ::IO.read(file).split('__END__')[1]
665
+ app, data =
666
+ ::IO.read(file).split(/^__END__$/, 2) rescue nil
667
+
668
+ if data
640
669
  data.gsub!(/\r\n/, "\n")
670
+ lines = app.count("\n") + 1
641
671
  template = nil
642
672
  data.each_line do |line|
673
+ lines += 1
643
674
  if line =~ /^@@\s*(.*)/
644
- template = templates[$1.to_sym] = ''
675
+ template = ''
676
+ templates[$1.to_sym] = { :filename => file, :line => lines, :template => template }
645
677
  elsif template
646
678
  template << line
647
679
  end
@@ -711,10 +743,10 @@ module Sinatra
711
743
  route('HEAD', path, opts, &block)
712
744
  end
713
745
 
714
- def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
715
- def post(path, opts={}, &bk); route 'POST', path, opts, &bk; end
716
- def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk; end
717
- def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end
746
+ def put(path, opts={}, &bk); route 'PUT', path, opts, &bk end
747
+ def post(path, opts={}, &bk); route 'POST', path, opts, &bk end
748
+ def delete(path, opts={}, &bk); route 'DELETE', path, opts, &bk end
749
+ def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk end
718
750
 
719
751
  private
720
752
  def route(verb, path, opts={}, &block)
@@ -734,7 +766,7 @@ module Sinatra
734
766
  lambda { unbound_method.bind(self).call }
735
767
  end
736
768
 
737
- invoke_hook(:route_added, verb, path)
769
+ invoke_hook(:route_added, verb, path, block)
738
770
 
739
771
  (routes[verb] ||= []).
740
772
  push([pattern, keys, conditions, block]).last
@@ -790,14 +822,14 @@ module Sinatra
790
822
  end
791
823
  end
792
824
 
793
- def development? ; environment == :development ; end
794
- def test? ; environment == :test ; end
795
- def production? ; environment == :production ; end
825
+ def development?; environment == :development end
826
+ def production?; environment == :production end
827
+ def test?; environment == :test end
796
828
 
797
829
  # Set configuration options for Sinatra and/or the app.
798
830
  # Allows scoping of settings for certain environments.
799
831
  def configure(*envs, &block)
800
- yield if envs.empty? || envs.include?(environment.to_sym)
832
+ yield self if envs.empty? || envs.include?(environment.to_sym)
801
833
  end
802
834
 
803
835
  # Use the specified Rack middleware
@@ -836,8 +868,10 @@ module Sinatra
836
868
  def new(*args, &bk)
837
869
  builder = Rack::Builder.new
838
870
  builder.use Rack::Session::Cookie if sessions? && !test?
839
- builder.use Rack::CommonLogger if logging?
840
- builder.use Rack::MethodOverride if methodoverride?
871
+ builder.use Rack::CommonLogger if logging?
872
+ builder.use Rack::MethodOverride if methodoverride?
873
+ builder.use ShowExceptions if show_exceptions?
874
+
841
875
  @middleware.each { |c,a,b| builder.use(c, *a, &b) }
842
876
  builder.run super
843
877
  builder.to_app
@@ -871,7 +905,7 @@ module Sinatra
871
905
  servers = Array(self.server)
872
906
  servers.each do |server_name|
873
907
  begin
874
- return Rack::Handler.get(server_name)
908
+ return Rack::Handler.get(server_name.capitalize)
875
909
  rescue LoadError
876
910
  rescue NameError
877
911
  end
@@ -898,24 +932,32 @@ module Sinatra
898
932
  send :define_method, message, &block
899
933
  end
900
934
 
935
+ public
936
+ CALLERS_TO_IGNORE = [
937
+ /lib\/sinatra.*\.rb$/, # all sinatra code
938
+ /\(.*\)/, # generated code
939
+ /custom_require\.rb$/, # rubygems require hacks
940
+ /active_support/, # active_support require hacks
941
+ ] unless self.const_defined?('CALLERS_TO_IGNORE')
942
+
901
943
  # Like Kernel#caller but excluding certain magic entries and without
902
944
  # line / method information; the resulting array contains filenames only.
903
945
  def caller_files
904
- ignore = [
905
- /lib\/sinatra.*\.rb$/, # all sinatra code
906
- /\(.*\)/, # generated code
907
- /custom_require\.rb$/, # rubygems require hacks
908
- /active_support/, # active_support require hacks
909
- ]
946
+ caller_locations.
947
+ map { |file,line| file }
948
+ end
949
+
950
+ def caller_locations
910
951
  caller(1).
911
- map { |line| line.split(/:\d/, 2).first }.
912
- reject { |file| ignore.any? { |pattern| file =~ pattern } }
952
+ map { |line| line.split(/:(?=\d|in )/)[0,2] }.
953
+ reject { |file,line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } }
913
954
  end
914
955
  end
915
956
 
916
957
  set :raise_errors, true
917
958
  set :dump_errors, false
918
959
  set :clean_trace, true
960
+ set :show_exceptions, Proc.new { development? }
919
961
  set :sessions, false
920
962
  set :logging, false
921
963
  set :methodoverride, false
@@ -957,6 +999,8 @@ module Sinatra
957
999
  end
958
1000
 
959
1001
  error NotFound do
1002
+ content_type 'text/html'
1003
+
960
1004
  (<<-HTML).gsub(/^ {8}/, '')
961
1005
  <!DOCTYPE html>
962
1006
  <html>
@@ -978,35 +1022,6 @@ module Sinatra
978
1022
  </html>
979
1023
  HTML
980
1024
  end
981
-
982
- error do
983
- next unless err = request.env['sinatra.error']
984
- heading = err.class.name + ' - ' + err.message.to_s
985
- (<<-HTML).gsub(/^ {8}/, '')
986
- <!DOCTYPE html>
987
- <html>
988
- <head>
989
- <style type="text/css">
990
- body {font-family:verdana;color:#333}
991
- #c {margin-left:20px}
992
- h1 {color:#1D6B8D;margin:0;margin-top:-30px}
993
- h2 {color:#1D6B8D;font-size:18px}
994
- pre {border-left:2px solid #ddd;padding-left:10px;color:#000}
995
- img {margin-top:10px}
996
- </style>
997
- </head>
998
- <body>
999
- <div id="c">
1000
- <img src="/__sinatra__/500.png">
1001
- <h1>#{escape_html(heading)}</h1>
1002
- <pre>#{escape_html(clean_backtrace(err.backtrace) * "\n")}</pre>
1003
- <h2>Params</h2>
1004
- <pre>#{escape_html(params.inspect)}</pre>
1005
- </div>
1006
- </body>
1007
- </html>
1008
- HTML
1009
- end
1010
1025
  end
1011
1026
  end
1012
1027