roda 0.9.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG +3 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +709 -0
- data/Rakefile +124 -0
- data/lib/roda.rb +608 -0
- data/lib/roda/plugins/all_verbs.rb +48 -0
- data/lib/roda/plugins/default_headers.rb +50 -0
- data/lib/roda/plugins/error_handler.rb +69 -0
- data/lib/roda/plugins/flash.rb +62 -0
- data/lib/roda/plugins/h.rb +24 -0
- data/lib/roda/plugins/halt.rb +79 -0
- data/lib/roda/plugins/header_matchers.rb +57 -0
- data/lib/roda/plugins/hooks.rb +106 -0
- data/lib/roda/plugins/indifferent_params.rb +47 -0
- data/lib/roda/plugins/middleware.rb +88 -0
- data/lib/roda/plugins/multi_route.rb +77 -0
- data/lib/roda/plugins/not_found.rb +62 -0
- data/lib/roda/plugins/pass.rb +34 -0
- data/lib/roda/plugins/render.rb +217 -0
- data/lib/roda/plugins/streaming.rb +165 -0
- data/spec/composition_spec.rb +19 -0
- data/spec/env_spec.rb +11 -0
- data/spec/integration_spec.rb +63 -0
- data/spec/matchers_spec.rb +658 -0
- data/spec/module_spec.rb +29 -0
- data/spec/opts_spec.rb +42 -0
- data/spec/plugin/all_verbs_spec.rb +29 -0
- data/spec/plugin/default_headers_spec.rb +63 -0
- data/spec/plugin/error_handler_spec.rb +67 -0
- data/spec/plugin/flash_spec.rb +59 -0
- data/spec/plugin/h_spec.rb +13 -0
- data/spec/plugin/halt_spec.rb +62 -0
- data/spec/plugin/header_matchers_spec.rb +61 -0
- data/spec/plugin/hooks_spec.rb +97 -0
- data/spec/plugin/indifferent_params_spec.rb +13 -0
- data/spec/plugin/middleware_spec.rb +52 -0
- data/spec/plugin/multi_route_spec.rb +98 -0
- data/spec/plugin/not_found_spec.rb +99 -0
- data/spec/plugin/pass_spec.rb +23 -0
- data/spec/plugin/render_spec.rb +148 -0
- data/spec/plugin/streaming_spec.rb +52 -0
- data/spec/plugin_spec.rb +61 -0
- data/spec/redirect_spec.rb +24 -0
- data/spec/request_spec.rb +55 -0
- data/spec/response_spec.rb +131 -0
- data/spec/session_spec.rb +35 -0
- data/spec/spec_helper.rb +89 -0
- data/spec/version_spec.rb +8 -0
- metadata +148 -0
data/spec/module_spec.rb
ADDED
@@ -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
|
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,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 == '<form>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
|