rtomayko-sinatra 0.8.9 → 0.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -8,6 +8,10 @@
8
8
  Documentation on using these features is forth-coming; the following
9
9
  provides the basic gist: http://gist.github.com/38605
10
10
 
11
+ * Parameters with subscripts are now parsed into a nested/recursive
12
+ Hash structure. e.g., "post[title]=Hello&post[body]=World" yields
13
+ params: {'post' => {'title' => 'Hello', 'body' => 'World'}}.
14
+
11
15
  * Regular expressions may now be used in route pattens; captures are
12
16
  available at "params[:captures]".
13
17
 
@@ -41,6 +45,16 @@
41
45
  has the status code specified. It's also possible to register an error
42
46
  page for a range of status codes: "error(500..599)".
43
47
 
48
+ * Sinatra's testing support is no longer dependent on Test::Unit. Requiring
49
+ 'sinatra/test' adds the Sinatra::Test module and Sinatra::TestHarness
50
+ class, which can be used with any test framework. The 'sinatra/test/unit',
51
+ 'sinatra/test/spec', 'sinatra/test/rspec', or 'sinatra/test/bacon' files
52
+ can be required to setup a framework-specific testing environment. See the
53
+ README for more information.
54
+
55
+ * Added support for Bacon (test framework). The 'sinatra/test/bacon' file
56
+ can be required to setup Sinatra test helpers on Bacon::Context.
57
+
44
58
  * Deprecated "set_option" and "set_options"; use "set" instead.
45
59
 
46
60
  * Deprecated the "env" option ("options.env"); use "environment" instead.
@@ -65,6 +79,10 @@
65
79
  treated as internal server errors and result in a 500 response
66
80
  status.
67
81
 
82
+ * Deprecated the "get_it", "post_it", "put_it", "delete_it", and "head_it"
83
+ test helper methods. Use "get", "post", "put", "delete", and "head",
84
+ respectively, instead.
85
+
68
86
  * Removed Event and EventContext classes. Applications are defined in a
69
87
  subclass of Sinatra::Base; each request is processed within an
70
88
  instance.
data/README.rdoc CHANGED
@@ -165,12 +165,13 @@ other templates.
165
165
 
166
166
  Templates may be defined at the end of the source file:
167
167
 
168
+ require 'rubygems'
169
+ require 'sinatra'
170
+
168
171
  get '/' do
169
172
  haml :index
170
173
  end
171
174
 
172
- use_in_file_templates!
173
-
174
175
  __END__
175
176
 
176
177
  @@ layout
@@ -180,6 +181,11 @@ Templates may be defined at the end of the source file:
180
181
  @@ index
181
182
  %div.title Hello world!!!!!
182
183
 
184
+ NOTE: Sinatra will automaticly load any in-file-templates in the
185
+ source file that first required sinatra. If you have in-file-templates
186
+ in another source file you will need to explicitly call
187
+ +use_in_file_templates! on main in that file.
188
+
183
189
  It's also possible to define named templates using the top-level template
184
190
  method:
185
191
 
@@ -380,59 +386,61 @@ typically don't have to +use+ them explicitly.
380
386
 
381
387
  == Testing
382
388
 
383
- === Test/Unit
389
+ The Sinatra::Test module includes a variety of helper methods for testing
390
+ your Sinatra app. Sinatra includes support for Test::Unit, test-spec, RSpec,
391
+ and Bacon through separate source files.
392
+
393
+ === Test::Unit
384
394
 
385
- require 'rubygems'
386
395
  require 'sinatra'
387
396
  require 'sinatra/test/unit'
388
397
  require 'my_sinatra_app'
389
398
 
390
399
  class MyAppTest < Test::Unit::TestCase
391
-
392
400
  def test_my_default
393
- get_it '/'
401
+ get '/'
394
402
  assert_equal 'My Default Page!', @response.body
395
403
  end
396
404
 
397
405
  def test_with_agent
398
- get_it '/', :agent => 'Songbird'
406
+ get '/', :agent => 'Songbird'
399
407
  assert_equal 'You're in Songbird!', @response.body
400
408
  end
401
409
 
