bmizerany-sinatra 0.9.0.5 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,24 +1,62 @@
1
- = 0.9.1 / unreleased
1
+ = 0.9.1 / 2009-03-01
2
2
 
3
3
  * Sinatra now runs under Ruby 1.9.1 [#61]
4
+
4
5
  * Route patterns (splats, :named, or Regexp captures) are now
5
6
  passed as arguments to the block. [#140]
7
+
6
8
  * The "helpers" method now takes a variable number of modules
7
9
  along with the normal block syntax. [#133]
10
+
11
+ * New request-level #forward method for middleware components: passes
12
+ the env to the downstream app and merges the response status, headers,
13
+ and body into the current context.
14
+
15
+ * Requests are now automatically forwarded to the downstream app when
16
+ running as middleware and no matching route is found or all routes
17
+ pass.
18
+
8
19
  * New simple API for extensions/plugins to add DSL-level and
9
20
  request-level methods. Use Sinatra.register(mixin) to extend
10
21
  the DSL with all public methods defined in the mixin module;
11
22
  use Sinatra.helpers(mixin) to make all public methods defined
12
23
  in the mixin module available at the request level. [#138]
13
- * Added "redirect back" to redirect to the referring URL.
24
+ See http://www.sinatrarb.com/extensions.html for details.
25
+
26
+ * Named parameters in routes now capture the "." character. This makes
27
+ routes like "/:path/:filename" match against requests like
28
+ "/foo/bar.txt"; in this case, "params[:filename]" is "bar.txt".
29
+ Previously, the route would not match at all.
30
+
31
+ * Added request-level "redirect back" to redirect to the referring
32
+ URL.
33
+
14
34
  * Added a new "clean_trace" option that causes backtraces dumped
15
35
  to rack.errors and displayed on the development error page to
16
36
  omit framework and core library backtrace lines. The option is
17
37
  enabled by default. [#77]
38
+
39
+ * The ERB output buffer is now available to helpers via the @_out_buf
40
+ instance variable.
41
+
42
+ * It's now much easier to test sessions in unit tests by passing a
43
+ ":session" option to any of the mock request methods. e.g.,
44
+ get '/', {}, :session => { 'foo' => 'bar' }
45
+
46
+ * The request-level #send_data method from Sinatra 0.3.3 has been added
47
+ for compatibility but is deprecated.
48
+
18
49
  * Fix :provides causing crash on any request when request has no
19
50
  Accept header [#139]
51
+
20
52
  * Fix that ERB templates were evaluated twice per "erb" call.
21
53
 
54
+ * Fix app-level middleware not being run when the Sinatra application is
55
+ run as middleware.
56
+
57
+ * Fixed some issues with running under Rack's CGI handler caused by
58
+ writing informational stuff to stdout.
59
+
22
60
  = 0.9.0.4 / 2009-01-25
23
61
 
24
62
  * Using halt with more than 1 args causes ArgumentError [#131]
@@ -420,8 +420,8 @@ and Bacon through separate source files.
420
420
  end
421
421
 
422
422
  def test_with_agent
423
- get '/', :agent => 'Songbird'
424
- assert_equal 'You're in Songbird!', @response.body
423
+ get '/', :env => { :agent => 'Songbird' }
424
+ assert_equal "You're in Songbird!", @response.body
425
425
  end
426
426
 
427
427
  ...
@@ -527,7 +527,7 @@ To update the Sinatra sources in the future:
527
527
 
528
528
  * {Project Website}[http://sinatra.github.com/] - Additional documentation,
529
529
  news, and links to other resources.
530
- * {Contributing}[http://sinatra.github.com/contribute.html] - Find a bug? Need
530
+ * {Contributing}[http://sinatra.github.com/contributing.html] - Find a bug? Need
531
531
  help? Have a patch?
532
532
  * {Lighthouse}[http://sinatra.lighthouseapp.com] - Issue tracking and release
533
533
  planning.
@@ -146,25 +146,6 @@ context "Sinatra" do
146
146
 
147
147
  end
148
148
 
149
- # Deprecated. WTF was going on here? What's the 1 in [:foo, 1] do?
150
- xspecify "should set status then call helper with a var" do
151
- helpers do
152
- def foo
153
- 'bah!'
154
- end
155
- end
156
-
157
- get '/set_body' do
158
- stop [404, [:foo, 1]]
159
- end
160
-
161
- get_it '/set_body'
162
-
163
- should.be.not_found
164
- body.should.equal 'bah!'
165
-
166
- end
167
-
168
149
  specify "should easily set response Content-Type" do
169
150
  get '/foo.html' do
170
151
  content_type 'text/html', :charset => 'utf-8'
@@ -10,47 +10,6 @@ class TesterWithEach
10
10
  end
11
11
  end
12
12
 
13
- context "Looking up a request" do
14
-
15
- setup do
16
- Sinatra.application = nil
17
- end
18
-
19
- # Deprecated. The lookup method is no longer used.
20
- xspecify "returns what's at the end" do
21
- block = Proc.new { 'Hello' }
22
- get '/', &block
23
-
24
- result = Sinatra.application.lookup(
25
- Rack::Request.new(
26
- 'REQUEST_METHOD' => 'GET',
27
- 'PATH_INFO' => '/'
28
- )
29
- )
30
-
31
- result.should.not.be.nil
32
- result.block.should.be block
33
- end
34
-
35
- # Deprecated. The lookup method is no longer used.
36
- xspecify "takes params in path" do
37
- block = Proc.new { 'Hello' }
38
- get '/:foo', &block
39
-
40
- result = Sinatra.application.lookup(
41
- Rack::Request.new(
42
- 'REQUEST_METHOD' => 'GET',
43
- 'PATH_INFO' => '/bar'
44
- )
45
- )
46
-
47
- result.should.not.be.nil
48
- result.block.should.be block
49
- result.params.should.equal "foo" => 'bar'
50
- end
51
-
52
- end
53
-
54
13
  context "An app returns" do
55
14
 
56
15
  setup do
@@ -85,20 +44,6 @@ context "An app returns" do
85
44
 
86
45
  end
87
46
 
88
- # Deprecated. The body method no longer halts.
89
- xspecify "the body set if set before the last" do
90
-
91
- get '/' do
92
- body 'Blake'
93
- 'Mizerany'
94
- end
95
-
96
- get_it '/'
97
- should.be.ok
98
- body.should.equal 'Blake'
99
-
100
- end
101
-
102
47
  specify "404 if NotFound is raised" do
103
48
 
104
49
  get '/' do
@@ -143,23 +88,6 @@ context "Application#configure blocks" do
143
88
 
144
89
  end
145
90
 
146
- context "Default Application Configuration" do
147
-
148
- # Sinatra::ServerError is no longer used
149
- xspecify "includes 404 and 500 error handlers" do
150
- Sinatra.application.errors.should.include(Sinatra::ServerError)
151
- Sinatra.application.errors[Sinatra::ServerError].should.not.be.nil
152
- Sinatra.application.errors.should.include(Sinatra::NotFound)
153
- Sinatra.application.errors[Sinatra::NotFound].should.not.be.nil
154
- end
155
-
156
- # Deprecated. No such thing as a Static event anymore.
157
- xspecify "includes Static event" do
158
- assert Sinatra.application.events[:get].any? { |e| Sinatra::Static === e }
159
- end
160
-
161
- end
162
-
163
91
  context "Events in an app" do
164
92
 
165
93
  setup do
@@ -23,32 +23,6 @@ context "Middleware Pipelines" do
23
23
  Sinatra.application = nil
24
24
  end
25
25
 
26
- # Sessions and logging are tested elsewhere. This is a bad test because it
27
- # asserts things about the implementation and not the effect.
28
- xspecify "includes default middleware with options set" do
29
- @app.set_options :sessions => true, :logging => true
30
- @app.send(:optional_middleware).should.include([Rack::Session::Cookie, [], nil])
31
- @app.send(:optional_middleware).should.include([Rack::CommonLogger, [], nil])
32
- end
33
-
34
- # Bad test.
35
- xspecify "does not include default middleware with options unset" do
36
- @app.set_options :sessions => false, :logging => false
37
- @app.send(:optional_middleware).should.not.include([Rack::Session::Cookie, [], nil])
38
- @app.send(:optional_middleware).should.not.include([Rack::CommonLogger, [], nil])
39
- end
40
-
41
- # Bad test.
42
- xspecify "includes only optional middleware when no explicit middleware added" do
43
- @app.set_options :sessions => true, :logging => true
44
- @app.send(:middleware).should.equal @app.send(:optional_middleware)
45
- end
46
-
47
- # Bad test.
48
- xspecify "should clear middleware before reload" do
49
- @app.clearables.should.include(@app.send(:explicit_middleware))
50
- end
51
-
52
26
  specify "should add middleware with use" do
53
27
  block = Proc.new { |env| }
54
28
  @app.use UpcaseMiddleware
@@ -21,6 +21,7 @@ context "Sessions" do
21
21
 
22
22
  specify "should be able to store data accross requests" do
23
23
  set_option :sessions, true
24
+ set_option :environment, :not_test # necessary because sessions are disabled
24
25
 
25
26
  get '/foo' do
26
27
  session[:test] = true
@@ -34,6 +35,8 @@ context "Sessions" do
34
35
  get_it '/foo', :env => { :host => 'foo.sinatrarb.com' }
35
36
  assert ok?
36
37
  assert include?('Set-Cookie')
38
+
39
+ set_option :environment, :test
37
40
  end
38
41
 
39
42
  end
@@ -93,7 +93,7 @@ context "SendData" do
93
93
  end
94
94
 
95
95
  # Deprecated. send_data is going away.
96
- xspecify "should send the data with options" do
96
+ specify "should send the data with options" do
97
97
  get '/' do
98
98
  send_data 'asdf', :status => 500
99
99
  end
@@ -105,9 +105,10 @@ context "SendData" do
105
105
  end
106
106
 
107
107
  # Deprecated. The Content-Disposition is no longer handled by sendfile.
108
- xspecify "should include a Content-Disposition header" do
108
+ specify "should include a Content-Disposition header" do
109
109
  get '/' do
110
- send_file File.dirname(__FILE__) + '/public/foo.xml'
110
+ send_file File.dirname(__FILE__) + '/public/foo.xml',
111
+ :disposition => 'attachment'
111
112
  end
112
113
 
113
114
  get_it '/'
@@ -115,7 +116,6 @@ context "SendData" do
115
116
  should.be.ok
116
117
  headers['Content-Disposition'].should.not.be.nil
117
118
  headers['Content-Disposition'].should.equal 'attachment; filename="foo.xml"'
118
- headers['Content-Transfer-Encoding'].should.equal 'binary'
119
119
  end
120
120
 
121
121
  specify "should include a Content-Disposition header when :disposition set to attachment" do
@@ -5,7 +5,7 @@ require 'rack'
5
5
  require 'rack/builder'
6
6
 
7
7
  module Sinatra
8
- VERSION = '0.9.0.5'
8
+ VERSION = '0.9.1'
9
9
 
10
10
  # The request object. See Rack::Request for more info:
11
11
  # http://rack.rubyforge.org/doc/classes/Rack/Request.html
@@ -49,7 +49,7 @@ module Sinatra
49
49
  else
50
50
  body = @body || []
51
51
  body = [body] if body.respond_to? :to_str
52
- if header["Content-Length"].nil? && body.respond_to?(:to_ary)
52
+ if body.respond_to?(:to_ary)
53
53
  header["Content-Length"] = body.to_ary.
54
54
  inject(0) { |len, part| len + part.bytesize }.to_s
55
55
  end
@@ -281,12 +281,17 @@ module Sinatra
281
281
  end
282
282
 
283
283
  def render_erb(template, data, options, &block)
284
+ original_out_buf = @_out_buf
284
285
  data = data.call if data.kind_of? Proc
285
- instance = ::ERB.new(data)
286
+
287
+ instance = ::ERB.new(data, nil, nil, '@_out_buf')
286
288
  locals = options[:locals] || {}
287
289
  locals_assigns = locals.to_a.collect { |k,v| "#{k} = locals[:#{k}]" }
290
+
288
291
  src = "#{locals_assigns.join("\n")}\n#{instance.src}"
289
292
  eval src, binding, '(__ERB__)', locals_assigns.length + 1
293
+ @_out_buf, result = original_out_buf, @_out_buf
294
+ result
290
295
  end
291
296
 
292
297
  def render_haml(template, data, options, &block)
@@ -339,10 +344,17 @@ module Sinatra
339
344
  invoke { dispatch! }
340
345
  invoke { error_block!(response.status) }
341
346
 
342
- # never respond with a body on HEAD requests
343
- @response.body = [] if @env['REQUEST_METHOD'] == 'HEAD'
347
+ status, header, body = @response.finish
348
+
349
+ # Never produce a body on HEAD requests. Do retain the Content-Length
350
+ # unless it's "0", in which case we assume it was calculated erroneously
351
+ # for a manual HEAD response and remove it entirely.
352
+ if @env['REQUEST_METHOD'] == 'HEAD'
353
+ body = []
354
+ header.delete('Content-Length') if header['Content-Length'] == '0'
355
+ end
344
356
 
345
- @response.finish
357
+ [status, header, body]
346
358
  end
347
359
 
348
360
  # Access options defined with Base.set.
@@ -361,6 +373,16 @@ module Sinatra
361
373
  throw :pass
362
374
  end
363
375
 
376
+ # Forward the request to the downstream app -- middleware only.
377
+ def forward
378
+ fail "downstream app not set" unless @app.respond_to? :call
379
+ status, headers, body = @app.call(@request.env)
380
+ @response.status = status
381
+ @response.body = body
382
+ headers.each { |k, v| @response[k] = v }
383
+ nil
384
+ end
385
+
364
386
  private
365
387
  # Run before filters and then locate and run a matching route.
366
388
  def route!
@@ -372,11 +394,11 @@ module Sinatra
372
394
  # routes
373
395
  if routes = self.class.routes[@request.request_method]
374
396
  original_params = @params
375
- path = @request.path_info
397
+ path = unescape(@request.path_info)
376
398
 
377
399
  routes.each do |pattern, keys, conditions, block|
378
400
  if match = pattern.match(path)
379
- values = match.captures.map{|val| val && unescape(val) }
401
+ values = match.captures.to_a
380
402
  params =
381
403
  if keys.any?
382
404
  keys.zip(values).inject({}) do |hash,(k,v)|
@@ -404,7 +426,13 @@ module Sinatra
404
426
  end
405
427
  end
406
428
 
407
- raise NotFound
429
+ # No matching route found or all routes passed -- forward downstream
430
+ # when running as middleware; 404 when running as normal app.
431
+ if @app
432
+ forward
433
+ else
434
+ raise NotFound
435
+ end
408
436
  end
409
437
 
410
438
  def nested_params(params)
@@ -517,8 +545,8 @@ module Sinatra
517
545
  @conditions = []
518
546
  @templates = {}
519
547
  @middleware = []
520
- @callsite = nil
521
548
  @errors = {}
549
+ @prototype = nil
522
550
 
523
551
  class << self
524
552
  attr_accessor :routes, :filters, :conditions, :templates,
@@ -673,7 +701,7 @@ module Sinatra
673
701
  if path.respond_to? :to_str
674
702
  special_chars = %w{. + ( )}
675
703
  pattern =
676
- URI.encode(path).gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
704
+ path.gsub(/((:\w+)|[\*#{special_chars.join}])/) do |match|
677
705
  case match
678
706
  when "*"
679
707
  keys << 'splat'
@@ -682,7 +710,7 @@ module Sinatra
682
710
  Regexp.escape(match)
683
711
  else
684
712
  keys << $2[1..-1]
685
- "([^/?&#\.]+)"
713
+ "([^/?&#]+)"
686
714
  end
687
715
  end
688
716
  [/^#{pattern}$/, keys]
@@ -695,13 +723,16 @@ module Sinatra
695
723
 
696
724
  public
697
725
  def helpers(*extensions, &block)
698
- class_eval(&block) if block_given?
699
- include *extensions
726
+ class_eval(&block) if block_given?
727
+ include *extensions if extensions.any?
700
728
  end
701
729
 
702
730
  def register(*extensions, &block)
703
- extensions << Module.new(&block) if block
704
- extend *extensions
731
+ extensions << Module.new(&block) if block_given?
732
+ extensions.each do |extension|
733
+ extend extension
734
+ extension.registered(self) if extension.respond_to?(:registered)
735
+ end
705
736
  end
706
737
 
707
738
  def development? ; environment == :development ; end
@@ -714,7 +745,7 @@ module Sinatra
714
745
  end
715
746
 
716
747
  def use(middleware, *args, &block)
717
- reset_middleware
748
+ @prototype = nil
718
749
  @middleware << [middleware, args, block]
719
750
  end
720
751
 
@@ -723,34 +754,73 @@ module Sinatra
723
754
  handler = detect_rack_handler
724
755
  handler_name = handler.name.gsub(/.*::/, '')
725
756
  puts "== Sinatra/#{Sinatra::VERSION} has taken the stage " +
726
- "on #{port} for #{environment} with backup from #{handler_name}"
757
+ "on #{port} for #{environment} with backup from #{handler_name}" unless handler_name =~/cgi/i
727
758
  handler.run self, :Host => host, :Port => port do |server|
728
759
  trap(:INT) do
729
760
  ## Use thins' hard #stop! if available, otherwise just #stop
730
761
  server.respond_to?(:stop!) ? server.stop! : server.stop
731
- puts "\n== Sinatra has ended his set (crowd applauds)"
762
+ puts "\n== Sinatra has ended his set (crowd applauds)" unless handler_name =~/cgi/i
732
763
  end
733
764
  end
734
765
  rescue Errno::EADDRINUSE => e
735
766
  puts "== Someone is already performing on port #{port}!"
736
767
  end
737
768
 
769
+ # The prototype instance used to process requests.
770
+ def prototype
771
+ @prototype ||= new
772
+ end
773
+
774
+ # Create a new instance of the class fronted by its middleware
775
+ # pipeline. The object is guaranteed to respond to #call but may not be
776
+ # an instance of the class new was called on.
777
+ def new(*args, &bk)
778
+ builder = Rack::Builder.new
779
+ builder.use Rack::Session::Cookie if sessions? && !test?
780
+ builder.use Rack::CommonLogger if logging?
781
+ builder.use Rack::MethodOverride if methodoverride?
782
+ @middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
783
+ builder.run super
784
+ builder.to_app
785
+ end
786
+
738
787
  def call(env)
739
788
  synchronize do
740
789
  reload! if reload?
741
- construct_middleware if @callsite.nil?
742
- @callsite.call(env)
790
+ prototype.call(env)
743
791
  end
744
792
  end
745
793
 
794
+ def reloading?
795
+ @reloading
796
+ end
797
+
746
798
  def reload!
747
799
  @reloading = true
748
- superclass.send :reset!, self
800
+ reset!
749
801
  $LOADED_FEATURES.delete("sinatra.rb")
750
802
  ::Kernel.load app_file
751
803
  @reloading = false
752
804
  end
753
805
 
806
+ def reset!(base=superclass)
807
+ @routes = base.dupe_routes
808
+ @templates = base.templates.dup
809
+ @conditions = []
810
+ @filters = base.filters.dup
811
+ @errors = base.errors.dup
812
+ @middleware = base.middleware.dup
813
+ @prototype = nil
814
+ end
815
+
816
+ protected
817
+ def dupe_routes
818
+ routes.inject({}) do |hash,(request_method,routes)|
819
+ hash[request_method] = routes.dup
820
+ hash
821
+ end
822
+ end
823
+
754
824
  private
755
825
  def detect_rack_handler
756
826
  servers = Array(self.server)
@@ -764,38 +834,11 @@ module Sinatra
764
834
  fail "Server handler (#{servers.join(',')}) not found."
765
835
  end
766
836
 
767
- def construct_middleware(builder=Rack::Builder.new)
768
- builder.use Rack::Session::Cookie if sessions?
769
- builder.use Rack::CommonLogger if logging?
770
- builder.use Rack::MethodOverride if methodoverride?
771
- @middleware.each { |c, args, bk| builder.use(c, *args, &bk) }
772
- builder.run new
773
- @callsite = builder.to_app
774
- end
775
-
776
- def reset_middleware
777
- @callsite = nil
778
- end
779
-
780
- def reset!(subclass = self)
781
- subclass.routes = dupe_routes
782
- subclass.templates = templates.dup
783
- subclass.conditions = []
784
- subclass.filters = filters.dup
785
- subclass.errors = errors.dup
786
- subclass.middleware = middleware.dup
787
- subclass.send :reset_middleware
788
- end
789
-
790
837
  def inherited(subclass)
791
- reset!(subclass)
838
+ subclass.reset! self
792
839
  super
793
840
  end
794
841
 
795
- def reloading?
796
- @reloading ||= false
797
- end
798
-
799
842
  @@mutex = Mutex.new
800
843
  def synchronize(&block)
801
844
  if lock?
@@ -805,13 +848,6 @@ module Sinatra
805
848
  end
806
849
  end
807
850
 
808
- def dupe_routes
809
- routes.inject({}) do |hash,(request_method,routes)|
810
- hash[request_method] = routes.dup
811
- hash
812
- end
813
- end
814
-
815
851
  def metadef(message, &block)
816
852
  (class << self; self; end).
817
853
  send :define_method, message, &block
@@ -916,13 +952,13 @@ module Sinatra
916
952
 
917
953
  # Base class for classic style (top-level) applications.
918
954
  class Default < Base
919
- set :raise_errors, false
955
+ set :raise_errors, Proc.new { test? }
920
956
  set :dump_errors, true
921
957
  set :sessions, false
922
- set :logging, true
958
+ set :logging, Proc.new { ! test? }
923
959
  set :methodoverride, true
924
960
  set :static, true
925
- set :run, false
961
+ set :run, Proc.new { ! test? }
926
962
 
927
963
  def self.register(*extensions, &block) #:nodoc:
928
964
  added_methods = extensions.map {|m| m.public_instance_methods }.flatten
@@ -980,26 +1016,3 @@ class String #:nodoc:
980
1016
  # earlier.
981
1017
  alias_method :bytesize, :length unless ''.respond_to? :bytesize
982
1018
  end
983
-
984
- class Rack::Builder
985
- ## Sugar to include a classic style app in a rackup.
986
- ##
987
- ## This will eval the source into a Sinatra::Default class
988
- ## Example:
989
- ##
990
- ## require 'sinatra/base'
991
- ##
992
- ## map '/foo' do
993
- ## run Sinatra("foo.rb")
994
- ## end
995
- ##
996
- ## run Sinatra("bar.rb")
997
- ##
998
- def Sinatra(file, base=Sinatra::Default)
999
- Sinatra.new(base) do
1000
- expanded = File.expand_path(file)
1001
- self.class_eval { set :app_file, expanded }
1002
- self.class_eval(File.read(expanded), expanded)
1003
- end
1004
- end
1005
- end