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
@@ -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
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "multi_route plugin" do
|
4
|
+
before do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :multi_route
|
7
|
+
|
8
|
+
route(:get) do |r|
|
9
|
+
r.is "" do
|
10
|
+
"get"
|
11
|
+
end
|
12
|
+
|
13
|
+
r.is "a" do
|
14
|
+
"geta"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
route(:post) do |r|
|
19
|
+
r.is "" do
|
20
|
+
"post"
|
21
|
+
end
|
22
|
+
|
23
|
+
r.is "a" do
|
24
|
+
"posta"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
route do |r|
|
29
|
+
r.get do
|
30
|
+
route(:get)
|
31
|
+
|
32
|
+
r.is "b" do
|
33
|
+
"getb"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
r.post do
|
37
|
+
route(:post)
|
38
|
+
|
39
|
+
r.is "b" do
|
40
|
+
"postb"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it "adds named routing support" do
|
48
|
+
body.should == 'get'
|
49
|
+
body('REQUEST_METHOD'=>'POST').should == 'post'
|
50
|
+
body('/a').should == 'geta'
|
51
|
+
body('/a', 'REQUEST_METHOD'=>'POST').should == 'posta'
|
52
|
+
body('/b').should == 'getb'
|
53
|
+
body('/b', 'REQUEST_METHOD'=>'POST').should == 'postb'
|
54
|
+
status('/c').should == 404
|
55
|
+
status('/c', 'REQUEST_METHOD'=>'POST').should == 404
|
56
|
+
end
|
57
|
+
|
58
|
+
it "handles loading the plugin multiple times correctly" do
|
59
|
+
app.plugin :multi_route
|
60
|
+
body.should == 'get'
|
61
|
+
body('REQUEST_METHOD'=>'POST').should == 'post'
|
62
|
+
body('/a').should == 'geta'
|
63
|
+
body('/a', 'REQUEST_METHOD'=>'POST').should == 'posta'
|
64
|
+
body('/b').should == 'getb'
|
65
|
+
body('/b', 'REQUEST_METHOD'=>'POST').should == 'postb'
|
66
|
+
status('/c').should == 404
|
67
|
+
status('/c', 'REQUEST_METHOD'=>'POST').should == 404
|
68
|
+
end
|
69
|
+
|
70
|
+
it "handles subclassing correctly" do
|
71
|
+
@app = Class.new(@app)
|
72
|
+
@app.route do |r|
|
73
|
+
r.get do
|
74
|
+
route(:post)
|
75
|
+
|
76
|
+
r.is "b" do
|
77
|
+
"1b"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
r.post do
|
81
|
+
route(:get)
|
82
|
+
|
83
|
+
r.is "b" do
|
84
|
+
"2b"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
body.should == 'post'
|
90
|
+
body('REQUEST_METHOD'=>'POST').should == 'get'
|
91
|
+
body('/a').should == 'posta'
|
92
|
+
body('/a', 'REQUEST_METHOD'=>'POST').should == 'geta'
|
93
|
+
body('/b').should == '1b'
|
94
|
+
body('/b', 'REQUEST_METHOD'=>'POST').should == '2b'
|
95
|
+
status('/c').should == 404
|
96
|
+
status('/c', 'REQUEST_METHOD'=>'POST').should == 404
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "not_found plugin" do
|
4
|
+
it "executes on no arguments" do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :not_found
|
7
|
+
|
8
|
+
not_found do
|
9
|
+
"not found"
|
10
|
+
end
|
11
|
+
|
12
|
+
route do |r|
|
13
|
+
r.on "a" do
|
14
|
+
"found"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
body.should == 'not found'
|
20
|
+
status.should == 404
|
21
|
+
body("/a").should == 'found'
|
22
|
+
status("/a").should == 200
|
23
|
+
end
|
24
|
+
|
25
|
+
it "allows overriding status inside not_found" do
|
26
|
+
app(:bare) do
|
27
|
+
plugin :not_found
|
28
|
+
|
29
|
+
not_found do
|
30
|
+
response.status = 403
|
31
|
+
"not found"
|
32
|
+
end
|
33
|
+
|
34
|
+
route do |r|
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
status.should == 403
|
39
|
+
end
|
40
|
+
|
41
|
+
it "does not modify behavior if not_found is not called" do
|
42
|
+
app(:not_found) do |r|
|
43
|
+
r.on "a" do
|
44
|
+
"found"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
body.should == ''
|
49
|
+
body("/a").should == 'found'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can set not_found via the plugin block" do
|
53
|
+
app(:bare) do
|
54
|
+
plugin :not_found do
|
55
|
+
"not found"
|
56
|
+
end
|
57
|
+
|
58
|
+
route do |r|
|
59
|
+
r.on "a" do
|
60
|
+
"found"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
body.should == 'not found'
|
66
|
+
body("/a").should == 'found'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "does not modify behavior if body is not an array" do
|
70
|
+
app(:bare) do
|
71
|
+
plugin :not_found do
|
72
|
+
"not found"
|
73
|
+
end
|
74
|
+
|
75
|
+
o = Object.new
|
76
|
+
def o.join() '' end
|
77
|
+
route do |r|
|
78
|
+
r.halt [404, {}, o]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
body.should == ''
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not modify behavior if body is not an empty array" do
|
86
|
+
app(:bare) do
|
87
|
+
plugin :not_found do
|
88
|
+
"not found"
|
89
|
+
end
|
90
|
+
|
91
|
+
route do |r|
|
92
|
+
response.status = 404
|
93
|
+
response.write 'a'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
body.should == 'a'
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "pass plugin" do
|
4
|
+
it "executes on no arguments" do
|
5
|
+
app(:pass) do |r|
|
6
|
+
r.on :id do |id|
|
7
|
+
r.pass if id == 'foo'
|
8
|
+
id
|
9
|
+
end
|
10
|
+
|
11
|
+
r.on ":x/:y" do |x, y|
|
12
|
+
x + y
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
body("/a").should == 'a'
|
17
|
+
body("/a/b").should == 'a'
|
18
|
+
body("/foo/a").should == 'fooa'
|
19
|
+
body("/foo/a/b").should == 'fooa'
|
20
|
+
status("/foo").should == 404
|
21
|
+
status.should == 404
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'tilt'
|
5
|
+
rescue LoadError
|
6
|
+
warn "tilt not installed, skipping render plugin test"
|
7
|
+
else
|
8
|
+
describe "render plugin" do
|
9
|
+
before do
|
10
|
+
app(:bare) do
|
11
|
+
plugin :render
|
12
|
+
render_opts[:views] = "./spec/views"
|
13
|
+
|
14
|
+
route do |r|
|
15
|
+
r.on "home" do
|
16
|
+
view("home", :locals=>{:name => "Agent Smith", :title => "Home"}, :layout_opts=>{:locals=>{:title=>"Home"}})
|
17
|
+
end
|
18
|
+
|
19
|
+
r.on "about" do
|
20
|
+
render("about", :locals=>{:title => "About Roda"})
|
21
|
+
end
|
22
|
+
|
23
|
+
r.on "inline" do
|
24
|
+
view(:inline=>"Hello <%= name %>", :locals=>{:name => "Agent Smith"}, :layout=>nil)
|
25
|
+
end
|
26
|
+
|
27
|
+
r.on "path" do
|
28
|
+
render(:path=>"./spec/views/about.erb", :locals=>{:title => "Path"}, :layout_opts=>{:locals=>{:title=>"Home"}})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "default actions" do
|
35
|
+
body("/about").strip.should == "<h1>About Roda</h1>"
|
36
|
+
body("/home").strip.should == "<title>Roda: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
37
|
+
body("/inline").strip.should == "Hello Agent Smith"
|
38
|
+
body("/path").strip.should == "<h1>Path</h1>"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "with str as engine" do
|
42
|
+
app.render_opts[:engine] = "str"
|
43
|
+
body("/about").strip.should == "<h1>About Roda</h1>"
|
44
|
+
body("/home").strip.should == "<title>Roda: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
45
|
+
body("/inline").strip.should == "Hello <%= name %>"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "with str as ext" do
|
49
|
+
app.render_opts[:ext] = "str"
|
50
|
+
body("/about").strip.should == "<h1>About Roda</h1>"
|
51
|
+
body("/home").strip.should == "<title>Roda: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
52
|
+
body("/inline").strip.should == "Hello Agent Smith"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "custom default layout support" do
|
56
|
+
app.render_opts[:layout] = "layout-alternative"
|
57
|
+
body("/home").strip.should == "<title>Alternative Layout: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "render plugin" do
|
62
|
+
it "simple layout support" do
|
63
|
+
app(:bare) do
|
64
|
+
plugin :render
|
65
|
+
|
66
|
+
route do |r|
|
67
|
+
render(:path=>"spec/views/layout-yield.erb") do
|
68
|
+
render(:path=>"spec/views/content-yield.erb")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
body.gsub(/\n+/, "\n").should == "Header\nThis is the actual content.\nFooter\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "views without default layouts" do
|
77
|
+
app(:bare) do
|
78
|
+
plugin :render, :views=>"./spec/views", :layout=>false
|
79
|
+
|
80
|
+
route do |r|
|
81
|
+
view("home", :locals=>{:name=>"Agent Smith", :title=>"Home"})
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
body.strip.should == "<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "layout overrides" do
|
89
|
+
app(:bare) do
|
90
|
+
plugin :render, :views=>"./spec/views"
|
91
|
+
|
92
|
+
route do |r|
|
93
|
+
view("home", :locals=>{:name=>"Agent Smith", :title=>"Home" }, :layout=>"layout-alternative", :layout_opts=>{:locals=>{:title=>"Home"}})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
body.strip.should == "<title>Alternative Layout: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "inline layouts and inline views" do
|
101
|
+
app(:render) do
|
102
|
+
view({:inline=>'bar'}, :layout=>{:inline=>'Foo: <%= yield %>'})
|
103
|
+
end
|
104
|
+
|
105
|
+
body.strip.should == "Foo: bar"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "inline renders with opts" do
|
109
|
+
app(:render) do
|
110
|
+
render({:inline=>'<%= bar %>'}, {:engine=>'str'})
|
111
|
+
end
|
112
|
+
|
113
|
+
body.strip.should == '<%= bar %>'
|
114
|
+
end
|
115
|
+
|
116
|
+
it "render_opts inheritance" do
|
117
|
+
c = Class.new(Roda)
|
118
|
+
c.plugin :render
|
119
|
+
sc = Class.new(c)
|
120
|
+
|
121
|
+
c.render_opts.should_not equal(sc.render_opts)
|
122
|
+
c.render_opts[:layout_opts].should_not equal(sc.render_opts[:layout_opts])
|
123
|
+
c.render_opts[:opts].should_not equal(sc.render_opts[:opts])
|
124
|
+
c.render_opts[:cache].should_not equal(sc.render_opts[:cache])
|
125
|
+
end
|
126
|
+
|
127
|
+
it "render plugin call should not override options" do
|
128
|
+
c = Class.new(Roda)
|
129
|
+
c.plugin :render, :layout=>:foo
|
130
|
+
c.plugin :render
|
131
|
+
c.render_opts[:layout].should == :foo
|
132
|
+
end
|
133
|
+
|
134
|
+
it "with caching disabled" do
|
135
|
+
app(:bare) do
|
136
|
+
plugin :render, :views=>"./spec/views", :cache=>false
|
137
|
+
|
138
|
+
route do |r|
|
139
|
+
view(:inline=>"Hello <%= name %>: <%= render_opts[:cache] %>", :locals=>{:name => "Agent Smith"}, :layout=>nil)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
body("/inline").strip.should == "Hello Agent Smith: false"
|
144
|
+
|
145
|
+
Class.new(app).render_opts[:cache].should == false
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "streaming plugin" do
|
4
|
+
it "adds stream method for streaming responses" do
|
5
|
+
app(:streaming) do |r|
|
6
|
+
stream do |out|
|
7
|
+
%w'a b c'.each{|v| out << v}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
s, h, b = req
|
12
|
+
s.should == 200
|
13
|
+
h.should == {'Content-Type'=>'text/html'}
|
14
|
+
b.to_a.should == %w'a b c'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should handle errors when streaming, and run callbacks" do
|
18
|
+
a = []
|
19
|
+
app(:streaming) do |r|
|
20
|
+
stream(:callback=>proc{a << 'e'}) do |out|
|
21
|
+
%w'a b'.each{|v| out << v}
|
22
|
+
raise Roda::RodaError, 'foo'
|
23
|
+
out << 'c'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
s, h, b = req
|
28
|
+
s.should == 200
|
29
|
+
h.should == {'Content-Type'=>'text/html'}
|
30
|
+
b.callback{a << 'd'}
|
31
|
+
proc{b.each{|v| a << v}}.should raise_error(Roda::RodaError)
|
32
|
+
a.should == %w'a b e d'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should handle :loop option to loop" do
|
36
|
+
a = []
|
37
|
+
app(:streaming) do |r|
|
38
|
+
b = %w'a b c'
|
39
|
+
stream(:loop=>true, :callback=>proc{a << 'e'}) do |out|
|
40
|
+
out << b.shift
|
41
|
+
raise Roda::RodaError, 'foo' if b.length == 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
s, h, b = req
|
46
|
+
s.should == 200
|
47
|
+
h.should == {'Content-Type'=>'text/html'}
|
48
|
+
b.callback{a << 'd'}
|
49
|
+
proc{b.each{|v| a << v}}.should raise_error(Roda::RodaError)
|
50
|
+
a.should == %w'a b e d'
|
51
|
+
end
|
52
|
+
end
|