sinatra 1.4.0.d → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

@@ -17,7 +17,7 @@ module Sinatra
17
17
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
18
18
  class Request < Rack::Request
19
19
  HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/
20
- HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+|\*))\s*(?:;#{HEADER_PARAM})*/
20
+ HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/
21
21
 
22
22
  # Returns an array of acceptable media types for the response
23
23
  def accept
@@ -71,9 +71,10 @@ module Sinatra
71
71
  [key, value]
72
72
  end
73
73
 
74
- @type = entry[/[^;]+/].delete(' ')
74
+ @entry = entry
75
+ @type = entry[/[^;]+/].delete(' ')
75
76
  @params = Hash[params]
76
- @q = @params.delete('q') { "1.0" }.to_f
77
+ @q = @params.delete('q') { "1.0" }.to_f
77
78
  end
78
79
 
79
80
  def <=>(other)
@@ -85,13 +86,21 @@ module Sinatra
85
86
  [ @q, -@type.count('*'), @params.size ]
86
87
  end
87
88
 
88
- def [](param)
89
- @params[param]
90
- end
91
-
92
89
  def to_str
93
90
  @type
94
91
  end
92
+
93
+ def to_s(full = false)
94
+ full ? entry : to_str
95
+ end
96
+
97
+ def respond_to?(*args)
98
+ super or to_str.respond_to?(*args)
99
+ end
100
+
101
+ def method_missing(*args, &block)
102
+ to_str.send(*args, &block)
103
+ end
95
104
  end
96
105
  end
97
106
 
@@ -1044,7 +1053,11 @@ module Sinatra
1044
1053
  rescue ::Exception => boom
1045
1054
  invoke { handle_exception!(boom) }
1046
1055
  ensure
1047
- filter! :after unless env['sinatra.static_file']
1056
+ begin
1057
+ filter! :after unless env['sinatra.static_file']
1058
+ rescue ::Exception => boom
1059
+ invoke { handle_exception!(boom) } unless @env['sinatra.error']
1060
+ end
1048
1061
  end
1049
1062
 
1050
1063
  # Error handling during requests.
@@ -1211,7 +1224,7 @@ module Sinatra
1211
1224
  template name, &block
1212
1225
  end
1213
1226
 
1214
- # Load embeded templates from the file; uses the caller's __FILE__
1227
+ # Load embedded templates from the file; uses the caller's __FILE__
1215
1228
  # when no file is specified.
1216
1229
  def inline_templates=(file = nil)
1217
1230
  file = (file.nil? || file == true) ? (caller_files.first || File.expand_path($0)) : file
@@ -1246,7 +1259,8 @@ module Sinatra
1246
1259
 
1247
1260
  # Lookup or register a mime type in Rack's mime registry.
1248
1261
  def mime_type(type, value = nil)
1249
- return type if type.nil? || type.to_s.include?('/')
1262
+ return type if type.nil?
1263
+ return type.to_s if type.to_s.include?('/')
1250
1264
  type = ".#{type}" unless type.to_s[0] == ?.
1251
1265
  return Rack::Mime.mime_type(type, nil) unless value
1252
1266
  Rack::Mime::MIME_TYPES[type] = value
@@ -1745,7 +1759,7 @@ module Sinatra
1745
1759
 
1746
1760
  set :run, false # start server via at-exit hook?
1747
1761
  set :running, false # is the built-in server running now?
1748
- set :server, %w[http webrick]
1762
+ set :server, %w[HTTP webrick]
1749
1763
  set :bind, Proc.new { development? ? 'localhost' : '0.0.0.0' }
1750
1764
  set :port, Integer(ENV['PORT'] || 4567)
1751
1765
 
@@ -1,3 +1,3 @@
1
1
  module Sinatra
2
- VERSION = '1.4.0.d'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -67,7 +67,7 @@ class BaseTest < Test::Unit::TestCase
67
67
  assert_equal Sinatra::Base.settings, Sinatra::Base.new.settings
68
68
  end
69
69
 
70
- it 'expses helpers' do
70
+ it 'exposes helpers' do
71
71
  assert_equal 'image/jpeg', Sinatra::Base.new.helpers.mime_type(:jpg)
72
72
  end
73
73
  end
@@ -82,7 +82,9 @@ private
82
82
  end
83
83
 
84
84
  def self.test_name(name)
85
- "test_#{sanitize_name(name).gsub(/\s+/,'_')}".to_sym
85
+ name = "test_#{sanitize_name(name).gsub(/\s+/,'_')}_0"
86
+ name = name.succ while method_defined? name
87
+ name.to_sym
86
88
  end
87
89
 
88
90
  def self.sanitize_name(name)
@@ -86,7 +86,7 @@ class ERBTest < Test::Unit::TestCase
86
86
  assert_equal '<outer><inner>hi</inner></outer>', body
87
87
  end
88
88
 
89
- it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
89
+ it "can render truly nested layouts by accepting a layout and a block with the contents" do
90
90
  mock_app do
91
91
  template(:main_outer_layout) { "<h1>Title</h1>\n<%= yield %>" }
92
92
  template(:an_inner_layout) { "<h2>Subtitle</h2>\n<%= yield %>" }
@@ -153,6 +153,27 @@ class BeforeFilterTest < Test::Unit::TestCase
153
153
  get '/foo/bar'
154
154
  assert_equal subpath, 'bar'
155
155
  end
156
+
157
+ it 'can catch exceptions in before filters and handle them properly' do
158
+ doodle = ''
159
+ mock_app do
160
+ before do
161
+ doodle += 'This begins'
162
+ raise StandardError, "before"
163
+ end
164
+ get "/" do
165
+ doodle = 'and runs'
166
+ end
167
+ error 500 do
168
+ "Error handled #{env['sinatra.error'].message}"
169
+ end
170
+ end
171
+
172
+ doodle = ''
173
+ get '/'
174
+ assert_equal 'Error handled before', body
175
+ assert_equal 'This begins', doodle
176
+ end
156
177
  end
157
178
 
158
179
  class AfterFilterTest < Test::Unit::TestCase
@@ -267,7 +288,7 @@ class AfterFilterTest < Test::Unit::TestCase
267
288
  assert ran_filter
268
289
  end
269
290
 
270
- it 'changes to path_info from a pattern matching before filter are respoected when routing' do
291
+ it 'changes to path_info from a pattern matching before filter are respected when routing' do
271
292
  mock_app do
272
293
  before('/foo') { request.path_info = '/bar' }
273
294
  get('/bar') { 'blah' }
@@ -434,4 +455,33 @@ class AfterFilterTest < Test::Unit::TestCase
434
455
  get('/', {}, { 'HTTP_ACCEPT' => '*/*' })
435
456
  assert_body 'txt'
436
457
  end
458
+
459
+ it 'can catch exceptions in after filters and handle them properly' do
460
+ doodle = ''
461
+ mock_app do
462
+ after do
463
+ doodle += ' and after'
464
+ raise StandardError, "after"
465
+ end
466
+ get "/foo" do
467
+ doodle = 'Been now'
468
+ raise StandardError, "now"
469
+ end
470
+ get "/" do
471
+ doodle = 'Been now'
472
+ end
473
+ error 500 do
474
+ "Error handled #{env['sinatra.error'].message}"
475
+ end
476
+ end
477
+
478
+ get '/foo'
479
+ assert_equal 'Error handled now', body
480
+ assert_equal 'Been now and after', doodle
481
+
482
+ doodle = ''
483
+ get '/'
484
+ assert_equal 'Error handled after', body
485
+ assert_equal 'Been now and after', doodle
486
+ end
437
487
  end
@@ -85,7 +85,7 @@ class HAMLTest < Test::Unit::TestCase
85
85
  assert_equal "bar\n", body
86
86
  end
87
87
 
88
- it "can rendere truly nested layouts by accepting a layout and a block with the contents" do
88
+ it "can render truly nested layouts by accepting a layout and a block with the contents" do
89
89
  mock_app do
90
90
  template(:main_outer_layout) { "%h1 Title\n= yield" }
91
91
  template(:an_inner_layout) { "%h2 Subtitle\n= yield" }
@@ -118,7 +118,7 @@ class HelpersTest < Test::Unit::TestCase
118
118
  end
119
119
 
120
120
  describe 'body' do
121
- it 'takes a block for defered body generation' do
121
+ it 'takes a block for deferred body generation' do
122
122
  mock_app do
123
123
  get('/') { body { 'Hello World' } }
124
124
  end
@@ -574,6 +574,12 @@ class HelpersTest < Test::Unit::TestCase
574
574
  it 'returns the argument when given a media type string' do
575
575
  assert_equal 'text/plain', mime_type('text/plain')
576
576
  end
577
+
578
+ it 'turns AcceptEntry into String' do
579
+ type = mime_type(Sinatra::Request::AcceptEntry.new('text/plain'))
580
+ assert_equal String, type.class
581
+ assert_equal 'text/plain', type
582
+ end
577
583
  end
578
584
 
579
585
  test 'Base.mime_type registers mime type' do
@@ -792,7 +798,7 @@ class HelpersTest < Test::Unit::TestCase
792
798
  assert_equal 'text/plain;charset=utf-8', response['Content-Type']
793
799
  end
794
800
 
795
- it 'sets the Content-Type response header if type option is set to a file extesion' do
801
+ it 'sets the Content-Type response header if type option is set to a file extension' do
796
802
  send_file_app :type => 'html'
797
803
  get '/file.txt'
798
804
  assert_equal 'text/html;charset=utf-8', response['Content-Type']
@@ -816,7 +822,7 @@ class HelpersTest < Test::Unit::TestCase
816
822
  assert_equal File.mtime(@file).httpdate, response['Last-Modified']
817
823
  end
818
824
 
819
- it 'allows passing in a differen Last-Modified response header with :last_modified' do
825
+ it 'allows passing in a different Last-Modified response header with :last_modified' do
820
826
  time = Time.now
821
827
  send_file_app :last_modified => time
822
828
  get '/file.txt'
@@ -867,7 +873,7 @@ class HelpersTest < Test::Unit::TestCase
867
873
  assert_status 201
868
874
  end
869
875
 
870
- it "is able to send files with unkown mime type" do
876
+ it "is able to send files with unknown mime type" do
871
877
  @file = File.dirname(__FILE__) + '/file.foobar'
872
878
  File.open(@file, 'wb') { |io| io.write('Hello World') }
873
879
  send_file_app
@@ -60,7 +60,7 @@ module IntegrationHelper
60
60
  def alive?
61
61
  3.times { get('/ping') }
62
62
  true
63
- rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error => error
63
+ rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError, SystemCallError, OpenURI::HTTPError, Timeout::Error
64
64
  false
65
65
  end
66
66
 
@@ -86,7 +86,8 @@ module IntegrationHelper
86
86
 
87
87
  def installed?
88
88
  return @installed unless @installed.nil?
89
- require server
89
+ s = server == 'HTTP' ? 'net/http/server' : server
90
+ require s
90
91
  @installed = true
91
92
  rescue LoadError
92
93
  warn "#{server} is not installed, skipping integration tests"
@@ -102,7 +103,7 @@ module IntegrationHelper
102
103
  file, dir = RbConfig::CONFIG.values_at('ruby_install_name', 'bindir')
103
104
  cmd << File.expand_path(file, dir).inspect
104
105
  end
105
- cmd << "-w" unless thin?
106
+ cmd << "-w" unless thin? || net_http_server?
106
107
  cmd << "-I" << File.expand_path('../../lib', __FILE__).inspect
107
108
  cmd << app_file.inspect << '-s' << server << '-o' << '127.0.0.1' << '-p' << port
108
109
  cmd << "-e" << environment.to_s << '2>&1'
@@ -134,6 +135,10 @@ module IntegrationHelper
134
135
  name.to_s == "trinidad"
135
136
  end
136
137
 
138
+ def net_http_server?
139
+ name.to_s == 'HTTP'
140
+ end
141
+
137
142
  def warnings
138
143
  log.scan(%r[(?:\(eval|lib/sinatra).*warning:.*$])
139
144
  end
@@ -171,7 +176,7 @@ module IntegrationHelper
171
176
 
172
177
  Thread.new do
173
178
  # Hack to ensure that Kernel#caller has the same info as
174
- # when run from command-line, for Sintra::Application.app_file.
179
+ # when run from command-line, for Sinatra::Application.app_file.
175
180
  # Also, line numbers are zero-based in JRuby's parser
176
181
  vm.provider.runtime.current_context.set_file_and_line(app_file, 0)
177
182
  # Run the app
@@ -17,7 +17,13 @@ class IntegrationTest < Test::Unit::TestCase
17
17
  random = "%064x" % Kernel.rand(2**256-1)
18
18
  server.get "/ping?x=#{random}"
19
19
  count = server.log.scan("GET /ping?x=#{random}").count
20
- server.webrick? ? assert(count > 0) : assert_equal(1, count)
20
+ if server.net_http_server?
21
+ assert_equal 0, count
22
+ elsif server.webrick?
23
+ assert(count > 0)
24
+ else
25
+ assert_equal(1, count)
26
+ end
21
27
  end
22
28
 
23
29
  it 'streams' do
@@ -76,7 +82,8 @@ class IntegrationTest < Test::Unit::TestCase
76
82
  with\sbackup\sfrom\s#{server}
77
83
  }ix
78
84
 
79
- assert_match exp, server.log
85
+ # because Net HTTP Server logs to $stderr by default
86
+ assert_match exp, server.log unless server.net_http_server?
80
87
  end
81
88
 
82
89
  it 'does not generate warnings' do
@@ -38,7 +38,7 @@ class RequestTest < Test::Unit::TestCase
38
38
  'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
39
39
  'rack.input' => StringIO.new('foo=bar')
40
40
  )