402
410
  ...
403
-
404
411
  end
405
412
 
406
- === Test/Spec
413
+ === Test::Spec
414
+
415
+ Install the test-spec gem and require <tt>'sinatra/test/spec'</tt> before
416
+ your app:
407
417
 
408
- require 'rubygems'
409
418
  require 'sinatra'
410
419
  require 'sinatra/test/spec'
411
420
  require 'my_sinatra_app'
412
421
 
413
422
  describe 'My app' do
414
-
415
423
  it "should show a default page" do
416
- get_it '/'
424
+ get '/'
417
425
  should.be.ok
418
426
  body.should.equal 'My Default Page!'
419
427
  end
420
428
 
421
429
  ...
422
-
423
430
  end
424
431
 
425
432
  === RSpec
426
433
 
427
- require 'rubygems'
428
- require 'spec'
434
+ Install the rspec gem and require <tt>'sinatra/test/rspec'</tt> before
435
+ your app:
436
+
429
437
  require 'sinatra'
430
438
  require 'sinatra/test/rspec'
431
439
  require 'my_sinatra_app'
432
440
 
433
441
  describe 'My app' do
434
442
  it 'should show a default page' do
435
- get_it '/'
443
+ get '/'
436
444
  @response.should be_ok
437
445
  @response.body.should == 'My Default Page!'
438
446
  end
@@ -441,20 +449,35 @@ typically don't have to +use+ them explicitly.
441
449
 
442
450
  end
443
451
 
444
- See Sinatra::Test::Methods for more information on +get_it+, +post_it+,
445
- +put_it+, and friends.
452
+ === Bacon
453
+
454
+ require 'sinatra'
455
+ require 'sinatra/test/bacon'
456
+ require 'my_sinatra_app'
457
+
458
+ describe 'My app' do
459
+ it 'should be ok' do
460
+ get '/'
461
+ should.be.ok
462
+ body.should == 'Im OK'
463
+ end
464
+ end
465
+
466
+ See Sinatra::Test for more information on +get+, +post+, +put+, and
467
+ friends.
446
468
 
447
469
  == Command line
448
470
 
449
471
  Sinatra applications can be run directly:
450
472
 
451
- ruby myapp.rb [-h] [-x] [-p PORT] [-e ENVIRONMENT]
473
+ ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-s HANDLER]
452
474
 
453
475
  Options are:
454
476
 
455
477
  -h # help
456
478
  -p # set the port (default is 4567)
457
479
  -e # set the environment (default is development)
480
+ -s # specify rack server/handler (default is thin)
458
481
  -x # turn on the mutex lock (default is off)
459
482
 
460
483
  == Contributing
@@ -1,7 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
3
  context "Simple Events" do
4
-
5
4
  def simple_request_hash(method, path)
