roda 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +3 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +709 -0
  5. data/Rakefile +124 -0
  6. data/lib/roda.rb +608 -0
  7. data/lib/roda/plugins/all_verbs.rb +48 -0
  8. data/lib/roda/plugins/default_headers.rb +50 -0
  9. data/lib/roda/plugins/error_handler.rb +69 -0
  10. data/lib/roda/plugins/flash.rb +62 -0
  11. data/lib/roda/plugins/h.rb +24 -0
  12. data/lib/roda/plugins/halt.rb +79 -0
  13. data/lib/roda/plugins/header_matchers.rb +57 -0
  14. data/lib/roda/plugins/hooks.rb +106 -0
  15. data/lib/roda/plugins/indifferent_params.rb +47 -0
  16. data/lib/roda/plugins/middleware.rb +88 -0
  17. data/lib/roda/plugins/multi_route.rb +77 -0
  18. data/lib/roda/plugins/not_found.rb +62 -0
  19. data/lib/roda/plugins/pass.rb +34 -0
  20. data/lib/roda/plugins/render.rb +217 -0
  21. data/lib/roda/plugins/streaming.rb +165 -0
  22. data/spec/composition_spec.rb +19 -0
  23. data/spec/env_spec.rb +11 -0
  24. data/spec/integration_spec.rb +63 -0
  25. data/spec/matchers_spec.rb +658 -0
  26. data/spec/module_spec.rb +29 -0
  27. data/spec/opts_spec.rb +42 -0
  28. data/spec/plugin/all_verbs_spec.rb +29 -0
  29. data/spec/plugin/default_headers_spec.rb +63 -0
  30. data/spec/plugin/error_handler_spec.rb +67 -0
  31. data/spec/plugin/flash_spec.rb +59 -0
  32. data/spec/plugin/h_spec.rb +13 -0
  33. data/spec/plugin/halt_spec.rb +62 -0
  34. data/spec/plugin/header_matchers_spec.rb +61 -0
  35. data/spec/plugin/hooks_spec.rb +97 -0
  36. data/spec/plugin/indifferent_params_spec.rb +13 -0
  37. data/spec/plugin/middleware_spec.rb +52 -0
  38. data/spec/plugin/multi_route_spec.rb +98 -0
  39. data/spec/plugin/not_found_spec.rb +99 -0
  40. data/spec/plugin/pass_spec.rb +23 -0
  41. data/spec/plugin/render_spec.rb +148 -0
  42. data/spec/plugin/streaming_spec.rb +52 -0
  43. data/spec/plugin_spec.rb +61 -0
  44. data/spec/redirect_spec.rb +24 -0
  45. data/spec/request_spec.rb +55 -0
  46. data/spec/response_spec.rb +131 -0
  47. data/spec/session_spec.rb +35 -0
  48. data/spec/spec_helper.rb +89 -0
  49. data/spec/version_spec.rb +8 -0
  50. metadata +148 -0