41
- params = Sinatra::Base.new!.send(:indifferent_hash).replace(request.params)
41
+ Sinatra::Base.new!.send(:indifferent_hash).replace(request.params)
42
42
  dumped = Marshal.dump(request.params)
43
43
  assert_equal 'bar', Marshal.load(dumped)['foo']
44
44
  end
@@ -50,6 +50,18 @@ class RequestTest < Test::Unit::TestCase
50
50
  assert_equal({ 'compress' => '0.25' }, request.preferred_type.params)
51
51
  end
52
52
 
53
+ it "makes accept types behave like strings" do
54
+ request = Sinatra::Request.new('HTTP_ACCEPT' => 'image/jpeg; compress=0.25')
55
+ assert_equal 'image/jpeg', request.preferred_type.to_s
56
+ assert_equal 'image/jpeg', request.preferred_type.to_str
57
+ assert_equal 'image', request.preferred_type.split('/').first
58
+
59
+ String.instance_methods.each do |method|
60
+ next unless "".respond_to? method
61
+ assert request.preferred_type.respond_to?(method), "responds to #{method}"
62
+ end
63
+ end
64
+
53
65
  it "properly decodes MIME type parameters" do
54
66
  request = Sinatra::Request.new(
55
67
  'HTTP_ACCEPT' => 'image/jpeg;unquoted=0.25;quoted="0.25";chartest="\";,\x"'
@@ -6,8 +6,7 @@ class ResponseTest < Test::Unit::TestCase
6
6
  setup { @response = Sinatra::Response.new }
7
7
 
8
8
  def assert_same_body(a, b)
9
- enum = Enumerable.const_get(:Enumerator)
10
- assert_equal enum.new(a).to_a, enum.new(b).to_a
9
+ assert_equal a.to_enum(:each).to_a, b.to_enum(:each).to_a
11
10
  end
12
11
 
13
12
  it "initializes with 200, text/html, and empty body" do
@@ -768,6 +768,119 @@ class RoutingTest < Test::Unit::TestCase
768
768
  assert_equal 'Hello Bar', body
769
769
  end
770
770
 
771
+ it 'matches mime_types with dots, hyphens and plus signs' do
772
+ mime_types = %w(
773
+ application/atom+xml
774
+ application/ecmascript
775
+ application/EDI-X12
776
+ application/EDIFACT
777
+ application/json
778
+ application/javascript
779
+ application/octet-stream
780
+ application/ogg
781
+ application/pdf
782
+ application/postscript
783
+ application/rdf+xml
784
+ application/rss+xml
785
+ application/soap+xml
786
+ application/font-woff
787
+ application/xhtml+xml
788
+ application/xml
789
+ application/xml-dtd
790
+ application/xop+xml
791
+ application/zip
792
+ application/gzip
793
+ audio/basic
794
+ audio/L24
795
+ audio/mp4
796
+ audio/mpeg
797
+ audio/ogg
798
+ audio/vorbis
799
+ audio/vnd.rn-realaudio
800
+ audio/vnd.wave
801
+ audio/webm
802
+ image/gif
803
+ image/jpeg
804
+ image/pjpeg
805
+ image/png
806
+ image/svg+xml
807
+ image/tiff
808
+ image/vnd.microsoft.icon
809
+ message/http
810
+ message/imdn+xml
811
+ message/partial
812
+ message/rfc822
813
+ model/example
814
+ model/iges
815
+ model/mesh
816
+ model/vrml
817
+ model/x3d+binary
818
+ model/x3d+vrml
819
+ model/x3d+xml
820
+ multipart/mixed
821
+ multipart/alternative
822
+ multipart/related
823
+ multipart/form-data
824
+ multipart/signed
825
+ multipart/encrypted
826
+ text/cmd
827
+ text/css
828
+ text/csv
829
+ text/html
830
+ text/javascript
831
+ application/javascript
832
+ text/plain
833
+ text/vcard
834
+ text/xml
835
+ video/mpeg
836
+ video/mp4
837
+ video/ogg
838
+ video/quicktime
839
+ video/webm
840
+ video/x-matroska
841
+ video/x-ms-wmv
842
+ video/x-flv
843
+ application/vnd.oasis.opendocument.text
844
+ application/vnd.oasis.opendocument.spreadsheet
845
+ application/vnd.oasis.opendocument.presentation
846
+ application/vnd.oasis.opendocument.graphics
847
+ application/vnd.ms-excel
848
+ application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
849
+ application/vnd.ms-powerpoint
850
+ application/vnd.openxmlformats-officedocument.presentationml.presentation
851
+ application/vnd.openxmlformats-officedocument.wordprocessingml.document
852
+ application/vnd.mozilla.xul+xml
853
+ application/vnd.google-earth.kml+xml
854
+ application/x-deb
855
+ application/x-dvi
856
+ application/x-font-ttf
857
+ application/x-javascript
858
+ application/x-latex
859
+ application/x-mpegURL
860
+ application/x-rar-compressed
861
+ application/x-shockwave-flash
862
+ application/x-stuffit
863
+ application/x-tar
864
+ application/x-www-form-urlencoded
865
+ application/x-xpinstall
866
+ audio/x-aac
867
+ audio/x-caf
868
+ image/x-xcf
869
+ text/x-gwt-rpc
870
+ text/x-jquery-tmpl
871
+ application/x-pkcs12
872
+ application/x-pkcs12
873
+ application/x-pkcs7-certificates
874
+ application/x-pkcs7-certificates
875
+ application/x-pkcs7-certreqresp
876
+ application/x-pkcs7-mime
877
+ application/x-pkcs7-mime
878
+ application/x-pkcs7-signature
879
+ )
880
+
881
+ mime_types.each { |mime_type| assert mime_type.match(Sinatra::Request::HEADER_VALUE_WITH_PARAMS) }
882
+ end
883
+
771
884
  it "filters by accept header" do
772
885
  mock_app {
773
886
  get '/', :provides => :xml do
@@ -776,6 +889,9 @@ class RoutingTest < Test::Unit::TestCase
776
889
  get '/foo', :provides => :html do
777
890
  env['HTTP_ACCEPT']
778
891
  end
892
+ get '/stream', :provides => 'text/event-stream' do
893
+ env['HTTP_ACCEPT']
894
+ end
779
895
  }
780
896
 
781
897
  get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
@@ -792,6 +908,13 @@ class RoutingTest < Test::Unit::TestCase
792
908
 
793
909
  get '/foo', {}, { 'HTTP_ACCEPT' => '' }
794
910
  assert !ok?
911
+
912
+ get '/stream', {}, { 'HTTP_ACCEPT' => 'text/event-stream' }
913
+ assert ok?
914
+ assert_equal 'text/event-stream', body
915
+
916
+ get '/stream', {}, { 'HTTP_ACCEPT' => '' }
917
+ assert !ok?
795
918
  end
796
919
 
797
920
  it "filters by current Content-Type" do