6
5
  Rack::Request.new({
7
6
  'REQUEST_METHOD' => method.to_s.upcase,
@@ -14,16 +13,16 @@ context "Simple Events" do
14
13
 
15
14
  def invoke_simple(path, request_path, &b)
16
15
  params = nil
17
- mock_app {
18
- get path do
19
- params = self.params
20
- b.call if b
21
- end
22
- }
16
+ get path do
17
+ params = self.params
18
+ b.call if b
19
+ end
23
20
  get_it request_path
24
21
  MockResult.new(b, params)
25
22
  end
26
23
 
24
+ setup { Sinatra.application = nil }
25
+
27
26
  specify "return last value" do
28
27
  block = Proc.new { 'Simple' }
29
28
  result = invoke_simple('/', '/', &block)
@@ -38,6 +37,7 @@ context "Simple Events" do
38
37
  result.params.should.equal "foo" => 'a', "bar" => 'b'
39
38
 
40
39
  # unscapes
40
+ Sinatra.application = nil
41
41
  result = invoke_simple('/:foo/:bar', '/a/blake%20mizerany')
42
42
  result.should.not.be.nil
43
43
  result.params.should.equal "foo" => 'a', "bar" => 'blake mizerany'
@@ -48,14 +48,17 @@ context "Simple Events" do
48
48
  result.should.not.be.nil
49
49
  result.params.should.equal "foo" => 'a', "bar" => 'b'
50
50
 
51
+ Sinatra.application = nil
51
52
  result = invoke_simple('/?:foo?/?:bar?', '/a/')
52
53
  result.should.not.be.nil
53
54
  result.params.should.equal "foo" => 'a', "bar" => nil
54
55
 
56
+ Sinatra.application = nil
55
57
  result = invoke_simple('/?:foo?/?:bar?', '/a')
56
58
  result.should.not.be.nil
57
59
  result.params.should.equal "foo" => 'a', "bar" => nil
58
60
 
61
+ Sinatra.application = nil
59
62
  result = invoke_simple('/:foo?/?:bar?', '/')
60
63
  result.should.not.be.nil
61
64
  result.params.should.equal "foo" => nil, "bar" => nil
data/compat/helper.rb CHANGED
@@ -13,9 +13,18 @@ require 'sinatra/test'
13
13
  require 'sinatra/test/unit'
14
14
  require 'sinatra/test/spec'
15
15
 
16
+ module Sinatra::Test
17
+ # we need to remove the new test helper methods since they conflict with
18
+ # the top-level methods of the same name.
19
+ %w(get head post put delete).each do |verb|
20
+ remove_method verb
21
+ end
22
+ include Sinatra::Delegator
23
+ end
24
+
16
25
  class Test::Unit::TestCase
26
+ include Sinatra::Test
17
27
  def setup
18
28
  @app = lambda { |env| Sinatra::Application.call(env) }
19
29
  end
20
- include Sinatra::Test
21
30
  end
data/lib/sinatra/base.rb CHANGED
@@ -4,7 +4,7 @@ require 'rack'
4
4
  require 'rack/builder'
5
5
 
6
6
  module Sinatra
7
- VERSION = '0.8.9'
7
+ VERSION = '0.8.10'
8
8
 
9
9
  class Request < Rack::Request
10
10
  def user_agent
@@ -14,6 +14,13 @@ module Sinatra
14
14
  def accept
15
15
  @env['HTTP_ACCEPT'].split(',').map { |a| a.strip }
16
16
  end
17
+
18
+ # Override Rack 0.9.x's #params implementation (see #72 in lighthouse)
19
+ def params
20
+ self.GET.update(self.POST)
21
+ rescue EOFError => boom
22
+ self.GET
23
+ end
17
24
  end
18
25
 
19
26
  class Response < Rack::Response
@@ -324,8 +331,7 @@ module Sinatra
324
331
  self.class.filters.each {|block| instance_eval(&block)}
325
332
  if routes = self.class.routes[@request.request_method]
326
333
  path = @request.path_info
327
- original_params = Hash.new{ |hash,k| hash[k.to_s] if Symbol === k }
328
- original_params.merge! @request.params
334
+ original_params = nested_params(@request.params)
329
335
 
330
336
  routes.each do |pattern, keys, conditions, method_name|
331
337
  if pattern =~ path
@@ -345,8 +351,7 @@ module Sinatra
345
351
  else
346
352
  {}
347
353
  end
348
- @params = original_params.dup
349
- @params.merge!(params)
354
+ @params = original_params.merge(params)
350
355
 
351
356
  catch(:pass) {
352
357
  conditions.each { |cond|
@@ -359,14 +364,24 @@ module Sinatra
359
364
  raise NotFound
360
365
  end
361
366
 
362
- def invoke(handler)
363
- res = catch(:halt) {
364
- if handler.respond_to?(:call)
365
- instance_eval(&handler)
366
- else
367
- send(handler)
367
+ def nested_params(params)
368
+ return indifferent_hash.merge(params) if !params.keys.join.include?('[')
369
+ params.inject indifferent_hash do |res, (key,val)|
370
+ if key =~ /\[.*\]/
371
+ splat = key.scan(/(^[^\[]+)|\[([^\]]+)\]/).flatten.compact
372
+ head, last = splat[0..-2], splat[-1]
373
+ head.inject(res){ |s,v| s[v] ||= indifferent_hash }[last] = val
368
374
  end
369
- }
375
+ res
376
+ end
377
+ end
378
+
379
+ def indifferent_hash
380
+ Hash.new {|hash,key| hash[key.to_s] if Symbol === key }
381
+ end
382
+
383
+ def invoke(block)
384
+ res = catch(:halt) { instance_eval(&block) }
370
385
  case
371
386
  when res.respond_to?(:to_str)
372
387
  @response.body = [res]
@@ -478,8 +493,13 @@ module Sinatra
478
493
  end
479
494
 
480
495
  def use_in_file_templates!
481
- line = caller.detect { |s| s !~ /lib\/sinatra.*\.rb/ &&
482
- s !~ /\(.*\)/ }
496
+ line = caller.detect do |s|
497
+ [
498
+ /lib\/sinatra.*\.rb/,
499
+ /\(.*\)/,
500
+ /rubygems\/custom_require\.rb/
501
+ ].all? { |x| s !~ x }
502
+ end
483
503
  file = line.sub(/:\d+.*$/, '')
484
504
  if data = ::IO.read(file).split('__END__')[1]
485
505
  data.gsub!(/\r\n/, "\n")
@@ -541,10 +561,10 @@ module Sinatra
541
561
 
542
562
  def get(path, opts={}, &block)
543
563
  conditions = @conditions.dup
544
- _, _, _, method_name = route('GET', path, opts, &block)
564
+ route('GET', path, opts, &block)
545
565
 
546
566
  @conditions = conditions
547
- head(path, opts) { invoke(method_name) ; [] }
567
+ head(path, opts) { invoke(block) ; [] }
548
568
  end
549
569
 
550
570
  def put(path, opts={}, &bk); route 'PUT', path, opts, &bk; end
@@ -553,21 +573,20 @@ module Sinatra
553
573
  def head(path, opts={}, &bk); route 'HEAD', path, opts, &bk; end
554
574
 
555
575
  private
556
- def route(method, path, opts={}, &block)
576
+ def route(verb, path, opts={}, &block)
557
577
  host_name opts[:host] if opts.key?(:host)
558
578
  user_agent opts[:agent] if opts.key?(:agent)
559
579
  accept_mime_types opts[:provides] if opts.key?(:provides)
560
580
 
561
581
  pattern, keys = compile(path)
562
582
  conditions, @conditions = @conditions, []
563
- method_name = "route { #{method} #{path} }"
564
- nmethods = instance_methods.grep(rx = /#{Regexp.escape(method_name)}/).size
565
- method_name += " [#{nmethods}]"
566
583
 
567
- define_method(method_name, &block)
584
+ define_method "#{verb} #{path}", &block
585
+ unbound_method = instance_method("#{verb} #{path}")
586
+ block = lambda { unbound_method.bind(self).call }
568
587
 
569
- (routes[method] ||= []).
570
- push([pattern, keys, conditions, method_name]).last
588
+ (routes[verb] ||= []).
589
+ push([pattern, keys, conditions, block]).last
571
590
  end
572
591
 
573
592
  def compile(path)
@@ -784,6 +803,7 @@ module Sinatra
784
803
  def self.reload!
785
804
  @reloading = true
786
805
  superclass.send :inherited, self
806
+ $LOADED_FEATURES.delete("sinatra.rb")
787
807
  ::Kernel.load app_file
788
808
  @reloading = false
789
809
  end
@@ -0,0 +1,17 @@
1
+ require 'bacon'
2
+ require 'sinatra/test'
3
+
4
+ Sinatra::Default.set(
5
+ :env => :test,
6
+ :run => false,
7
+ :raise_errors => true,
8
+ :logging => false
9
+ )
10
+
11
+ module Sinatra::Test
12
+ def should
13
+ @response.should
14
+ end
15
+ end
16
+
17
+ Bacon::Context.send(:include, Sinatra::Test)
@@ -1,2 +1,9 @@
1
1
  require 'sinatra/test'
2
2
  require 'spec/interop/test'
3
+
4
+ Sinatra::Default.set(
5
+ :env => :test,
6
+ :run => false,
7
+ :raise_errors => true,
8
+ :logging => false
9
+ )
@@ -1,2 +1,9 @@
1
1
  require 'test/spec'
2
2
  require 'sinatra/test'
3
+ require 'sinatra/test/unit'
4
+
5
+ module Sinatra::Test
6
+ def should
7
+ @response.should
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
- require 'test/unit'
2
1
  require 'sinatra/test'
2
+ require 'test/unit'
3
3
 
4
4
  Test::Unit::TestCase.send :include, Sinatra::Test
5
5
 
data/lib/sinatra/test.rb CHANGED
@@ -1,112 +1,114 @@
1
1
  require 'sinatra/base'
2
- require 'test/unit'
3
2
 
4
- module Sinatra::Test
5
- include Rack::Utils
6
-
7
- attr_reader :app, :request, :response
8
-
9
- def mock_app(base=Sinatra::Base, &block)
10
- @app = Sinatra.new(base, &block)
11
- end
12
-
13
- undef request if method_defined?(:request)
14
-
15
- def request(verb, path, *args)
16
- fail "@app not set - cannot make request" if @app.nil?
17
- @request = Rack::MockRequest.new(@app)
18
- opts, input =
19
- case args.size
20
- when 2 # input, env
21
- input, env = args
22
- if input.kind_of?(Hash) # params, env
23
- [env, param_string(input)]
24
- else
25
- [env, input]
26
- end
27
- when 1 # params
28
- if (data = args.first).kind_of?(Hash)
29
- env = (data.delete(:env) || {})
30
- [env, param_string(data)]
3
+ module Sinatra
4
+
5
+ module Test
6
+ include Rack::Utils
7
+
8
+ attr_reader :app, :request, :response
9
+
10
+ def test_request(verb, path, *args)
11
+ @app = Sinatra::Application if @app.nil? && defined?(Sinatra::Application)
12
+ fail "@app not set - cannot make request" if @app.nil?
13
+ @request = Rack::MockRequest.new(@app)
14
+ opts, input =
15
+ case args.size
16
+ when 2 # input, env
17
+ input, env = args
18
+ if input.kind_of?(Hash) # params, env
19
+ [env, param_string(input)]
20
+ else
21
+ [env, input]
22
+ end
23
+ when 1 # params
24
+ if (data = args.first).kind_of?(Hash)
25
+ env = (data.delete(:env) || {})
26
+ [env, param_string(data)]
27
+ else
28
+ [{}, data]
29
+ end
30
+ when 0
31
+ [{}, '']
31
32
  else
32
- [{}, data]
33
+ raise ArgumentError, "zero, one, or two arguments expected"
33
34
  end
34
- when 0
35
- [{}, '']
36
- else
37
- raise ArgumentError, "zero, one, or two arguments expected"
38
- end
39
- opts = rack_opts(opts)
40
- opts[:input] ||= input
41
- yield @request if block_given?
42
- @response = @request.request(verb, path, opts)
43
- end
35
+ opts = rack_opts(opts)
36
+ opts[:input] ||= input
37
+ yield @request if block_given?
38
+ @response = @request.request(verb, path, opts)
39
+ end
44
40
 
45
- def get(path, *args, &b) ; request('GET', path, *args, &b) ; end
46
- def head(path, *args, &b) ; request('HEAD', path, *args, &b) ; end
47
- def post(path, *args, &b) ; request('POST', path, *args, &b) ; end
48
- def put(path, *args, &b) ; request('PUT', path, *args, &b) ; end
49
- def delete(path, *args, &b) ; request('DELETE', path, *args, &b) ; end
41
+ def get(path, *args, &b) ; test_request('GET', path, *args, &b) ; end
42
+ def head(path, *args, &b) ; test_request('HEAD', path, *args, &b) ; end
43
+ def post(path, *args, &b) ; test_request('POST', path, *args, &b) ; end
44
+ def put(path, *args, &b) ; test_request('PUT', path, *args, &b) ; end
45
+ def delete(path, *args, &b) ; test_request('DELETE', path, *args, &b) ; end
50
46
 
51
- def follow!
52
- request 'GET', @response.location
53
- end
47
+ def follow!
48
+ test_request 'GET', @response.location
49
+ end
54
50
 
55
- def should
56
- @response.should
57
- end
51
+ def body ; @response.body ; end
52
+ def status ; @response.status ; end
58
53
 
59
- def body
60
- @response.body
61
- end
54
+ # Delegate other missing methods to @response.
55
+ def method_missing(name, *args, &block)
56
+ if @response && @response.respond_to?(name)
57
+ @response.send(name, *args, &block)
58
+ else
59
+ super
60
+ end
61
+ end
62
62
 
63
- def status
64
- @response.status
65
- end
63
+ # Also check @response since we delegate there.
64
+ def respond_to?(symbol, include_private=false)
65
+ super || (@response && @response.respond_to?(symbol, include_private))
66
+ end
66
67
 
67
- RACK_OPT_NAMES = {
68
- :accept => "HTTP_ACCEPT",
69
- :agent => "HTTP_USER_AGENT",
70
- :host => "HTTP_HOST",
71
- :session => "HTTP_COOKIE",
72
- :cookies => "HTTP_COOKIE",
73
- :content_type => "CONTENT_TYPE"
74
- }
75
-
76
- def rack_opts(opts)
77
- opts.inject({}) do |hash,(key,val)|
78
- key = RACK_OPT_NAMES[key] || key
79
- hash[key] = val
80
- hash
68
+ RACK_OPT_NAMES = {
69
+ :accept => "HTTP_ACCEPT",
70
+ :agent => "HTTP_USER_AGENT",
71
+ :host => "HTTP_HOST",
72
+ :session => "HTTP_COOKIE",
73
+ :cookies => "HTTP_COOKIE",
74
+ :content_type => "CONTENT_TYPE"
75
+ }
76
+
77
+ def rack_opts(opts)
78
+ opts.inject({}) do |hash,(key,val)|
79
+ key = RACK_OPT_NAMES[key] || key
80
+ hash[key] = val
81
+ hash
82
+ end
81
83
  end
82
- end
83
84
 
84
- def env_for(opts={})
85
- opts = rack_opts(opts)
86
- Rack::MockRequest.env_for(opts)
87
- end
85
+ def env_for(opts={})
86
+ opts = rack_opts(opts)
87
+ Rack::MockRequest.env_for(opts)
88
+ end
88
89
 
89
- def param_string(hash)
90
- hash.map { |pair| pair.map{|v|escape(v)}.join('=') }.join('&')
91
- end
90
+ def param_string(hash)
91
+ hash.map { |pair| pair.map{|v|escape(v)}.join('=') }.join('&')
92
+ end
92
93
 
93
- if defined? Sinatra::Compat
94
- # Deprecated. Use: "get" instead of "get_it".
95
- %w(get head post put delete).each do |verb|
96
- alias_method "#{verb}_it", verb
97
- remove_method verb
94
+ if defined? Sinatra::Compat
95
+ # Deprecated. Use: "get" instead of "get_it".
96
+ %w(get head post put delete).each do |verb|
97
+ eval <<-RUBY, binding, __FILE__, __LINE__
98
+ def #{verb}_it(*args, &block)
99
+ sinatra_warn "The #{verb}_it method is deprecated; use #{verb} instead."
100
+ test_request('#{verb.upcase}', *args, &block)
101
+ end
102
+ RUBY
103
+ end
98
104
  end
105
+ end
99
106
 
100
- include Sinatra::Delegator
107
+ class TestHarness
108
+ include Test
101
109
 
102
- # Deprecated. Tests no longer delegate missing methods to the
103
- # mock response. Use: @response
104
- def method_missing(name, *args, &block)
105
- if @response && @response.respond_to?(name)
106
- @response.send(name, *args, &block)
107
- else
108
- super
109
- end
110
+ def initialize(app=nil)
111
+ @app = app || Sinatra::Application
110
112
  end
111
113
  end
112
114
  end
data/lib/sinatra.rb CHANGED
@@ -1,3 +1,8 @@
1
+ libdir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
3
+
1
4
  require 'sinatra/base'
2
5
  require 'sinatra/main'
3
6
  require 'sinatra/compat'
7
+
8
+ use_in_file_templates!