roda 2.28.0 → 2.29.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +46 -0
  3. data/README.rdoc +25 -7
  4. data/doc/release_notes/2.29.0.txt +156 -0
  5. data/lib/roda.rb +25 -3
  6. data/lib/roda/plugins/_erubis_escaping.rb +2 -0
  7. data/lib/roda/plugins/_symbol_regexp_matchers.rb +22 -0
  8. data/lib/roda/plugins/assets.rb +3 -2
  9. data/lib/roda/plugins/branch_locals.rb +74 -0
  10. data/lib/roda/plugins/caching.rb +15 -7
  11. data/lib/roda/plugins/chunked.rb +10 -7
  12. data/lib/roda/plugins/content_for.rb +4 -1
  13. data/lib/roda/plugins/drop_body.rb +3 -2
  14. data/lib/roda/plugins/error_email.rb +3 -2
  15. data/lib/roda/plugins/error_mail.rb +3 -2
  16. data/lib/roda/plugins/head.rb +2 -1
  17. data/lib/roda/plugins/header_matchers.rb +3 -0
  18. data/lib/roda/plugins/heartbeat.rb +3 -2
  19. data/lib/roda/plugins/json.rb +5 -3
  20. data/lib/roda/plugins/json_parser.rb +3 -2
  21. data/lib/roda/plugins/mailer.rb +3 -3
  22. data/lib/roda/plugins/match_affix.rb +6 -0
  23. data/lib/roda/plugins/multi_route.rb +3 -1
  24. data/lib/roda/plugins/padrino_render.rb +3 -2
  25. data/lib/roda/plugins/params_capturing.rb +3 -3
  26. data/lib/roda/plugins/partials.rb +3 -3
  27. data/lib/roda/plugins/path.rb +4 -2
  28. data/lib/roda/plugins/path_rewriter.rb +2 -2
  29. data/lib/roda/plugins/per_thread_caching.rb +2 -0
  30. data/lib/roda/plugins/placeholder_string_matchers.rb +42 -0
  31. data/lib/roda/plugins/precompile_templates.rb +3 -2
  32. data/lib/roda/plugins/render.rb +86 -37
  33. data/lib/roda/plugins/render_each.rb +2 -1
  34. data/lib/roda/plugins/render_locals.rb +102 -0
  35. data/lib/roda/plugins/run_append_slash.rb +2 -1
  36. data/lib/roda/plugins/run_handler.rb +2 -1
  37. data/lib/roda/plugins/sinatra_helpers.rb +4 -4
  38. data/lib/roda/plugins/static_path_info.rb +2 -0
  39. data/lib/roda/plugins/static_routing.rb +1 -1
  40. data/lib/roda/plugins/streaming.rb +9 -4
  41. data/lib/roda/plugins/symbol_matchers.rb +23 -20
  42. data/lib/roda/plugins/view_options.rb +63 -28
  43. data/lib/roda/plugins/view_subdirs.rb +1 -0
  44. data/lib/roda/plugins/websockets.rb +2 -0
  45. data/lib/roda/version.rb +1 -1
  46. data/spec/composition_spec.rb +2 -2
  47. data/spec/matchers_spec.rb +6 -5
  48. data/spec/plugin/_erubis_escaping_spec.rb +5 -5
  49. data/spec/plugin/backtracking_array_spec.rb +0 -2
  50. data/spec/plugin/branch_locals_spec.rb +88 -0
  51. data/spec/plugin/content_for_spec.rb +8 -2
  52. data/spec/plugin/halt_spec.rb +8 -0
  53. data/spec/plugin/header_matchers_spec.rb +20 -5
  54. data/spec/plugin/multi_route_spec.rb +1 -1
  55. data/spec/plugin/named_templates_spec.rb +2 -2
  56. data/spec/plugin/params_capturing_spec.rb +1 -1
  57. data/spec/plugin/per_thread_caching_spec.rb +1 -1
  58. data/spec/plugin/placeholder_string_matchers_spec.rb +159 -0
  59. data/spec/plugin/render_locals_spec.rb +114 -0
  60. data/spec/plugin/render_spec.rb +83 -8
  61. data/spec/plugin/streaming_spec.rb +104 -4
  62. data/spec/plugin/symbol_matchers_spec.rb +1 -1
  63. data/spec/plugin/view_options_spec.rb +83 -7
  64. data/spec/plugin/websockets_spec.rb +7 -8
  65. data/spec/spec_helper.rb +22 -2
  66. metadata +11 -2
