template_streaming 0.0.11 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ <style>
2
+ #uncaught_exceptions {
3
+ position: absolute;
4
+ top: 0px;
5
+ left: 0px;
6
+ margin: 8px;
7
+ background-color: #fff; color: #333;
8
+ }
9
+
10
+ #uncaught_exceptions,
11
+ #uncaught_exceptions p,
12
+ #uncaught_exceptions ol,
13
+ #uncaught_exceptions ul,
14
+ #uncaught_exceptions td {
15
+ font-family: verdana, arial, helvetica, sans-serif;
16
+ font-size: 13px;
17
+ line-height: 18px;
18
+ }
19
+
20
+ #uncaught_exceptions h1 {
21
+ margin-top: 0px;
22
+ }
23
+
24
+ #uncaught_exceptions pre {
25
+ background-color: #eee;
26
+ padding: 10px;
27
+ font-size: 11px;
28
+ }
29
+
30
+ #uncaught_exceptions a { color: #000; }
31
+ #uncaught_exceptions a:visited { color: #666; }
32
+ #uncaught_exceptions a:hover { color: #fff; background-color:#000; }
33
+ </style>
34
+
35
+ <div id="uncaught_exceptions">
36
+ <%= @content %>
37
+ </div>
@@ -1,5 +1,5 @@
1
1
  module TemplateStreaming
2
- VERSION = [0, 0, 11]
2
+ VERSION = [0, 1, 0]
3
3
 
4
4
  class << VERSION
5
5
  include Comparable
