roda-cj 0.9.1

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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +13 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +715 -0
  5. data/Rakefile +124 -0
  6. data/lib/roda/plugins/all_verbs.rb +48 -0
  7. data/lib/roda/plugins/default_headers.rb +50 -0
  8. data/lib/roda/plugins/error_handler.rb +69 -0
  9. data/lib/roda/plugins/flash.rb +108 -0
  10. data/lib/roda/plugins/h.rb +24 -0
  11. data/lib/roda/plugins/halt.rb +79 -0
  12. data/lib/roda/plugins/header_matchers.rb +57 -0
  13. data/lib/roda/plugins/hooks.rb +106 -0
  14. data/lib/roda/plugins/indifferent_params.rb +47 -0
  15. data/lib/roda/plugins/middleware.rb +88 -0
  16. data/lib/roda/plugins/multi_route.rb +77 -0
  17. data/lib/roda/plugins/not_found.rb +62 -0
  18. data/lib/roda/plugins/pass.rb +34 -0
  19. data/lib/roda/plugins/render.rb +217 -0
  20. data/lib/roda/plugins/streaming.rb +165 -0
  21. data/lib/roda/version.rb +3 -0
  22. data/lib/roda.rb +610 -0
  23. data/spec/composition_spec.rb +19 -0
  24. data/spec/env_spec.rb +11 -0
  25. data/spec/integration_spec.rb +63 -0
  26. data/spec/matchers_spec.rb +683 -0
  27. data/spec/module_spec.rb +29 -0
  28. data/spec/opts_spec.rb +42 -0
  29. data/spec/plugin/all_verbs_spec.rb +29 -0
  30. data/spec/plugin/default_headers_spec.rb +63 -0
  31. data/spec/plugin/error_handler_spec.rb +67 -0
  32. data/spec/plugin/flash_spec.rb +123 -0
  33. data/spec/plugin/h_spec.rb +13 -0
  34. data/spec/plugin/halt_spec.rb +62 -0
  35. data/spec/plugin/header_matchers_spec.rb +61 -0
  36. data/spec/plugin/hooks_spec.rb +97 -0
  37. data/spec/plugin/indifferent_params_spec.rb +13 -0
  38. data/spec/plugin/middleware_spec.rb +52 -0
  39. data/spec/plugin/multi_route_spec.rb +98 -0
  40. data/spec/plugin/not_found_spec.rb +99 -0
  41. data/spec/plugin/pass_spec.rb +23 -0
  42. data/spec/plugin/render_spec.rb +148 -0
  43. data/spec/plugin/streaming_spec.rb +52 -0
  44. data/spec/plugin_spec.rb +61 -0
  45. data/spec/redirect_spec.rb +24 -0
  46. data/spec/request_spec.rb +55 -0
  47. data/spec/response_spec.rb +131 -0
  48. data/spec/session_spec.rb +35 -0
  49. data/spec/spec_helper.rb +89 -0
  50. data/spec/version_spec.rb +8 -0
  51. metadata +136 -0