@@ -20,7 +20,7 @@ describe "_erubis_escaping plugin" do
20
20
  end
21
21
  end
22
22
 
23
- it "should escape inside <%= %> and not inside <%== %>, and handle postfix conditionals" do
23
+ deprecated "should escape inside <%= %> and not inside <%== %>, and handle postfix conditionals" do
24
24
  app(:bare) do
25
25
  plugin :render, :escape=>true
26
26
 
@@ -32,7 +32,7 @@ describe "_erubis_escaping plugin" do
32
32
  body.must_equal '&lt;&gt; <>'
33
33
  end
34
34
 
35
- it "should consider classes in :escape_safe_classes as safe" do
35
+ deprecated "should consider classes in :escape_safe_classes as safe" do
36
36
  c = Class.new(String)
37
37
  c2 = Class.new(String)
38
38
  app(:bare) do
@@ -47,7 +47,7 @@ describe "_erubis_escaping plugin" do
47
47
  body.must_equal '&lt;&gt; <>'
48
48
  end
49
49
 
50
- it "should covnert arguments to strings when escaping with safe classes" do
50
+ deprecated "should covnert arguments to strings when escaping with safe classes" do
51
51
  app(:bare) do
52
52
  plugin :render, :escape=>true, :escape_safe_classes=>[]
53
53
 
@@ -59,7 +59,7 @@ describe "_erubis_escaping plugin" do
59
59
  body.must_equal '&lt;&gt; <>'
60
60
  end
61
61
 
62
- it "should allow use of custom :escaper" do
62
+ deprecated "should allow use of custom :escaper" do
63
63
  escaper = Object.new
64
64
  def escaper.escape_xml(s)
65
65
  s.gsub("'", "''")
@@ -75,7 +75,7 @@ describe "_erubis_escaping plugin" do
75
75
  body.must_equal "ab''1 ab'1"
76
76
  end
77
77
 
78
- it "should allow for per-branch escaping via set_view options" do
78
+ deprecated "should allow for per-branch escaping via set_view options" do
79
79
  app(:bare) do
80
80
  plugin :render, :escape=>true
81
81
  plugin :view_options
@@ -38,7 +38,5 @@ describe "backtracking_array plugin" do
38
38
  end
39
39
 
40
40
  tests.call
41
- app.plugin(:static_path_info)
42
- tests.call
43
41
  end
44
42
  end