@@ -0,0 +1,3 @@
1
+ require 'template_streaming'
2
+ require 'template_streaming/error_recovery'
3
+ require 'template_streaming/new_relic' if defined?(NewRelic)
@@ -0,0 +1,75 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe TemplateStreaming::Autoflushing do
4
+ include StreamingApp
5
+
6
+ describe "when streaming" do
7
+ describe "when autoflushing is on" do
8
+ use_attribute_value TemplateStreaming, :autoflush, 0
9
+
10
+ it "should automatically flush" do
11
+ layout "[<%= yield %>]"
12
+ view "(<%= render :partial => 'partial' %>)"
13
+ partial 'partial'
14
+ action { render :stream => true, :layout => 'layout' }
15
+ run
16
+ received.should == chunks('[', '(', 'partial', ')', ']', :end => true)
17
+ end
18
+
19
+ it "should autoflush correctly for partials with layouts where the partial is given as an option" do
20
+ layout "[<%= yield %>]"
21
+ view "(<%= render :partial => 'partial' %>)"
22
+ partial "{<%= render :layout => 'subpartial_layout', :partial => 'subpartial' %>}"
23
+ template 'test/_subpartial_layout', '<<%= yield %>>'
24
+ template 'test/_subpartial', 'subpartial'
25
+ action { render :stream => true, :layout => 'layout' }
26
+ run
27
+ received.should == chunks('[', '(', '{', '<', 'subpartial', '>', '}', ')', ']', :end => true)
28
+ end
29
+
30
+ it "should autoflush correctly for partials with layouts where the partial is given as a block" do
31
+ layout "[<%= yield %>]"
32
+ view "(<%= render :partial => 'partial' %>)"
33
+ partial "{<% render :layout => 'subpartial_layout' do %>`<%= render :partial => 'subpartial' %>'<% end %>}"
34
+ template 'test/_subpartial_layout', '<<%= yield %>>'
35
+ template 'test/_subpartial', 'subpartial'
36
+ action { render :stream => true, :layout => 'layout' }
37
+ run
38
+ received.should == chunks('[', '(', '{', '<', '`', 'subpartial', '\'', '>', '}', ')', ']', :end => true)
39
+ end
40
+
41
+ it "should autoflush correctly for views with multiple partials" do
42
+ layout "[<%= yield %>][<%= yield %>]"
43
+ view "(<%= render :partial => 'partial' %>)(<%= render :partial => 'partial' %>)"
44
+ partial 'partial'
45
+ action { render :stream => true, :layout => 'layout' }
46
+ run
47
+ received.should == chunks('[', '(', 'partial', ')(', 'partial', ')', '][', '(', 'partial', ')(', 'partial', ')', ']', :end => true)
48
+ end
49
+
50
+ it "should flush correctly when some of the automatic flushes are throttled" do
51
+ with_attribute_value TemplateStreaming, :autoflush, 0.2 do
52
+ data.t = Time.now
53
+ Time.stub(:now).and_return(data.t)
54
+ view <<-EOS.gsub(/^ *\|/, '')
55
+ |<%= 1 -%>
56
+ |<%= Time.stub(:now).and_return(data.t + 0.1); render :partial => 'a' -%>
57
+ |<%= 2 -%>
58
+ |<%= Time.stub(:now).and_return(data.t + 0.2); render :partial => 'b' -%>
59
+ |<%= 3 -%>
60
+ |<%= Time.stub(:now).and_return(data.t + 0.3); render :partial => 'c' -%>
61
+ |<%= 4 -%>
62
+ EOS
63
+ action { render :stream => true, :layout => nil }
64
+ template 'test/_a', 'a'
65
+ template 'test/_b', 'b'
66
+ template 'test/_c', 'c'
67
+ template 'test/_d', 'd'
68
+ action { render :stream => true, :layout => nil }
69
+ run
70
+ received.should == chunks('1', 'a2b3', 'c4', :end => true)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe TemplateStreaming::Caching do
4
+ include StreamingApp
5
+
6
+ describe "page caching" do
7
+ use_attribute_value ActionController::Base, :page_cache_directory, "#{TMP}/page_cache"
8
+ use_attribute_value ActionController::Base, :perform_caching, true
9
+
10
+ before do
11
+ controller.caches_page :action
12
+ action { render :stream => true }
13
+ view "a<% flush %>b"
14
+ end
15
+
16
+ it "should render and cache the page correctly" do
17
+ run
18
+ received.should == chunks('a', 'b', :end => true)
19
+ File.read("#{controller.page_cache_directory}/index.html").should == 'ab'
20
+ end
21
+ end
22
+
23
+ describe "action caching" do
24
+ before do
25
+ push_attribute_value ActionController::Base, :cache_store, ActiveSupport::Cache::MemoryStore.new
26
+ end
27
+
28
+ after do
29
+ pop_attribute_value ActionController::Base, :cache_store
30
+ end
31
+
32
+ describe "when streaming" do
33
+ describe "when no layout is used" do
34
+ before do
35
+ controller.caches_action :action
36
+ end
37
+
38
+ it "should render the page correctly" do
39
+ view "a<% flush %>b"
40
+ action { render :stream => true, :layout => nil }
41
+ run
42
+ received.should == chunks('a', 'b', :end => true)
43
+ end
44
+
45
+ it "should use the cached copy if it exists" do
46
+ view "<% data.render_count += 1 %>a<% flush %>b"
47
+ action { render :stream => true, :layout => nil }
48
+ data.render_count = 0
49
+ run
50
+ run
51
+ received.should == 'ab'
52
+ data.render_count.should == 1
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "when not streaming" do
58
+ describe "when no layout is used" do
59
+ before do
60
+ controller.caches_action :action
61
+ action { render :stream => false, :layout => nil }
62
+ end
63
+
64
+ it "should render the page correctly" do
65
+ view "a<% flush %>b"
66
+ run
67
+ received.should == 'ab'
68
+ end
69
+
70
+ it "should use the cached copy if it exists" do
71
+ view "<% data.render_count += 1 %>a<% flush %>b"
72
+ data.render_count = 0
73
+ run
74
+ run
75
+ received.should == 'ab'
76
+ data.render_count.should == 1
77
+ end
78
+ end
79
+
80
+ describe "when the layout is cached" do
81
+ before do
82
+ controller.caches_action :action, :layout => true
83
+ action { render :stream => false, :layout => 'layout' }
84
+ end
85
+
86
+ it "should cache the layout" do
87
+ layout "<% data.render_count += 1 %>[<%= yield %>]"
88
+ view "view"
89
+ data.render_count = 0
90
+
91
+ run
92
+ received.should == '[view]'
93
+ data.render_count.should == 1
94
+
95
+ run
96
+ received.should == '[view]'
97
+ data.render_count.should == 1
98
+ end
99
+ end
100
+
101
+ describe "when the layout is not cached" do
102
+ before do
103
+ controller.caches_action :action, :layout => false
104
+ # AC always does render(:layout => true) to render the layout when the
105
+ # body is cached, even if an explicit layout name is given. Hence, our
106
+ # layout name must match the controller name.
107
+ action { render :stream => false, :layout => 'test' }
108
+ end
109
+
110
+ it "should not cache the layout" do
111
+ template 'layouts/test', "<% data.render_count += 1 %>[<%= yield %>]"
112
+ view "view"
113
+ data.render_count = 0
114
+
115
+ run
116
+ received.should == '[view]'
117
+ data.render_count.should == 1
118
+
119
+ run
120
+ received.should == '[view]'
121
+ data.render_count.should == 2
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,261 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe TemplateStreaming::ErrorRecovery do
4
+ include StreamingApp
5
+
6
+ describe "when there is an error during rendering" do
7
+ before do
8
+ class TestController
9
+ def rescue_action(exception)
10
+ # Run the default handler.
11
+ ActionController::Base.instance_method(:rescue_action).bind(self).call(exception)
12
+ end
13
+ end
14
+ end
15
+
16
+ describe "when not streaming" do
17
+ it "should show the standard error page" do
18
+ view "<% raise 'test exception' %>"
19
+ run
20
+ received.should include("Action Controller: Exception caught")
21
+ received.should include('test exception')
22
+ end
23
+ end
24
+
25
+ describe "when streaming" do
26
+ before do
27
+ controller.render_streaming_errors_with do |view, exceptions|
28
+ messages = exceptions.map { |e| e.original_exception.message }
29
+ "(#{messages.join(',')})"
30
+ end
31
+ end
32
+
33
+ describe "for local requests" do
34
+ before do
35
+ controller.class_eval do
36
+ def local_request?
37
+ true
38
+ end
39
+ end
40
+ end
41
+
42
+ it "should run the error callback for each error raised" do
43
+ messages = []
44
+ controller.on_streaming_error do |error|
45
+ messages << error.original_exception.message
46
+ end
47
+ view "<% render :partial => 'a' %><% render :partial => 'b' %>"
48
+ template 'test/_a', "<% raise 'a' %>"
49
+ template 'test/_b', "<% raise 'b' %>"
50
+ action { render :stream => true, :layout => nil }
51
+ run
52
+ messages.should == ['a', 'b']
53
+ end
54
+
55
+ describe "when a structurally-complete response is rendered" do
56
+ before do
57
+ view "<% raise 'x' %>"
58
+ action { render :stream => true, :layout => 'layout' }
59
+ end
60
+
61
+ it "should inject errors correctly when the error occurs before the doctype" do
62
+ layout "<%= yield %><!DOCTYPE html><html><head></head><body></body></html>"
63
+ run
64
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>(x)</body></html>", :end => true)
65
+ end
66
+
67
+ it "should inject errors correctly when the error occurs before the opening html tag" do
68
+ layout "<!DOCTYPE html><% flush %><% yield %><html><head></head><body></body></html>"
69
+ run
70
+ received.should == chunks("<!DOCTYPE html>", "<html><head></head><body>(x)</body></html>", :end => true)
71
+ end
72
+
73
+ it "should inject errors correctly when the error occurs before the beginning of the head" do
74
+ layout "<!DOCTYPE html><html><% flush %><% yield %><head></head><body></body></html>"
75
+ run
76
+ received.should == chunks("<!DOCTYPE html><html>", "<head></head><body>(x)</body></html>", :end => true)
77
+ end
78
+
79
+ it "should inject errors correctly when the error occurs during the head" do
80
+ layout "<!DOCTYPE html><html><head><% flush %><% yield %></head><body></body></html>"
81
+ run
82
+ received.should == chunks("<!DOCTYPE html><html><head>", "</head><body>(x)</body></html>", :end => true)
83
+ end
84
+
85
+ it "should inject errors correctly when the error occurs between the head and body" do
86
+ layout "<!DOCTYPE html><html><head></head><% flush %><% yield %><body></body></html>"
87
+ run
88
+ received.should == chunks("<!DOCTYPE html><html><head></head>", "<body>(x)</body></html>", :end => true)
89
+ end
90
+
91
+ it "should inject errors correctly when the error occurs during the body" do
92
+ layout "<!DOCTYPE html><html><head></head><body><% flush %><% yield %></body></html>"
93
+ run
94
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "(x)</body></html>", :end => true)
95
+ end
96
+
97
+ it "should inject errors correctly when the error occurs after the body" do
98
+ layout "<!DOCTYPE html><html><head></head><body></body><% flush %><% yield %></html>"
99
+ run
100
+ received.should == chunks("<!DOCTYPE html><html><head></head><body></body>", "</html>", "(x)", :end => true)
101
+ end
102
+
103
+ it "should inject errors correctly when the error occurs after the closing html tag" do
104
+ layout "<!DOCTYPE html><html><head></head><body></body></html><% flush %><% yield %>"
105
+ run
106
+ received.should == chunks("<!DOCTYPE html><html><head></head><body></body></html>", "(x)", :end => true)
107
+ end
108
+ end
109
+
110
+ describe "when an structurally-incomplete response is rendered" do
111
+ before do
112
+ action { render :stream => true, :layout => nil }
113
+ end
114
+
115
+ it "should inject errors correctly when nothing is rendered" do
116
+ view "<% flush %><% raise 'x' %>"
117
+ run
118
+ received.should == chunks("<!DOCTYPE html><html><head><title>Unhandled Exception</title></head><body>(x)</body></html>", :end => true)
119
+ end
120
+
121
+ it "should inject errors correctly when just the doctype is rendered" do
122
+ view "<!DOCTYPE html><% flush %><% raise 'x' %>"
123
+ run
124
+ received.should == chunks("<!DOCTYPE html>", "<html><head><title>Unhandled Exception</title></head><body>(x)</body></html>", :end => true)
125
+ end
126
+
127
+ it "should inject errors correctly when just the doctype and opening html tag are rendered" do
128
+ view "<!DOCTYPE html><html><% flush %><% raise 'x' %>"
129
+ run
130
+ received.should == chunks("<!DOCTYPE html><html>", "<head><title>Unhandled Exception</title></head><body>(x)</body></html>", :end => true)
131
+ end
132
+
133
+ it "should inject errors correctly when only half the head is rendered" do
134
+ view "<!DOCTYPE html><html><head><% flush %><% raise 'x' %>"
135
+ run
136
+ received.should == chunks("<!DOCTYPE html><html><head>", "</head><body>(x)</body></html>", :end => true)
137
+ end
138
+
139
+ it "should inject errors correctly when only a head is rendered" do
140
+ view "<!DOCTYPE html><html><head></head><% flush %><% raise 'x' %>"
141
+ run
142
+ received.should == chunks("<!DOCTYPE html><html><head></head>", "<body>(x)</body></html>", :end => true)
143
+ end
144
+
145
+ it "should inject errors correctly when the closing body tag is missing" do
146
+ view "<!DOCTYPE html><html><head></head><body><% flush %><% raise 'x' %>"
147
+ run
148
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "(x)</body></html>", :end => true)
149
+ end
150
+
151
+ it "should inject errors correctly when the closing html tag is missing" do
152
+ view "<!DOCTYPE html><html><head></head><body></body><% flush %><% raise 'x' %>"
153
+ run
154
+ received.should == chunks("<!DOCTYPE html><html><head></head><body></body>", "(x)</html>", :end => true)
155
+ end
156
+ end
157
+
158
+ describe "when the response consists of multiple templates" do
159
+ before do
160
+ action { render :stream => true, :layout => 'layout' }
161
+ end
162
+
163
+ it "should inject errors when there is an error in the toplevel layout" do
164
+ layout "<!DOCTYPE html><html><head></head><body><% flush %><%= raise 'x' %></body></html>"
165
+ view ''
166
+ run
167
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "(x)</body></html>", :end => true)
168
+ end
169
+
170
+ it "should inject errors when there is an error in the toplevel view" do
171
+ layout "<!DOCTYPE html><html><head></head><body><% flush %>[<%= yield %>]</body></html>"
172
+ view "<% raise 'x' %>"
173
+ run
174
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "[](x)</body></html>", :end => true)
175
+ end
176
+
177
+ it "should inject errors when there is an error in a partial" do
178
+ layout "<!DOCTYPE html><html><head></head><body><% flush %>[<%= yield %>]</body></html>"
179
+ view "view{<%= render :partial => 'partial' %>}"
180
+ partial "<% raise 'x' %>"
181
+ run
182
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "[view{}](x)</body></html>", :end => true)
183
+ end
184
+
185
+ it "should inject errors when there is an error in a subpartial" do
186
+ layout "<!DOCTYPE html><html><head></head><body><% flush %><%= yield %></body></html>"
187
+ view "view{<%= render :partial => 'partial' %>}"
188
+ partial "partial`<%= render :partial => 'subpartial' %>'"
189
+ template 'test/_subpartial', "<% raise 'x' %>"
190
+ run
191
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "view{partial`'}(x)</body></html>", :end => true)
192
+ end
193
+
194
+ it "should inject errors from all partials which raised an unhandled exception" do
195
+ layout "<!DOCTYPE html><html><head></head><body><% flush %><%= yield %></body></html>"
196
+ view "view[<%= render :partial => 'x' %><%= render :partial => 'ok' %><%= render :partial => 'y' %>]"
197
+ template 'test/_x', "<% raise 'x' %>"
198
+ template 'test/_y', "<% raise 'y' %>"
199
+ template 'test/_ok', "ok"
200
+ run
201
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "view[ok](x,y)</body></html>", :end => true)
202
+ end
203
+ end
204
+ end
205
+
206
+ describe "for nonlocal requests" do
207
+ before do
208
+ controller.class_eval do
209
+ def local_request?
210
+ false
211
+ end
212
+ end
213
+ end
214
+
215
+ it "should run the error callback for each error raised" do
216
+ messages = []
217
+ controller.on_streaming_error do |error|
218
+ messages << error.original_exception.message
219
+ end
220
+ view "<% render :partial => 'a' %><% render :partial => 'b' %>"
221
+ template 'test/_a', "<% raise 'a' %>"
222
+ template 'test/_b', "<% raise 'b' %>"
223
+ action { render :stream => true, :layout => nil }
224
+ run
225
+ messages.should == ['a', 'b']
226
+ end
227
+
228
+ it "should not inject any error information" do
229
+ layout "<!DOCTYPE html><html><head></head><body><% flush %><%= yield %></body></html>"
230
+ view "...<% raise 'x' %>..."
231
+ action { render :stream => true, :layout => 'layout' }
232
+ run
233
+ received.should == chunks("<!DOCTYPE html><html><head></head><body>", "</body></html>", :end => true)
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ describe "the default error rendering callback" do
240
+ before do
241
+ TestController.class_eval do
242
+ def rescue_action(exception)
243
+ # Run the default handler.
244
+ ActionController::Base.instance_method(:rescue_action).bind(self).call(exception)
245
+ end
246
+
247
+ def local_request?
248
+ true
249
+ end
250
+ end
251
+ end
252
+
253
+ it "should render the standard error information" do
254
+ view "<% raise 'test exception' %>"
255
+ action { render :stream => true }
256
+ run
257
+ received.should include('test exception')
258
+ received.should include('#uncaught_exceptions')
259
+ end
260
+ end
261
+ end