data/spec/opts_spec.rb ADDED
@@ -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,123 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "flash plugin" do
4
+ it "flash.now[] sets flash for current page" do
5
+ app(:bare) do
6
+ use Rack::Session::Cookie, :secret => "1"
7
+ plugin :flash
8
+
9
+ route do |r|
10
+ r.on do
11
+ flash.now['a'] = 'b'
12
+ flash['a']
13
+ end
14
+ end
15
+ end
16
+
17
+ body.should == 'b'
18
+ end
19
+
20
+ it "flash[] sets flash for next page" do
21
+ app(:bare) do
22
+ use Rack::Session::Cookie, :secret => "1"
23
+ plugin :flash
24
+
25
+ route do |r|
26
+ r.on 'a' do
27
+ "c#{flash['a']}"
28
+ end
29
+
30
+ r.on do
31
+ flash['a'] = "b#{flash['a']}"
32
+ flash['a'] || ''
33
+ end
34
+ end
35
+ end
36
+
37
+ env = proc{|h| h['Set-Cookie'] ? {'HTTP_COOKIE'=>h['Set-Cookie'].sub("; path=/; HttpOnly", '')} : {}}
38
+ _, h, b = req
39
+ b.join.should == ''
40
+ _, h, b = req(env[h])
41
+ b.join.should == 'b'
42
+ _, h, b = req(env[h])
43
+ b.join.should == 'bb'
44
+ _, h, b = req('/a', env[h])
45
+ b.join.should == 'cbbb'
46
+ _, h, b = req(env[h])
47
+ b.join.should == ''
48
+ _, h, b = req(env[h])
49
+ b.join.should == 'b'
50
+ _, h, b = req(env[h])
51
+ b.join.should == 'bb'
52
+ end
53
+ end
54
+
55
+ describe "FlashHash" do
56
+ before do
57
+ @h = Roda::RodaPlugins::Flash::FlashHash.new
58
+ end
59
+
60
+ it ".new should accept nil for empty hash" do
61
+ @h = Roda::RodaPlugins::Flash::FlashHash.new(nil)
62
+ @h.now.should == {}
63
+ @h.next.should == {}
64
+ end
65
+
66
+ it ".new should accept a hash" do
67
+ @h = Roda::RodaPlugins::Flash::FlashHash.new(1=>2)
68
+ @h.now.should == {1=>2}
69
+ @h.next.should == {}
70
+ end
71
+
72
+ it "#[]= assigns to next flash" do
73
+ @h[1] = 2
74
+ @h.now.should == {}
75
+ @h.next.should == {1=>2}
76
+ end
77
+
78
+ it "#discard removes given key from next hash" do
79
+ @h[1] = 2
80
+ @h[nil] = 3
81
+ @h.next.should == {1=>2, nil=>3}
82
+ @h.discard(nil)
83
+ @h.next.should == {1=>2}
84
+ @h.discard(1)
85
+ @h.next.should == {}
86
+ end
87
+
88
+ it "#discard removes all entries from next hash with no arguments" do
89
+ @h[1] = 2
90
+ @h[nil] = 3
91
+ @h.next.should == {1=>2, nil=>3}
92
+ @h.discard
93
+ @h.next.should == {}
94
+ end
95
+
96
+ it "#keep copies entry for key from current hash to next hash" do
97
+ @h.now[1] = 2
98
+ @h.now[nil] = 3
99
+ @h.next.should == {}
100
+ @h.keep(nil)
101
+ @h.next.should == {nil=>3}
102
+ @h.keep(1)
103
+ @h.next.should == {1=>2, nil=>3}
104
+ end
105
+
106
+ it "#keep copies all entries from current hash to next hash" do
107
+ @h.now[1] = 2
108
+ @h.now[nil] = 3
109
+ @h.next.should == {}
110
+ @h.keep
111
+ @h.next.should == {1=>2, nil=>3}
112
+ end
113
+
114
+ it "#sweep replaces current hash with next hash" do
115
+ @h[1] = 2
116
+ @h[nil] = 3
117
+ @h.next.should == {1=>2, nil=>3}
118
+ @h.now.should == {}
119
+ @h.sweep.should == {1=>2, nil=>3}
120
+ @h.next.should == {}
121
+ @h.now.should == {1=>2, nil=>3}
122
+ end
123
+ 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
@@ -0,0 +1,13 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "indifferent_params plugin" do
4
+ it "allows indifferent access to request params via params method" do
5
+ app(:indifferent_params) do |r|
6
+ r.on do
7
+ "#{params[:a]}/#{params[:b][0][:c]}"
8
+ end
9
+ end
10
+
11
+ body('QUERY_STRING'=>'a=2&b[][c]=3', 'rack.input'=>StringIO.new).should == '2/3'
12
+ end
13
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "middleware plugin" do
4
+ it "turns Roda app into middlware" do
5
+ a2 = app(:bare) do
6
+ plugin :middleware
7
+
8
+ route do |r|
9
+ r.is "a" do
10
+ "a2"
11
+ end
12
+ r.post "b" do
13
+ "b2"
14
+ end
15
+ end
16
+ end
17
+
18
+ a3 = app(:bare) do
19
+ plugin :middleware
20
+
21
+ route do |r|
22
+ r.get "a" do
23
+ "a3"
24
+ end
25
+ r.get "b" do
26
+ "b3"
27
+ end
28
+ end
29
+ end
30
+
31
+ app(:bare) do
32
+ use a3
33
+ use a2
34
+
35
+ route do |r|
36
+ r.is "a" do
37
+ "a1"
38
+ end
39
+ r.is "b" do
40
+ "b1"
41
+ end
42
+ end
43
+ end
44
+
45
+ body('/a').should == 'a3'
46
+ body('/b').should == 'b3'
47
+ body('/a', 'REQUEST_METHOD'=>'POST').should == 'a2'
48
+ body('/b', 'REQUEST_METHOD'=>'POST').should == 'b2'
49
+ body('/a', 'REQUEST_METHOD'=>'PATCH').should == 'a2'
50
+ body('/b', 'REQUEST_METHOD'=>'PATCH').should == 'b1'
51
+ end
52
+ end