@@ -0,0 +1,88 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ begin
4
+ require 'tilt/erb'
5
+ rescue LoadError
6
+ warn "tilt not installed, skipping branch_locals plugin test"
7
+ else
8
+
9
+ describe "branch_locals plugin" do
10
+ it "should set view and layout locals to use" do
11
+ app(:branch_locals) do
12
+ set_view_locals :title=>'About Roda'
13
+ set_layout_locals :title=>'Home'
14
+ view(:inline=>'<h1><%= title %></h1>', :layout=>{:inline=>"<title>Alternative Layout: <%= title %></title>\n<%= yield %>"})
15
+ end
16
+
17
+ body.strip.must_equal "<title>Alternative Layout: Home</title>\n<h1>About Roda</h1>"
18
+ end
19
+
20
+ it "should merge multiple calls to set view and layout locals" do
21
+ app(:branch_locals) do
22
+ set_layout_locals :title=>'About Roda'
23
+ set_view_locals :title=>'Home'
24
+
25
+ set_layout_locals :a=>'A'
26
+ set_view_locals :b=>'B'
27
+
28
+ view(:inline=>'<%= title %>:<%= b %>', :layout=>{:inline=>"<%= title %>:<%= a %>::<%= yield %>"})
29
+ end
30
+
31
+ body.strip.must_equal "About Roda:A::Home:B"
32
+ end
33
+
34
+ it "should merge multiple calls in the correct order" do
35
+ app(:branch_locals) do
36
+ set_layout_locals :title=>'Roda'
37
+ set_view_locals :title=>'H'
38
+
39
+ set_layout_locals :a=>'A', :title=>'About Roda'
40
+ set_view_locals :b=>'B', :title=>'Home'
41
+
42
+ view(:inline=>'<%= title %>:<%= b %>', :layout=>{:inline=>"<%= title %>:<%= a %>::<%= yield %>"})
43
+ end
44
+
45
+ body.strip.must_equal "About Roda:A::Home:B"
46
+ end
47
+
48
+ it "should have set_view_locals have more precedence than plugin options, but less than view/render method options" do
49
+ app(:bare) do
50
+ plugin :render, :views=>"./spec/views", :layout_opts=>{:template=>'multiple-layout'}
51
+ plugin :render_locals, :render=>{:title=>'Home', :b=>'B'}, :layout=>{:title=>'About Roda', :a=>'A'}
52
+ plugin :branch_locals
53
+
54
+ route do |r|
55
+ r.is 'c' do
56
+ view(:multiple)
57
+ end
58
+
59
+ set_view_locals :b=>'BB'
60
+ set_layout_locals :a=>'AA'
61
+
62
+ r.on 'b' do
63
+ set_view_locals :title=>'About'
64
+ set_layout_locals :title=>'Roda'
65
+
66
+ r.is 'a' do
67
+ view(:multiple)
68
+ end
69
+
70
+ view("multiple", :locals=>{:b => "BBB"}, :layout_opts=>{:locals=>{:a=>'AAA'}})
71
+ end
72
+
73
+ r.is 'a' do
74
+ view(:multiple)
75
+ end
76
+
77
+ view("multiple", :locals=>{:b => "BBB"}, :layout_opts=>{:locals=>{:a=>'AAA'}})
78
+ end
79
+ end
80
+
81
+ body('/c').strip.must_equal "About Roda:A::Home:B"
82
+ body('/b/a').strip.must_equal "Roda:AA::About:BB"
83
+ body('/b').strip.must_equal "Roda:AAA::About:BBB"
84
+ body('/a').strip.must_equal "About Roda:AA::Home:BB"
85
+ body.strip.must_equal "About Roda:AAA::Home:BBB"
86
+ end
87
+ end
88
+ end
@@ -52,11 +52,17 @@ describe "content_for plugin with multiple calls to the same key" do
52
52
  end
53
53
  end
54
54
 
55
- it "should replace with multiple calls to the same key by default" do
55
+ deprecated "should replace with multiple calls to the same key by default" do
56
56
  body.strip.must_equal "bar baz"
57
57
  end
58
58
 
59
- it "should append with multiple calls to the same key if :append plugin option is used" do
59
+ it "should replace with multiple calls to the same key if :append=>false plugin option is used" do
60
+ app.plugin :content_for, :append => false
61
+ body.strip.must_equal "bar baz"
62
+ end
63
+
64
+ # RODA3: Make default behavior
65
+ it "should append with multiple calls to the same key if :append=>true plugin option is used" do
60
66
  app.plugin :content_for, :append => true
61
67
  body.strip.must_equal "bar foobaz"
62
68
  end
@@ -102,6 +102,14 @@ describe "halt plugin" do
102
102
  end
103
103
 
104
104
  it "should raise an error for single argument not integer, String, or Array" do