@@ -0,0 +1,29 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe "Roda.request_module and .response_module" do
4
+ it "should include given module in request or response class" do
5
+ app(:bare) do
6
+ request_module(Module.new{def h; halt response.finish end})
7
+ response_module(Module.new{def finish; [1, {}, []] end})
8
+
9
+ route do |r|
10
+ r.h
11
+ end
12
+ end
13
+
14
+ req.should == [1, {}, []]
15
+ end
16
+
17
+ it "should accept blocks and turn them into modules" do
18
+ app(:bare) do
19
+ request_module{def h; halt response.finish end}
20
+ response_module{def finish; [1, {}, []] end}
21
+
22
+ route do |r|
23
+ r.h
24
+ end
25
+ end
26
+
27
+ req.should == [1, {}, []]
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path("spec_helper", File.dirname(__FILE__))
2
+
3
+ describe "opts" do
4
+ it "is inheritable and allows overriding" do
5
+ c = Class.new(Roda)
6
+ c.opts[:foo] = "bar"
7
+ c.opts[:foo].should == "bar"
8
+
9
+ sc = Class.new(c)
10
+ sc.opts[:foo].should == "bar"
11
+
12
+ sc.opts[:foo] = "baz"
13
+ sc.opts[:foo].should == "baz"
14
+ c.opts[:foo].should == "bar"
15
+ end
16
+
17
+ it "should be available as an instance methods" do
18
+ app(:bare) do
19
+ opts[:hello] = "Hello World"
20
+
21
+ route do |r|
22
+ r.on do
23
+ opts[:hello]
24
+ end
25
+ end
26
+ end
27
+
28
+ body.should == "Hello World"
29
+ end
30
+
31
+ it "should only shallow clone by default" do
32
+ c = Class.new(Roda)
33
+ c.opts[:foo] = "bar"
34
+ c.opts[:foo].should == "bar"
35
+
36
+ sc = Class.new(c)
37
+ sc.opts[:foo].replace("baz")
38
+
39
+ sc.opts[:foo].should == "baz"
40
+ c.opts[:foo].should == "baz"
41
+ end
42
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "all_verbs plugin" do
4
+ it "adds method for each http verb" do
5
+ app(:all_verbs) do |r|
6
+ r.delete{'d'}
7
+ r.head{'h'}
8
+ r.options{'o'}
9
+ r.patch{'pa'}
10
+ r.put{'pu'}
11
+ r.trace{'t'}
12
+ if Rack::Request.method_defined?(:link?)
13
+ r.link{'l'}
14
+ r.unlink{'u'}
15
+ end
16
+ end
17
+
18
+ body('REQUEST_METHOD'=>'DELETE').should == 'd'
19
+ body('REQUEST_METHOD'=>'HEAD').should == 'h'
20
+ body('REQUEST_METHOD'=>'OPTIONS').should == 'o'
21
+ body('REQUEST_METHOD'=>'PATCH').should == 'pa'
22
+ body('REQUEST_METHOD'=>'PUT').should == 'pu'
23
+ body('REQUEST_METHOD'=>'TRACE').should == 't'
24
+ if Rack::Request.method_defined?(:link?)
25
+ body('REQUEST_METHOD'=>'LINK').should == 'l'
26
+ body('REQUEST_METHOD'=>'UNLINK').should == 'u'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "default_headers plugin" do
4
+ it "sets the default headers to use for the response" do
5
+ h = {'Content-Type'=>'text/json', 'Foo'=>'bar'}
6
+
7
+ app(:bare) do
8
+ plugin :default_headers, h
9
+ route do |r|
10
+ end
11
+ end
12
+
13
+ req[1].should == h
14
+ req[1].should_not equal(h)
15
+ end
16
+
17
+ it "should not override existing default headers" do
18
+ h = {'Content-Type'=>'text/json', 'Foo'=>'bar'}
19
+
20
+ app(:bare) do
21
+ plugin :default_headers, h
22
+ plugin :default_headers
23
+
24
+ route do |r|
25
+ end
26
+ end
27
+
28
+ req[1].should == h
29
+ end
30
+
31
+ it "should allow modifying the default headers at a later point" do
32
+ h = {'Content-Type'=>'text/json', 'Foo'=>'bar'}
33
+
34
+ app(:bare) do
35
+ plugin :default_headers
36
+ default_headers['Content-Type'] = 'text/json'
37
+ default_headers['Foo'] = 'bar'
38
+
39
+ route do |r|
40
+ end
41
+ end
42
+
43
+ req[1].should == h
44
+ end
45
+
46
+ it "should work correctly in subclasses" do
47
+ h = {'Content-Type'=>'text/json', 'Foo'=>'bar'}
48
+
49
+ app(:bare) do
50
+ plugin :default_headers
51
+ default_headers['Content-Type'] = 'text/json'
52
+ default_headers['Foo'] = 'bar'
53
+
54
+ route do |r|
55
+ end
56
+ end
57
+
58
+ @app = Class.new(@app)
59
+ @app.route{}
60
+
61
+ req[1].should == h
62
+ end
63
+ end
@@ -0,0 +1,67 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "error_handler plugin" do
4
+ it "executes only if error raised" do
5
+ app(:bare) do
6
+ plugin :error_handler
7
+
8
+ error do |e|
9
+ e.message
10
+ end
11
+
12
+ route do |r|
13
+ r.on "a" do
14
+ "found"
15
+ end
16
+
17
+ raise ArgumentError, "bad idea"
18
+ end
19
+ end
20
+
21
+ body("/a").should == 'found'
22
+ status("/a").should == 200
23
+ body.should == 'bad idea'
24
+ status.should == 500
25
+ end
26
+
27
+ it "can override status inside error block" do
28
+ app(:bare) do
29
+ plugin :error_handler do |e|
30
+ response.status = 501
31
+ e.message
32
+ end
33
+
34
+ route do |r|
35
+ raise ArgumentError, "bad idea"
36
+ end
37
+ end
38
+
39
+ status.should == 501
40
+ end
41
+
42
+ it "can set error via the plugin block" do
43
+ app(:bare) do
44
+ plugin :error_handler do |e|
45
+ e.message
46
+ end
47
+
48
+ route do |r|
49
+ raise ArgumentError, "bad idea"
50
+ end
51
+ end
52
+
53
+ body.should == 'bad idea'
54
+ end
55
+
56
+ it "has default error handler also raise" do
57
+ app(:bare) do
58
+ plugin :error_handler
59
+
60
+ route do |r|
61
+ raise ArgumentError, "bad idea"
62
+ end
63
+ end
64
+
65
+ proc{req}.should raise_error(ArgumentError)
66
+ end
67
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ begin
4
+ require 'sinatra/flash/hash'
5
+ rescue LoadError
6
+ warn "sinatra-flash not installed, skipping flash plugin test"
7
+ else
8
+ describe "flash plugin" do
9
+ it "flash.now[] sets flash for current page" do
10
+ app(:bare) do
11
+ use Rack::Session::Cookie, :secret => "1"
12
+ plugin :flash
13
+
14
+ route do |r|
15
+ r.on do
16
+ flash.now['a'] = 'b'
17
+ flash['a']
18
+ end
19
+ end
20
+ end
21
+
22
+ body.should == 'b'
23
+ end
24
+
25
+ it "flash[] sets flash for next page" do
26
+ app(:bare) do
27
+ use Rack::Session::Cookie, :secret => "1"
28
+ plugin :flash
29
+
30
+ route do |r|
31
+ r.on 'a' do
32
+ "c#{flash['a']}"
33
+ end
34
+
35
+ r.on do
36
+ flash['a'] = "b#{flash['a']}"
37
+ flash['a'] || ''
38
+ end
39
+ end
40
+ end
41
+
42
+ env = proc{|h| h['Set-Cookie'] ? {'HTTP_COOKIE'=>h['Set-Cookie'].sub("; path=/; HttpOnly", '')} : {}}
43
+ _, h, b = req
44
+ b.join.should == ''
45
+ _, h, b = req(env[h])
46
+ b.join.should == 'b'
47
+ _, h, b = req(env[h])
48
+ b.join.should == 'bb'
49
+ _, h, b = req('/a', env[h])
50
+ b.join.should == 'cbbb'
51
+ _, h, b = req(env[h])
52
+ b.join.should == ''
53
+ _, h, b = req(env[h])
54
+ b.join.should == 'b'
55
+ _, h, b = req(env[h])
56
+ b.join.should == 'bb'
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "h plugin" do
4
+ it "adds h method for html escaping" do
5
+ app(:h) do |r|
6
+ r.on do
7
+ h("<form>") + h(:form)
8
+ end
9
+ end
10
+
11
+ body.should == '&lt;form&gt;form'
12
+ end
13
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "halt plugin" do
4
+ it "should still have halt return rack response as argument given it as argument" do
5
+ app(:halt) do |r|
6
+ r.halt [200, {}, ['foo']]
7
+ end
8
+
9
+ body.should == "foo"
10
+ end
11
+
12
+ it "should consider string argument as response body" do
13
+ app(:halt) do |r|
14
+ r.halt "foo"
15
+ end
16
+
17
+ body.should == "foo"
18
+ end
19
+
20
+ it "should consider integer argument as response status" do
21
+ app(:halt) do |r|
22
+ r.halt 300
23
+ end
24
+
25
+ status.should == 300
26
+ end
27
+
28
+ it "should consider 2 arguments as response status and body" do
29
+ app(:halt) do |r|
30
+ r.halt 300, "foo"
31
+ end
32
+
33
+ status.should == 300
34
+ body.should == "foo"
35
+ end
36
+
37
+ it "should consider 3 arguments as response" do
38
+ app(:halt) do |r|
39
+ r.halt 300, {'a'=>'b'}, "foo"
40
+ end
41
+
42
+ status.should == 300
43
+ header('a').should == 'b'
44
+ body.should == "foo"
45
+ end
46
+
47
+ it "should raise an error for too many arguments" do
48
+ app(:halt) do |r|
49
+ r.halt 300, {'a'=>'b'}, "foo", 1
50
+ end
51
+
52
+ proc{req}.should raise_error(Roda::RodaError)
53
+ end
54
+
55
+ it "should raise an error for single argument not integer, String, or Array" do
56
+ app(:halt) do |r|
57
+ r.halt('a'=>'b')
58
+ end
59
+
60
+ proc{req}.should raise_error(Roda::RodaError)
61
+ end
62
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "accept matcher" do
4
+ it "should accept mimetypes and set response Content-Type" do
5
+ app(:header_matchers) do |r|
6
+ r.on :accept=>"application/xml" do
7
+ response["Content-Type"]
8
+ end
9
+ end
10
+
11
+ body("HTTP_ACCEPT" => "application/xml").should == "application/xml"
12
+ status.should == 404
13
+ end
14
+ end
15
+
16
+ describe "header matcher" do
17
+ it "should match if header present" do
18
+ app(:header_matchers) do |r|
19
+ r.on :header=>"http-accept" do
20
+ "bar"
21
+ end
22
+ end
23
+
24
+ body("HTTP_ACCEPT" => "application/xml").should == "bar"
25
+ status.should == 404
26
+ end
27
+ end
28
+
29
+ describe "host matcher" do
30
+ it "should match a host" do
31
+ app(:header_matchers) do |r|
32
+ r.on :host=>"example.com" do
33
+ "worked"
34
+ end
35
+ end
36
+
37
+ body("HTTP_HOST" => "example.com").should == 'worked'
38
+ status("HTTP_HOST" => "foo.com").should == 404
39
+ end
40
+
41
+ it "should match a host with a regexp" do
42
+ app(:header_matchers) do |r|
43
+ r.on :host=>/example/ do
44
+ "worked"
45
+ end
46
+ end
47
+
48
+ body("HTTP_HOST" => "example.com").should == 'worked'
49
+ status("HTTP_HOST" => "foo.com").should == 404
50
+ end
51
+
52
+ it "doesn't yield HOST" do
53
+ app(:header_matchers) do |r|
54
+ r.on :host=>"example.com" do |*args|
55
+ args.size.to_s
56
+ end
57
+ end
58
+
59
+ body("HTTP_HOST" => "example.com").should == '0'
60
+ end
61
+ end
@@ -0,0 +1,97 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "hooks plugin" do
4
+ before do
5
+ a = @a = []
6
+ app(:bare) do
7
+ plugin :hooks
8
+
9
+ before do
10
+ response['foo'] = 'bar'
11
+ end
12
+
13
+ after do |r|
14
+ if r
15
+ a << [r[0], r[1]['foo'], r[2]]
16
+ r[0] += 1
17
+ else
18
+ a << r
19
+ end
20
+ end
21
+
22
+ route do |r|
23
+ r.is "" do
24
+ f = response['foo']
25
+ response['foo'] = 'baz'
26
+ f
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ it "adds before and after hooks for running code before and after requests" do
33
+ s, h, b = req
34
+ s.should == 201
35
+ h['foo'].should == 'baz'
36
+ b.join.should == 'bar'
37
+ @a.should == [[200, 'baz', ['bar']]]
38
+ end
39
+
40
+ it "multiple plugin calls do not override existing hooks" do
41
+ app.plugin :hooks
42
+ s, h, b = req
43
+ s.should == 201
44
+ h['foo'].should == 'baz'
45
+ b.join.should == 'bar'
46
+ @a.should == [[200, 'baz', ['bar']]]
47
+ end
48
+
49
+ it "after hooks are still called if an exception is raised" do
50
+ a = @a
51
+ @app.before do
52
+ raise Roda::RodaError, "foo"
53
+ end
54
+
55
+ @app.after do |r|
56
+ a << r
57
+ a << $!
58
+ end
59
+
60
+ proc{req}.should raise_error(Roda::RodaError)
61
+ a.pop.should be_a_kind_of(Roda::RodaError)
62
+ a.pop.should == nil
63
+ end
64
+
65
+ it "handles multiple before and after blocks correctly" do
66
+ a = @a
67
+ @app.before do
68
+ response['bar'] = "foo#{response['foo']}"
69
+ end
70
+
71
+ @app.after do |r|
72
+ a << r[1]['bar']
73
+ r[0] *= 2
74
+ end
75
+
76
+ s, h, b = req
77
+ s.should == 402
78
+ h['foo'].should == 'baz'
79
+ h['bar'].should == 'foo'
80
+ b.join.should == 'bar'
81
+ a.should == [[200, 'baz', ['bar']], 'foo']
82
+ end
83
+
84
+ it "copies before and after blocks when subclassing" do
85
+ @app = Class.new(@app)
86
+ @app.route do |r|
87
+ r.on do
88
+ "foo"
89
+ end
90
+ end
91
+ s, h, b = req
92
+ s.should == 201
93
+ h['foo'].should == 'bar'
94
+ b.join.should == 'foo'
95
+ @a.should == [[200, 'bar', ['foo']]]
96
+ end
97
+ end