roda 1.2.0 → 1.3.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 +4 -4
- data/CHANGELOG +42 -0
- data/README.rdoc +73 -144
- data/doc/conventions.rdoc +10 -8
- data/doc/release_notes/1.3.0.txt +109 -0
- data/lib/roda.rb +67 -100
- data/lib/roda/plugins/assets.rb +4 -4
- data/lib/roda/plugins/chunked.rb +4 -1
- data/lib/roda/plugins/class_level_routing.rb +7 -1
- data/lib/roda/plugins/cookies.rb +34 -0
- data/lib/roda/plugins/default_headers.rb +7 -6
- data/lib/roda/plugins/delegate.rb +8 -1
- data/lib/roda/plugins/delete_empty_headers.rb +33 -0
- data/lib/roda/plugins/delete_nil_headers.rb +34 -0
- data/lib/roda/plugins/environments.rb +12 -4
- data/lib/roda/plugins/error_email.rb +6 -1
- data/lib/roda/plugins/error_handler.rb +7 -4
- data/lib/roda/plugins/hash_matcher.rb +32 -0
- data/lib/roda/plugins/header_matchers.rb +12 -2
- data/lib/roda/plugins/json.rb +9 -6
- data/lib/roda/plugins/module_include.rb +92 -0
- data/lib/roda/plugins/multi_route.rb +7 -0
- data/lib/roda/plugins/multi_run.rb +11 -5
- data/lib/roda/plugins/named_templates.rb +7 -1
- data/lib/roda/plugins/not_found.rb +6 -0
- data/lib/roda/plugins/param_matchers.rb +43 -0
- data/lib/roda/plugins/path_matchers.rb +51 -0
- data/lib/roda/plugins/render.rb +32 -14
- data/lib/roda/plugins/static_path_info.rb +10 -3
- data/lib/roda/plugins/symbol_matchers.rb +1 -1
- data/lib/roda/version.rb +13 -1
- data/spec/freeze_spec.rb +28 -0
- data/spec/plugin/class_level_routing_spec.rb +26 -0
- data/spec/plugin/content_for_spec.rb +1 -2
- data/spec/plugin/cookies_spec.rb +25 -0
- data/spec/plugin/default_headers_spec.rb +4 -7
- data/spec/plugin/delegate_spec.rb +4 -1
- data/spec/plugin/delete_empty_headers_spec.rb +15 -0
- data/spec/plugin/error_handler_spec.rb +31 -0
- data/spec/plugin/hash_matcher_spec.rb +27 -0
- data/spec/plugin/header_matchers_spec.rb +15 -0
- data/spec/plugin/json_spec.rb +1 -2
- data/spec/plugin/mailer_spec.rb +2 -2
- data/spec/plugin/module_include_spec.rb +31 -0
- data/spec/plugin/multi_route_spec.rb +14 -0
- data/spec/plugin/multi_run_spec.rb +41 -0
- data/spec/plugin/named_templates_spec.rb +25 -0
- data/spec/plugin/not_found_spec.rb +29 -0
- data/spec/plugin/param_matchers_spec.rb +37 -0
- data/spec/plugin/path_matchers_spec.rb +42 -0
- data/spec/plugin/render_spec.rb +33 -8
- data/spec/plugin/static_path_info_spec.rb +6 -0
- data/spec/plugin/view_subdirs_spec.rb +1 -2
- data/spec/response_spec.rb +12 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/version_spec.rb +8 -2
- metadata +19 -3
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "delete_empty_headers plugin" do
|
4
|
+
it "automatically deletes headers that are empty" do
|
5
|
+
app(:delete_empty_headers) do |r|
|
6
|
+
response['Foo'] = ''
|
7
|
+
response['Content-Type'] = ''
|
8
|
+
response['Content-Length'] = ''
|
9
|
+
response['Bar'] = '1'
|
10
|
+
'a'
|
11
|
+
end
|
12
|
+
|
13
|
+
req[1].should == {'Bar'=>'1'}
|
14
|
+
end
|
15
|
+
end
|
@@ -39,6 +39,37 @@ describe "error_handler plugin" do
|
|
39
39
|
status.should == 501
|
40
40
|
end
|
41
41
|
|
42
|
+
it "calculates correct Content-Length" do
|
43
|
+
app(:bare) do
|
44
|
+
plugin :error_handler do |e|
|
45
|
+
"a"
|
46
|
+
end
|
47
|
+
|
48
|
+
route do |r|
|
49
|
+
raise ArgumentError, "bad idea"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
header('Content-Length').should == "1"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "clears existing headers" do
|
57
|
+
app(:bare) do
|
58
|
+
plugin :error_handler do |e|
|
59
|
+
"a"
|
60
|
+
end
|
61
|
+
|
62
|
+
route do |r|
|
63
|
+
response['Content-Type'] = 'text/pdf'
|
64
|
+
response['Foo'] = 'bar'
|
65
|
+
raise ArgumentError, "bad idea"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
header('Content-Type').should == 'text/html'
|
70
|
+
header('Foo').should == nil
|
71
|
+
end
|
72
|
+
|
42
73
|
it "can set error via the plugin block" do
|
43
74
|
app(:bare) do
|
44
75
|
plugin :error_handler do |e|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "hash_matcher plugin" do
|
4
|
+
it "should enable the handling of arbitrary hash keys" do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :hash_matcher
|
7
|
+
hash_matcher(:foos){|v| consume(self.class.cached_matcher(:"foos-#{v}"){/((?:foo){#{v}})/})}
|
8
|
+
route do |r|
|
9
|
+
r.is :foos=>1 do |f|
|
10
|
+
"1#{f}"
|
11
|
+
end
|
12
|
+
r.is :foos=>2 do |f|
|
13
|
+
"2#{f}"
|
14
|
+
end
|
15
|
+
r.is :foos=>3 do |f|
|
16
|
+
"3#{f}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
body("/foo").should == '1foo'
|
22
|
+
body("/foofoo").should == '2foofoo'
|
23
|
+
body("/foofoofoo").should == '3foofoofoo'
|
24
|
+
status("/foofoofoofoo").should == 404
|
25
|
+
status.should == 404
|
26
|
+
end
|
27
|
+
end
|
@@ -24,6 +24,21 @@ describe "header matcher" do
|
|
24
24
|
body("HTTP_ACCEPT" => "application/xml").should == "bar"
|
25
25
|
status.should == 404
|
26
26
|
end
|
27
|
+
|
28
|
+
it "should yield the header value if :match_header_yield option is present" do
|
29
|
+
app(:bare) do
|
30
|
+
plugin :header_matchers
|
31
|
+
opts[:match_header_yield] = true
|
32
|
+
route do |r|
|
33
|
+
r.on :header=>"http-accept" do |v|
|
34
|
+
"bar-#{v}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
body("HTTP_ACCEPT" => "application/xml").should == "bar-application/xml"
|
40
|
+
status.should == 404
|
41
|
+
end
|
27
42
|
end
|
28
43
|
|
29
44
|
describe "host matcher" do
|
data/spec/plugin/json_spec.rb
CHANGED
data/spec/plugin/mailer_spec.rb
CHANGED
@@ -91,7 +91,7 @@ describe "mailer plugin" do
|
|
91
91
|
|
92
92
|
it "supports regular web requests in same application" do
|
93
93
|
app(:mailer) do |r|
|
94
|
-
r.get "foo"
|
94
|
+
r.get "foo/:bar" do |bar|
|
95
95
|
"foo#{bar}"
|
96
96
|
end
|
97
97
|
r.mail "bar" do
|
@@ -100,7 +100,7 @@ describe "mailer plugin" do
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
body("/foo", '
|
103
|
+
body("/foo/baz", 'rack.input'=>StringIO.new).should == 'foobaz'
|
104
104
|
app.mail('/bar').body.should == 'b'
|
105
105
|
end
|
106
106
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "module_include plugin" do
|
4
|
+
it "should include given module in request or response class" do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :module_include
|
7
|
+
request_module(Module.new{def h; halt response.finish end})
|
8
|
+
response_module(Module.new{def finish; [1, {}, []] end})
|
9
|
+
|
10
|
+
route do |r|
|
11
|
+
r.h
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
req.should == [1, {}, []]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept blocks and turn them into modules" do
|
19
|
+
app(:bare) do
|
20
|
+
plugin :module_include
|
21
|
+
request_module{def h; halt response.finish end}
|
22
|
+
response_module{def finish; [1, {}, []] end}
|
23
|
+
|
24
|
+
route do |r|
|
25
|
+
r.h
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
req.should == [1, {}, []]
|
30
|
+
end
|
31
|
+
end
|
@@ -76,6 +76,20 @@ describe "multi_route plugin" do
|
|
76
76
|
status('/c', 'REQUEST_METHOD'=>'POST').should == 404
|
77
77
|
end
|
78
78
|
|
79
|
+
it "works when freezing the app" do
|
80
|
+
app.freeze
|
81
|
+
body.should == 'get'
|
82
|
+
body('REQUEST_METHOD'=>'POST').should == 'post'
|
83
|
+
body('/a').should == 'geta'
|
84
|
+
body('/a', 'REQUEST_METHOD'=>'POST').should == 'posta'
|
85
|
+
body('/b').should == 'getb'
|
86
|
+
body('/b', 'REQUEST_METHOD'=>'POST').should == 'postb'
|
87
|
+
status('/c').should == 404
|
88
|
+
status('/c', 'REQUEST_METHOD'=>'POST').should == 404
|
89
|
+
|
90
|
+
proc{app.route("foo"){}}.should raise_error
|
91
|
+
end
|
92
|
+
|
79
93
|
it "uses multi_route to dispatch to any named route" do
|
80
94
|
status('/foo').should == 404
|
81
95
|
body('/foo/get/').should == 'get'
|
@@ -28,4 +28,45 @@ describe "multi_run plugin" do
|
|
28
28
|
body("/b/a").should == 'b2'
|
29
29
|
body.should == 'c'
|
30
30
|
end
|
31
|
+
|
32
|
+
it "works when freezing the app" do
|
33
|
+
app(:multi_run) do |r|
|
34
|
+
r.multi_run
|
35
|
+
"c"
|
36
|
+
end
|
37
|
+
|
38
|
+
app.run "a", Class.new(Roda).class_eval{route{"a1"}; app}
|
39
|
+
app.run "b", Class.new(Roda).class_eval{route{"b1"}; app}
|
40
|
+
app.run "b/a", Class.new(Roda).class_eval{route{"b2"}; app}
|
41
|
+
app.freeze
|
42
|
+
|
43
|
+
body("/a").should == 'a1'
|
44
|
+
body("/b").should == 'b1'
|
45
|
+
body("/b/a").should == 'b2'
|
46
|
+
body.should == 'c'
|
47
|
+
|
48
|
+
proc{app.run "a", Class.new(Roda).class_eval{route{"a1"}; app}}.should raise_error
|
49
|
+
end
|
50
|
+
|
51
|
+
it "works when subclassing" do
|
52
|
+
app(:multi_run) do |r|
|
53
|
+
r.multi_run
|
54
|
+
"c"
|
55
|
+
end
|
56
|
+
|
57
|
+
app.run "a", Class.new(Roda).class_eval{route{"a1"}; app}
|
58
|
+
body("/a").should == 'a1'
|
59
|
+
|
60
|
+
a = app
|
61
|
+
@app = Class.new(a)
|
62
|
+
|
63
|
+
a.run "b", Class.new(Roda).class_eval{route{"b2"}; app}
|
64
|
+
app.run "b", Class.new(Roda).class_eval{route{"b1"}; app}
|
65
|
+
|
66
|
+
body("/a").should == 'a1'
|
67
|
+
body("/b").should == 'b1'
|
68
|
+
|
69
|
+
@app = a
|
70
|
+
body("/b").should == 'b2'
|
71
|
+
end
|
31
72
|
end
|
@@ -25,6 +25,31 @@ describe "named_templates plugin" do
|
|
25
25
|
body.should == 'bar13-foo12-baz'
|
26
26
|
end
|
27
27
|
|
28
|
+
it "works when freezing the app" do
|
29
|
+
app(:bare) do
|
30
|
+
plugin :named_templates
|
31
|
+
|
32
|
+
template :foo do
|
33
|
+
@b = 2
|
34
|
+
"foo<%= @a %><%= @b %>"
|
35
|
+
end
|
36
|
+
template :layout, :engine=>:str do
|
37
|
+
@c = 3
|
38
|
+
'bar#{@a}#{@c}-#{yield}-baz'
|
39
|
+
end
|
40
|
+
|
41
|
+
route do |r|
|
42
|
+
@a = 1
|
43
|
+
view(:foo)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
app.freeze
|
48
|
+
body.should == 'bar13-foo12-baz'
|
49
|
+
|
50
|
+
proc{app.template(:b){"a"}}.should raise_error
|
51
|
+
end
|
52
|
+
|
28
53
|
it "works with the view_subdirs plugin" do
|
29
54
|
app(:bare) do
|
30
55
|
plugin :render
|
@@ -38,6 +38,35 @@ describe "not_found plugin" do
|
|
38
38
|
status.should == 403
|
39
39
|
end
|
40
40
|
|
41
|
+
it "calculates correct Content-Length" do
|
42
|
+
app(:bare) do
|
43
|
+
plugin :not_found do
|
44
|
+
"a"
|
45
|
+
end
|
46
|
+
|
47
|
+
route{}
|
48
|
+
end
|
49
|
+
|
50
|
+
header('Content-Length').should == "1"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "clears existing headers" do
|
54
|
+
app(:bare) do
|
55
|
+
plugin :not_found do ||
|
56
|
+
"a"
|
57
|
+
end
|
58
|
+
|
59
|
+
route do |r|
|
60
|
+
response['Content-Type'] = 'text/pdf'
|
61
|
+
response['Foo'] = 'bar'
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
header('Content-Type').should == 'text/html'
|
67
|
+
header('Foo').should == nil
|
68
|
+
end
|
69
|
+
|
41
70
|
it "does not modify behavior if not_found is not called" do
|
42
71
|
app(:not_found) do |r|
|
43
72
|
r.on "a" do
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "param_matchers plugin" do
|
4
|
+
it "param! matcher should yield a param only if given and not empty" do
|
5
|
+
app(:param_matchers) do |r|
|
6
|
+
r.get "signup", :param! => "email" do |email|
|
7
|
+
email
|
8
|
+
end
|
9
|
+
|
10
|
+
r.on do
|
11
|
+
"No email"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
io = StringIO.new
|
16
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "email=john@doe.com").should == 'john@doe.com'
|
17
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "").should == 'No email'
|
18
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "email=").should == 'No email'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "param matcheshould yield a param only if given" do
|
22
|
+
app(:param_matchers) do |r|
|
23
|
+
r.get "signup", :param=>"email" do |email|
|
24
|
+
email
|
25
|
+
end
|
26
|
+
|
27
|
+
r.on do
|
28
|
+
"No email"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
io = StringIO.new
|
33
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "email=john@doe.com").should == 'john@doe.com'
|
34
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "").should == 'No email'
|
35
|
+
body("/signup", "rack.input" => io, "QUERY_STRING" => "email=").should == ''
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "path_matchers plugin" do
|
4
|
+
it ":extension matcher should match given file extension" do
|
5
|
+
app(:path_matchers) do |r|
|
6
|
+
r.on "css" do
|
7
|
+
r.on :extension=>"css" do |file|
|
8
|
+
file
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
body("/css/reset.css").should == 'reset'
|
14
|
+
status("/css/reset.bar").should == 404
|
15
|
+
end
|
16
|
+
|
17
|
+
it ":suffix matcher should match given suffix" do
|
18
|
+
app(:path_matchers) do |r|
|
19
|
+
r.on "css" do
|
20
|
+
r.on :suffix=>".css" do |file|
|
21
|
+
file
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
body("/css/reset.css").should == 'reset'
|
27
|
+
status("/css/reset.bar").should == 404
|
28
|
+
end
|
29
|
+
|
30
|
+
it ":prefix matcher should match given prefix" do
|
31
|
+
app(:path_matchers) do |r|
|
32
|
+
r.on "css" do
|
33
|
+
r.on :prefix=>"reset" do |file|
|
34
|
+
file
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
body("/css/reset.css").should == '.css'
|
40
|
+
status("/css/foo.bar").should == 404
|
41
|
+
end
|
42
|
+
end
|
data/spec/plugin/render_spec.rb
CHANGED
@@ -9,8 +9,7 @@ else
|
|
9
9
|
describe "render plugin" do
|
10
10
|
before do
|
11
11
|
app(:bare) do
|
12
|
-
plugin :render
|
13
|
-
render_opts[:views] = "./spec/views"
|
12
|
+
plugin :render, :views=>"./spec/views"
|
14
13
|
|
15
14
|
route do |r|
|
16
15
|
r.on "home" do
|
@@ -45,14 +44,14 @@ describe "render plugin" do
|
|
45
44
|
end
|
46
45
|
|
47
46
|
it "with str as engine" do
|
48
|
-
app.
|
47
|
+
app.plugin :render, :engine => "str"
|
49
48
|
body("/about").strip.should == "<h1>About Roda</h1>"
|
50
49
|
body("/home").strip.should == "<title>Roda: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
51
50
|
body("/inline").strip.should == "Hello <%= name %>"
|
52
51
|
end
|
53
52
|
|
54
53
|
it "with str as ext" do
|
55
|
-
app.
|
54
|
+
app.plugin :render, :ext => "str"
|
56
55
|
body("/about").strip.should == "<h1>About Roda</h1>"
|
57
56
|
body("/home").strip.should == "<title>Roda: Home</title>\n<h1>Home</h1>\n<p>Hello Agent Smith</p>"
|
58
57
|
body("/inline").strip.should == "Hello Agent Smith"
|
@@ -125,9 +124,11 @@ describe "render plugin" do
|
|
125
124
|
end
|
126
125
|
|
127
126
|
it "template renders with :template opts" do
|
128
|
-
app(:
|
129
|
-
|
130
|
-
|
127
|
+
app(:bare) do
|
128
|
+
plugin :render, :views => "./spec/views"
|
129
|
+
route do
|
130
|
+
render(:template=>"about", :locals=>{:title => "About Roda"})
|
131
|
+
end
|
131
132
|
end
|
132
133
|
body.strip.should == "<h1>About Roda</h1>"
|
133
134
|
end
|
@@ -164,6 +165,30 @@ describe "render plugin" do
|
|
164
165
|
body('/b').should == "i-b"
|
165
166
|
end
|
166
167
|
|
168
|
+
it "template cache respects :template_opts" do
|
169
|
+
c = Class.new do
|
170
|
+
def initialize(path, _, opts)
|
171
|
+
@path = path
|
172
|
+
@opts = opts
|
173
|
+
end
|
174
|
+
def render(*)
|
175
|
+
"#{@path}-#{@opts[:foo]}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
app(:render) do |r|
|
180
|
+
r.is "a" do
|
181
|
+
render(:inline=>"i", :template_class=>c, :template_opts=>{:foo=>'a'})
|
182
|
+
end
|
183
|
+
r.is "b" do
|
184
|
+
render(:inline=>"i", :template_class=>c, :template_opts=>{:foo=>'b'})
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
body('/a').should == "i-a"
|
189
|
+
body('/b').should == "i-b"
|
190
|
+
end
|
191
|
+
|
167
192
|
it "render_opts inheritance" do
|
168
193
|
c = Class.new(Roda)
|
169
194
|
c.plugin :render
|
@@ -171,7 +196,7 @@ describe "render plugin" do
|
|
171
196
|
|
172
197
|
c.render_opts.should_not equal(sc.render_opts)
|
173
198
|
c.render_opts[:layout_opts].should_not equal(sc.render_opts[:layout_opts])
|
174
|
-
c.render_opts[:
|
199
|
+
c.render_opts[:template_opts].should_not equal(sc.render_opts[:template_opts])
|
175
200
|
c.render_opts[:cache].should_not equal(sc.render_opts[:cache])
|
176
201
|
end
|
177
202
|
|