105
+ app(:halt) do |r|
106
+ r.halt nil
107
+ end
108
+
109
+ proc{req}.must_raise(Roda::RodaError)
110
+ end
111
+
112
+ deprecated "should raise an error for single argument not integer, String, or Array" do
105
113
  app(:halt) do |r|
106
114
  r.halt('a'=>'b')
107
115
  end
@@ -14,7 +14,8 @@ describe "accept matcher" do
14
14
  end
15
15
 
16
16
  describe "header matcher" do
17
- it "should match if header present" do
17
+ # RODA3: undeprecate, and switch http-accept to accept
18
+ deprecated "should match if header present" do
18
19
  app(:header_matchers) do |r|
19
20
  r.on :header=>"http-accept" do
20
21
  "bar"
@@ -28,15 +29,17 @@ describe "header matcher" do
28
29
 
29
30
  it "should yield the header value" do
30
31
  app(:header_matchers) do |r|
31
- r.on :header=>"http-accept" do |v|
32
+ r.on :header=>"accept" do |v|
32
33
  "bar-#{v}"
33
34
  end
34
35
  end
35
36
 
37
+ app.opts[:header_matcher_prefix] = true
36
38
  body("HTTP_ACCEPT" => "application/xml").must_equal "bar-application/xml"
37
39
  status.must_equal 404
38
40
  end
39
41
 
42
+ # RODA3: Remove
40
43
  it "should automatically use HTTP prefix for headers if :header_matcher_prefix is set" do
41
44
  app(:bare) do
42
45
  plugin :header_matchers
@@ -65,7 +68,8 @@ describe "host matcher" do
65
68
  status("HTTP_HOST" => "foo.com").must_equal 404
66
69
  end
67
70
 
68
- it "should match a host with a regexp" do
71
+ # RODA3: switch deprecated to it
72
+ deprecated "should match a host with a regexp" do
69
73
  app(:header_matchers) do |r|
70
74
  r.on :host=>/example/ do
71
75
  "worked"
@@ -76,7 +80,7 @@ describe "host matcher" do
76
80
  status("HTTP_HOST" => "foo.com").must_equal 404
77
81
  end
78
82
 
79
- it "doesn't yield host" do
83
+ it "doesn't yield host if given a string" do
80
84
  app(:header_matchers) do |r|
81
85
  r.on :host=>"example.com" do |*args|
82
86
  args.size.to_s
@@ -86,7 +90,7 @@ describe "host matcher" do
86
90
  body("HTTP_HOST" => "example.com").must_equal '0'
87
91
  end
88
92
 
89
- it "yields host if passed a regexp and opts[:host_matcher_captures] is set" do
93
+ deprecated "doesn't yield host if passed a regexp" do
90
94
  app(:header_matchers) do |r|
91
95
  r.on :host=>/\A(.*)\.example\.com\z/ do |*m|
92
96
  m.empty? ? '0' : m[0]
@@ -94,10 +98,21 @@ describe "host matcher" do
94
98
  end
95
99
 
96
100
  body("HTTP_HOST" => "foo.example.com").must_equal '0'
101
+ end
102
+
103
+ # RODA3: Remove :host_matcher_captures setting
104
+ it "yields host if passed a regexp and opts[:host_matcher_captures] is set" do
105
+ app(:header_matchers) do |r|
106
+ r.on :host=>/\A(.*)\.example\.com\z/ do |*m|
107
+ m.empty? ? '0' : m[0]
108
+ end
109
+ end
110
+
97
111
  app.opts[:host_matcher_captures] = true
98
112
  body("HTTP_HOST" => "foo.example.com").must_equal 'foo'
99
113
  end
100
114
 
115
+ # RODA3: Remove
101
116
  it "doesn't yields host if passed a string and opts[:host_matcher_captures] is set" do
102
117
  app(:header_matchers) do |r|
103
118
  r.on :host=>'example.com' do |*m|
@@ -171,7 +171,7 @@ describe "multi_route plugin" do
171
171
  end
172
172
 
173
173
  describe "multi_route plugin" do
174
- it "r.multi_route works even without routes defined" do
174
+ deprecated "r.multi_route works even without routes defined" do
175
175
  app(:multi_route) do |r|
176
176
  r.multi_route
177
177
  'a'
@@ -55,10 +55,10 @@ describe "named_templates plugin" do
55
55
  proc{app.template(:b){"a"}}.must_raise
56
56
  end
57
57
 
58
- it "works with the view_subdirs plugin" do
58
+ it "works with the view_options plugin" do
59
59
  app(:bare) do
60
60
  plugin :render
61
- plugin :view_subdirs
61
+ plugin :view_options
62
62
  plugin :named_templates
63
63
 
64
64
  template "foo/bar" do
@@ -31,7 +31,7 @@ describe "params_capturing plugin" do
31
31
  body('/quux/asdf/890', 'rack.input'=>StringIO.new).must_equal 'y--890-quux-asdf-890-3'
32
32
  end
33
33
 
34
- it "should add captures to r.params for string matchers" do
34
+ deprecated "should add captures to r.params for string matchers" do
35
35
  app(:params_capturing) do |r|
36
36
  r.on("bar/:foo") do |foo|
37
37
  "b-#{foo}-#{r[:foo]}-#{r[:captures].length}"
@@ -1,7 +1,7 @@
1
1
  require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
2
 
3
3
  describe "per_thread_caching plugin" do
4
- it "should use a per thread cache instead of a shared cache" do
4
+ deprecated "should use a per thread cache instead of a shared cache" do
5
5
  app(:bare) do
6
6
  plugin :per_thread_caching
7
7
  @c = thread_safe_cache
@@ -0,0 +1,159 @@
1
+ require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
2
+
3
+ describe "placeholder_string_matchers plugin" do
4
+ it "should handle string with embedded param" do
5
+ app(:placeholder_string_matchers) do |r|
6
+ r.on "posts/:id" do |id|
7
+ id
8
+ end
9
+
10
+ r.on "responses-:id" do |id|
11
+ id
12
+ end
13
+ end
14
+
15
+ body('/posts/123').must_equal '123'
16
+ status('/post/123').must_equal 404
17
+ body('/responses-123').must_equal '123'
18
+ end
19
+
20
+ it "should handle multiple params in single string" do
21
+ app(:placeholder_string_matchers) do |r|
22
+ r.on "u/:uid/posts/:id" do |uid, id|
23
+ uid + id
24
+ end
25
+ end
26
+
27
+ body("/u/jdoe/posts/123").must_equal 'jdoe123'
28
+ status("/u/jdoe/pots/123").must_equal 404
29
+ end
30
+
31
+ it "should escape regexp metacharaters in string" do
32
+ app(:placeholder_string_matchers) do |r|
33
+ r.on "u/:uid/posts?/:id" do |uid, id|
34
+ uid + id
35
+ end
36
+ end
37
+
38
+ body("/u/jdoe/posts?/123").must_equal 'jdoe123'
39
+ status("/u/jdoe/post/123").must_equal 404
40
+ end
41
+
42
+ it "should handle colons by themselves" do
43
+ app(:placeholder_string_matchers) do |r|
44
+ r.on "u/:/:uid/posts/::id" do |uid, id|
45
+ uid + id
46
+ end
47
+ end
48
+
49
+ body("/u/:/jdoe/posts/:123").must_equal 'jdoe123'
50
+ status("/u/a/jdoe/post/b123").must_equal 404
51
+ end
52
+
53
+ it "should work with params_capturing plugin to add captures to r.params for string matchers" do
54
+ app(:bare) do
55
+ plugin :placeholder_string_matchers
56
+ plugin :params_capturing
57
+
58
+ route do |r|
59
+ r.on("bar/:foo") do |foo|
60
+ "b-#{foo}-#{r[:foo]}-#{r[:captures].length}"
61
+ end
62
+
63
+ r.on("baz/:bar", :foo) do |bar, foo|
64
+ "b-#{bar}-#{foo}-#{r[:bar]}-#{r[:foo]}-#{r[:captures].length}"
65
+ end
66
+ end
67
+ end
68
+
69
+ body('/bar/banana', 'rack.input'=>StringIO.new).must_equal 'b-banana-banana-1'
70
+ body('/baz/ban/ana', 'rack.input'=>StringIO.new).must_equal 'b-ban-ana-ban-ana-2'
71
+ end
72
+
73
+ it "works with symbol_matchers plugin" do
74
+ app(:bare) do
75
+ plugin :placeholder_string_matchers
76
+ plugin :symbol_matchers
77
+ symbol_matcher(:f, /(f+)/)
78
+
79
+ route do |r|
80
+ r.is ":d" do |d|
81
+ "d#{d}"
82
+ end
83
+
84
+ r.is "thing/:thing" do |d|
85
+ "thing#{d}"
86
+ end
87
+
88
+ r.is "thing2", ":thing" do |d|
89
+ "thing2#{d}"
90
+ end
91
+
92
+ r.is ":f" do |f|
93
+ "f#{f}"
94
+ end
95
+
96
+ r.is 'q:rest' do |rest|
97
+ "rest#{rest}"
98
+ end
99
+
100
+ r.is ":w" do |w|
101
+ "w#{w}"
102
+ end
103
+
104
+ r.is ':d/:w/:f' do |d, w, f|
105
+ "dwf#{d}#{w}#{f}"
106
+ end
107
+ end
108
+ end
109
+
110
+ status.must_equal 404
111
+ body("/1").must_equal 'd1'
112
+ body("/11232135").must_equal 'd11232135'
113
+ body("/a").must_equal 'wa'
114
+ body("/1az0").must_equal 'w1az0'
115
+ body("/f").must_equal 'ff'
116
+ body("/ffffffffffffffff").must_equal 'fffffffffffffffff'
117
+ status("/-").must_equal 404
118
+ body("/1/1a/f").must_equal 'dwf11af'
119
+ body("/12/1azy/fffff").must_equal 'dwf121azyfffff'
120
+ status("/1/f/a").must_equal 404
121
+ body("/qa/b/c/d//f/g").must_equal 'resta/b/c/d//f/g'
122
+ body('/q').must_equal 'rest'
123
+ body('/thing/q').must_equal 'thingq'
124
+ body('/thing2/q').must_equal 'thing2q'
125
+ end
126
+
127
+ deprecated "works with symbol_matchers plugin and deprecated matchers" do
128
+ app(:bare) do
129
+ plugin :placeholder_string_matchers
130
+ plugin :symbol_matchers
131
+ symbol_matcher(:f, /(f+)/)
132
+
133
+ route do |r|
134
+ r.is "foo:optd" do |o|
135
+ "foo#{o.inspect}"
136
+ end
137
+
138
+ r.is "bar:opt" do |o|
139
+ "bar#{o.inspect}"
140
+ end
141
+
142
+ r.is "format:format" do |f|
143
+ "format#{f.inspect}"
144
+ end
145
+ end
146
+ end
147
+
148
+ body("/foo").must_equal 'foonil'
149
+ body("/foo/123").must_equal 'foo"123"'
150
+ status("/foo/bar").must_equal 404
151
+ status("/foo/123/a").must_equal 404
152
+ body("/bar").must_equal 'barnil'
153
+ body("/bar/foo").must_equal 'bar"foo"'
154
+ status("/bar/foo/baz").must_equal 404
155
+ body("/format").must_equal 'formatnil'
156
+ body("/format.json").must_equal 'format"json"'
157
+ status("/format.").must_equal 404
158
+ end
159
+ end