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,13 @@
1
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ ROOT = File.expand_path('..', File.dirname(__FILE__))
4
+ TMP = "#{ROOT}/spec/tmp"
5
+
6
+ require 'bundler'
7
+ Bundler.setup(:default, :development)
8
+
9
+ require 'action_controller'
10
+ require 'template_streaming'
11
+ require 'temporaries'
12
+
13
+ require 'support/streaming_app'
@@ -0,0 +1,135 @@
1
+ module StreamingApp
2
+ VIEW_PATH = "#{TMP}/views"
3
+ COOKIE_SECRET = 'x'*30
4
+
5
+ def self.included(base)
6
+ base.before { setup_streaming_app }
7
+ base.after { teardown_streaming_app }
8
+ end
9
+
10
+ def setup_streaming_app
11
+ push_temporary_directory TMP
12
+
13
+ ActionController::Base.session = {:key => "session", :secret => COOKIE_SECRET}
14
+ ActionController::Routing::Routes.clear!
15
+ ActionController::Routing::Routes.add_route('/', :controller => 'test', :action => 'action')
16
+
17
+ push_constant_value Object, :TestController, Class.new(Controller)
18
+ # Since we use Class.new, the class name is undefined in AC::Layout's
19
+ # inherited hook, and so layout is not automatically called - do it now.
20
+ TestController.layout('test', {}, true)
21
+ TestController.view_paths = [VIEW_PATH]
22
+ @log_buffer = ''
23
+ TestController.logger = Logger.new(StringIO.new(@log_buffer))
24
+
25
+ $current_spec = self
26
+ @data = OpenStruct.new
27
+ end
28
+
29
+ def controller
30
+ TestController
31
+ end
32
+
33
+ def teardown_streaming_app
34
+ pop_constant_value Object, :TestController
35
+ pop_temporary_directory
36
+ FileUtils.rm_rf VIEW_PATH
37
+ $current_spec = nil
38
+ end
39
+
40
+ def view(text)
41
+ template("test/action", text)
42
+ end
43
+
44
+ def layout(text)
45
+ template("layouts/layout", text)
46
+ end
47
+
48
+ def partial(text)
49
+ template("test/_partial", text)
50
+ end
51
+
52
+ def template(template_path, text)
53
+ path = "#{controller.view_paths.first}/#{template_path}.html.erb"
54
+ FileUtils.mkdir_p File.dirname(path)
55
+ open(path, 'w') { |f| f.print text }
56
+ end
57
+
58
+ def action(&block)
59
+ TestController.class_eval do
60
+ define_method(:action, &block)
61
+ end
62
+ end
63
+
64
+ def run(env_overrides={})
65
+ env = default_env.merge(env_overrides)
66
+ app = ActionController::Dispatcher.new
67
+ @data.received = ''
68
+ @status, @headers, @body = app.call(env)
69
+ @body.each do |chunk|
70
+ @data.received << chunk
71
+ end
72
+ end
73
+
74
+ attr_reader :status, :headers, :body, :data
75
+
76
+ def session
77
+ cookie_value = headers['Set-Cookie'].scan(/^session=([^;]*)/).first.first
78
+ verifier = ActiveSupport::MessageVerifier.new(COOKIE_SECRET, 'SHA1')
79
+ verifier.verify(CGI.unescape(cookie_value))
80
+ end
81
+
82
+ def default_env
83
+ {
84
+ 'REQUEST_METHOD' => 'GET',
85
+ 'SCRIPT_NAME' => '',
86
+ 'PATH_INFO' => '/',
87
+ 'QUERY_STRING' => '',
88
+ 'SERVER_NAME' => 'test.example.com',
89
+ 'SERVER_PORT' => '',
90
+ 'rack.version' => [1, 1],
91
+ 'rack.url_scheme' => 'http',
92
+ 'rack.input' => StringIO.new,
93
+ 'rack.errors' => StringIO.new,
94
+ 'rack.multithread' => false,
95
+ 'rack.multiprocess' => false,
96
+ 'rack.run_once' => true,
97
+ 'rack.logger' => Logger.new(STDERR),
98
+ }
99
+ end
100
+
101
+ class Controller < ActionController::Base
102
+ def action
103
+ end
104
+
105
+ def rescue_action(exception)
106
+ STDERR.puts "#{exception.class}: #{exception.message}"
107
+ STDERR.puts exception.backtrace.join("\n").gsub(/^/, ' ')
108
+ raise exception
109
+ end
110
+ end
111
+
112
+ module Helpers
113
+ def data
114
+ $current_spec.data
115
+ end
116
+
117
+ def received
118
+ data.received
119
+ end
120
+
121
+ def chunks(*chunks)
122
+ options = chunks.last.is_a?(Hash) ? chunks.pop : {}
123
+ content = ''
124
+ chunks.each do |chunk|
125
+ content << chunk.size.to_s(16) << "\r\n" << chunk << "\r\n"
126
+ end
127
+ content << "0\r\n\r\n" if options[:end]
128
+ content
129
+ end
130
+ end
131
+
132
+ include Helpers
133
+ Controller.send :include, Helpers
134
+ ActionView::Base.send :include, Helpers
135
+ end
@@ -0,0 +1,926 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe TemplateStreaming do
4
+ include StreamingApp
5
+
6
+ describe "#flush" do
7
+ describe "when streaming" do
8
+ before do
9
+ action do
10
+ render :stream => true, :layout => 'layout'
11
+ end
12
+ end
13
+
14
+ it "should flush the rendered content immediately" do
15
+ layout <<-'EOS'.gsub(/^ *\|/, '')
16
+ |1
17
+ |<% flush -%>
18
+ |<% received.should == chunks("1\n") -%>
19
+ |<%= yield -%>
20
+ |<% flush -%>
21
+ |<% received.should == chunks("1\n", "a\n", "b\n", "c\n") -%>
22
+ |2
23
+ |<% flush -%>
24
+ |<% received.should == chunks("1\n", "a\n", "b\n", "c\n", "2\n") -%>
25
+ EOS
26
+
27
+ view <<-'EOS'.gsub(/^ *\|/, '')
28
+ |a
29
+ |<% flush -%>
30
+ |<% received.should == chunks("1\n", "a\n") -%>
31
+ |b
32
+ |<% flush -%>
33
+ |<% received.should == chunks("1\n", "a\n", "b\n") -%>
34
+ |c
35
+ EOS
36
+
37
+ run
38
+ received.should == chunks("1\n", "a\n", "b\n", "c\n", "2\n", :end => true)
39
+ end
40
+ end
41
+
42
+ describe "when not streaming" do
43
+ it "should not affect the output" do
44
+ view "a<% flush %>b"
45
+ action { render :stream => false, :layout => nil }
46
+ run
47
+ received.should == 'ab'
48
+ end
49
+
50
+ it "should not invert the layout rendering order" do
51
+ view "<% data.order << :view -%>"
52
+ layout "<% data.order << :layout1 -%><%= yield -%><% data.order << :layout2 -%>"
53
+ action { render :stream => false, :layout => 'layout' }
54
+ data.order = []
55
+ run
56
+ data.order.should == [:view, :layout1, :layout2]
57
+ end
58
+ end
59
+ end
60
+
61
+ describe "#push" do
62
+ describe "when streaming" do
63
+ before do
64
+ action do
65
+ render :stream => true, :layout => 'layout'
66
+ end
67
+ end
68
+
69
+ it "should send the given data to the client immediately" do
70
+ layout <<-'EOS'.gsub(/^ *\|/, '')
71
+ |<% push 'a' -%>
72
+ |<% received.should == chunks("a") -%>
73
+ |<% push 'b' -%>
74
+ |<% received.should == chunks("a", "b") -%>
75
+ EOS
76
+ view ''
77
+ run
78
+ received.should == chunks("a", "b", :end => true)
79
+ end
80
+ end
81
+
82
+ describe "when not streaming" do
83
+ before do
84
+ action do
85
+ render :stream => false, :layout => 'layout'
86
+ end
87
+ end
88
+
89
+ it "should do nothing" do
90
+ layout <<-'EOS'.gsub(/^ *\|/, '')
91
+ |<% push 'a' -%>
92
+ |<% received.should == '' -%>
93
+ |x
94
+ EOS
95
+ view ''
96
+ run
97
+ received.should == "x\n"
98
+ end
99
+ end
100
+ end
101
+
102
+ describe "response headers" do
103
+ describe "when streaming" do
104
+ before do
105
+ action do
106
+ render :stream => true, :layout => nil
107
+ end
108
+ end
109
+
110
+ it "should not set a content length" do
111
+ view ''
112
+ run
113
+ headers.key?('Content-Length').should be_false
114
+ end
115
+
116
+ it "should specify chunked transfer encoding" do
117
+ view ''
118
+ run
119
+ headers['Transfer-Encoding'].should == 'chunked'
120
+ end
121
+ end
122
+
123
+ describe "when not streaming" do
124
+ before do
125
+ action do
126
+ render :stream => false, :layout => nil
127
+ end
128
+ end
129
+
130
+ it "should not specify a transfer encoding" do
131
+ view ''
132
+ run
133
+ headers.key?('Transfer-Encoding').should be_false
134
+ end
135
+
136
+ it "should set a content length" do
137
+ view ''
138
+ run
139
+ headers['Content-Length'].should == '0'
140
+ end
141
+ end
142
+ end
143
+
144
+ describe ".stream" do
145
+ before do
146
+ TestController.layout 'layout'
147
+ layout "[<% flush %><%= yield %>]"
148
+ view "a"
149
+ end
150
+
151
+ it "should stream all actions if no options are given" do
152
+ TestController.stream
153
+ run
154
+ received.should == chunks('[', 'a]', :end => true)
155
+ end
156
+
157
+ it "should stream the action if it is included with :only" do
158
+ TestController.stream :only => :action
159
+ run
160
+ received.should == chunks('[', 'a]', :end => true)
161
+ end
162
+
163
+ it "should not stream the action if it is excepted" do
164
+ TestController.stream :except => :action
165
+ run
166
+ received.should == "[a]"
167
+ end
168
+
169
+ it "should be overridden to true by an explicit :stream => true when rendering" do
170
+ TestController.stream :except => :action
171
+ action do
172
+ render :stream => true
173
+ end
174
+ run
175
+ received.should == chunks('[', 'a]', :end => true)
176
+ end
177
+
178
+ it "should be overridden to false by an explicit :stream => false when rendering" do
179
+ TestController.stream :only => :action
180
+ action do
181
+ render :stream => false
182
+ end
183
+ run
184
+ received.should == "[a]"
185
+ end
186
+ end
187
+
188
+ describe "#render in the controller" do
189
+ describe "when streaming" do
190
+ before do
191
+ @render_options = {:stream => true}
192
+ view "(<% flush %><%= render :partial => 'partial' %>)"
193
+ partial "a<% flush %>b"
194
+ end
195
+
196
+ describe "with a layout" do
197
+ before do
198
+ @render_options[:layout] = 'layout'
199
+ layout "[<% flush %><%= yield %>]"
200
+ end
201
+
202
+ it "should stream templates specified with :action" do
203
+ render_options = @render_options
204
+ action do
205
+ render render_options.merge(:action => 'action')
206
+ end
207
+ run
208
+ received.should == chunks('[', '(', 'a', 'b)]', :end => true)
209
+ end
210
+
211
+ it "should stream templates specified with :partial" do
212
+ render_options = @render_options
213
+ action do
214
+ render render_options.merge(:partial => 'partial')
215
+ end
216
+ run
217
+ received.should == chunks('[', 'a', 'b]', :end => true)
218
+ end
219
+
220
+ it "should stream :inline templates" do
221
+ render_options = @render_options
222
+ action do
223
+ render render_options.merge(:inline => "a<% flush %>b")
224
+ end
225
+ run
226
+ received.should == chunks('[', 'a', 'b]', :end => true)
227
+ end
228
+ end
229
+
230
+ describe "without a layout" do
231
+ before do
232
+ @render_options[:layout] = nil
233
+ end
234
+
235
+ it "should stream templates specified with :action" do
236
+ render_options = @render_options
237
+ action do
238
+ render render_options.merge(:action => 'action')
239
+ end
240
+ run
241
+ received.should == chunks('(', 'a', 'b)', :end => true)
242
+ end
243
+
244
+ it "should stream templates specified with :partial" do
245
+ render_options = @render_options
246
+ action do
247
+ render render_options.merge(:partial => 'partial')
248
+ end
249
+ run
250
+ received.should == chunks('a', 'b', :end => true)
251
+ end
252
+
253
+ it "should stream :inline templates" do
254
+ render_options = @render_options
255
+ action do
256
+ render render_options.merge(:inline => "a<% flush %>b")
257
+ end
258
+ run
259
+ received.should == chunks('a', 'b', :end => true)
260
+ end
261
+ end
262
+
263
+ it "should not affect the :text option" do
264
+ layout "[<%= yield %>]"
265
+ render_options = @render_options
266
+ action do
267
+ render render_options.merge(:text => 'test')
268
+ end
269
+ run
270
+ headers['Content-Type'].should == 'text/html; charset=utf-8'
271
+ received.should == 'test'
272
+ end
273
+
274
+ it "should not affect the :xml option" do
275
+ layout "[<%= yield %>]"
276
+ render_options = @render_options
277
+ action do
278
+ render render_options.merge(:xml => {:key => 'value'})
279
+ end
280
+ run
281
+ headers['Content-Type'].should == 'application/xml; charset=utf-8'
282
+ received.gsub(/\n\s*/, '').should == '<?xml version="1.0" encoding="UTF-8"?><hash><key>value</key></hash>'
283
+ end
284
+
285
+ it "should not affect the :js option" do
286
+ layout "[<%= yield %>]"
287
+ render_options = @render_options
288
+ action do
289
+ render render_options.merge(:js => "alert('hi')")
290
+ end
291
+ run
292
+ headers['Content-Type'].should == 'text/javascript; charset=utf-8'
293
+ received.gsub(/\n\s*/, '').should == "alert('hi')"
294
+ end
295
+
296
+ it "should not affect the :json option" do
297
+ layout "[<%= yield %>]"
298
+ render_options = @render_options
299
+ action do
300
+ render render_options.merge(:json => {:key => 'value'})
301
+ end
302
+ run
303
+ headers['Content-Type'].should == 'application/json; charset=utf-8'
304
+ received.should == '{"key":"value"}'
305
+ end
306
+
307
+ it "should not affect the :update option" do
308
+ layout "[<%= yield %>]"
309
+ render_options = @render_options
310
+ action do
311
+ render :update, render_options do |page|
312
+ page << "alert('hi')"
313
+ end
314
+ end
315
+ run
316
+ headers['Content-Type'].should == 'text/javascript; charset=utf-8'
317
+ received.should == "alert('hi')"
318
+ end
319
+
320
+ it "should not affect the :nothing option" do
321
+ layout "[<%= yield %>]"
322
+ render_options = @render_options
323
+ action do
324
+ render render_options.merge(:nothing => true)
325
+ end
326
+ run
327
+ headers['Content-Type'].should == 'text/html; charset=utf-8'
328
+ received.should == ' '
329
+ end
330
+
331
+ it "should set the given response status" do
332
+ layout "[<%= yield %>]"
333
+ render_options = @render_options
334
+ action do
335
+ render render_options.merge(:nothing => true, :status => 418)
336
+ end
337
+ run
338
+ status.should == 418
339
+ end
340
+ end
341
+
342
+ describe "when not streaming" do
343
+ before do
344
+ @render_options = {:stream => false}
345
+ view "(<%= render :partial => 'partial' %>)"
346
+ partial "ab"
347
+ end
348
+
349
+ describe "with a layout" do
350
+ before do
351
+ @render_options[:layout] = 'layout'
352
+ layout "[<%= yield %>]"
353
+ end
354
+
355
+ it "should not stream templates specified with :action" do
356
+ render_options = @render_options
357
+ action do
358
+ render render_options.merge(:action => 'action')
359
+ end
360
+ run
361
+ received.should == '[(ab)]'
362
+ end
363
+
364
+ it "should not stream templates specified with :partial" do
365
+ render_options = @render_options
366
+ action do
367
+ render render_options.merge(:partial => 'partial')
368
+ end
369
+ run
370
+ received.should == '[ab]'
371
+ end
372
+
373
+ it "should not stream :inline templates" do
374
+ render_options = @render_options
375
+ action do
376
+ render render_options.merge(:inline => 'ab')
377
+ end
378
+ run
379
+ received.should == '[ab]'
380
+ end
381
+ end
382
+
383
+ describe "without a layout" do
384
+ before do
385
+ @render_options[:layout] = nil
386
+ end
387
+
388
+ it "should not stream templates specified with :action" do
389
+ render_options = @render_options
390
+ action do
391
+ render render_options.merge(:action => 'action')
392
+ end
393
+ run
394
+ received.should == '(ab)'
395
+ end
396
+
397
+ it "should not stream templates specified with :partial" do
398
+ render_options = @render_options
399
+ action do
400
+ render render_options.merge(:partial => 'partial')
401
+ end
402
+ run
403
+ received.should == 'ab'
404
+ end
405
+
406
+ it "should not stream :inline templates" do
407
+ render_options = @render_options
408
+ action do
409
+ render render_options.merge(:inline => 'ab')
410
+ end
411
+ run
412
+ received.should == 'ab'
413
+ end
414
+ end
415
+
416
+ it "should not stream a given :text string" do
417
+ render_options = @render_options
418
+ action do
419
+ render render_options.merge(:text => 'ab')
420
+ end
421
+ run
422
+ received.should == 'ab'
423
+ end
424
+ end
425
+
426
+ it "should use the standard defaults when only a :stream option is given" do
427
+ template 'layouts/controller_layout', "[<%= yield %>]"
428
+ TestController.layout 'controller_layout'
429
+ view 'a'
430
+ action do
431
+ render :stream => false
432
+ end
433
+ run
434
+ received.should == '[a]'
435
+ end
436
+ end
437
+
438
+ describe "#render in the view" do
439
+ describe "when streaming" do
440
+ before do
441
+ action do
442
+ render :stream => true, :layout => 'layout'
443
+ end
444
+ layout "[<% flush %><%= yield %>]"
445
+ template 'test/_partial_layout', "{<% flush %><%= yield %>}"
446
+ end
447
+
448
+ it "should render partials with layouts correctly" do
449
+ partial 'x'
450
+ view "(<% flush %><%= render :partial => 'partial', :layout => 'partial_layout' %>)"
451
+ run
452
+ received.should == chunks('[', '(', '{', 'x})]', :end => true)
453
+ end
454
+
455
+ it "should render blocks with layouts correctly" do
456
+ template 'test/_partial_layout', "{<% flush %><%= yield %>}"
457
+ view "(<% flush %><% render :layout => 'partial_layout' do %>x<% end %>)"
458
+ run
459
+ received.should == chunks('[', '(', '{', 'x})]', :end => true)
460
+ end
461
+ end
462
+
463
+ describe "when not streaming" do
464
+ before do
465
+ action do
466
+ render :stream => false, :layout => 'layout'
467
+ end
468
+ layout "[<%= yield %>]"
469
+ template 'test/_partial_layout', "{<%= yield %>}"
470
+ end
471
+
472
+ it "should render partials with layouts correctly" do
473
+ partial 'x'
474
+ view "(<%= render :partial => 'partial', :layout => 'partial_layout' %>)"
475
+ run
476
+ received.should == '[({x})]'
477
+ end
478
+
479
+ it "should render blocks with layouts correctly" do
480
+ template 'test/_partial_layout', "{<%= yield %>}"
481
+ view "(<% render :layout => 'partial_layout' do %>x<% end %>)"
482
+ run
483
+ received.should == '[({x})]'
484
+ end
485
+ end
486
+ end
487
+
488
+ describe "#render_to_string in the controller" do
489
+ it "should not flush anything out to the client" do
490
+ TestController.stream
491
+ action do
492
+ @string = render_to_string :partial => 'partial'
493
+ received.should == ''
494
+ render :stream => true
495
+ end
496
+ layout "<%= yield %>"
497
+ view "<%= @string %>"
498
+ partial "partial"
499
+ run
500
+ received.should == chunks("partial", :end => true)
501
+ end
502
+ end
503
+
504
+ describe "#render_to_string in the view" do
505
+ it "should not flush anything out to the client" do
506
+ TestController.stream
507
+ TestController.helper_method :render_to_string
508
+ layout "<%= yield %>"
509
+ view <<-'EOS'.gsub(/^ *\|/, '')
510
+ |<% string = render_to_string :partial => 'partial' -%>
511
+ |<% received.should == '' -%>
512
+ |<%= string -%>
513
+ EOS
514
+ partial "partial"
515
+ action do
516
+ render :stream => true
517
+ end
518
+ run
519
+ received.should == chunks("partial", :end => true)
520
+ end
521
+ end
522
+
523
+ describe "initial chunk padding" do
524
+ before do
525
+ view "a<% flush %>"
526
+ action do
527
+ render :stream => true, :layout => nil
528
+ end
529
+ end
530
+
531
+ it "should extend to 255 bytes for Internet Explorer" do
532
+ run('HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows; U; MSIE 9.0; WIndows NT 9.0; en-US)')
533
+ received.should == chunks("a<!--#{'+'*247}-->", :end => true)
534
+ end
535
+
536
+ it "should extend to 2048 bytes for Chrome" do
537
+ run('HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.25 (KHTML, like Gecko) Chrome/12.0.706.0 Safari/534.25')
538
+ received.should == chunks("a<!--#{'+'*2040}-->", :end => true)
539
+ end
540
+
541
+ it "should extend to 1024 bytes for Safari" do
542
+ run('HTTP_USER_AGENT' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27')
543
+ received.should == chunks("a<!--#{'+'*1016}-->", :end => true)
544
+ end
545
+
546
+ it "should not be included for Firefox" do
547
+ run('HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64; rv:2.2a1pre) Gecko/20110324 Firefox/4.2a1pre')
548
+ received.should == chunks("a", :end => true)
549
+ end
550
+ end
551
+
552
+ describe "#when_streaming_template" do
553
+ before do
554
+ TestController.when_streaming_template { |c| c.data.order << :callback }
555
+ view "<% data.order << :rendering %>"
556
+ layout '<%= yield %>'
557
+ data.order = []
558
+ end
559
+
560
+ it "should be called when streaming" do
561
+ action do
562
+ data.order << :action
563
+ render :stream => true
564
+ end
565
+ run
566
+ data.order.should == [:action, :callback, :rendering]
567
+ end
568
+
569
+ it "should not be called when not streaming" do
570
+ action do
571
+ data.order << :action
572
+ render :stream => false
573
+ end
574
+ run
575
+ data.order.should == [:action, :rendering]
576
+ end
577
+ end
578
+
579
+ class BlackHoleSessionStore < ActionController::Session::AbstractStore
580
+ def get_session(env, sid)
581
+ ['id', {}]
582
+ end
583
+
584
+ def set_session(env, sid, data)
585
+ true
586
+ end
587
+
588
+ def destroy(env)
589
+ true
590
+ end
591
+ end
592
+
593
+ describe "#flash" do
594
+ describe "when streaming" do
595
+ it "should behave correctly when referenced in the controller" do
596
+ values = []
597
+ view ""
598
+ action do
599
+ flash[:key] = "value" if params[:set]
600
+ values << flash[:key]
601
+ render :stream => true
602
+ end
603
+ run('QUERY_STRING' => 'set=1')
604
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
605
+
606
+ run('HTTP_COOKIE' => session_cookie)
607
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
608
+
609
+ run('HTTP_COOKIE' => session_cookie)
610
+ values.should == ['value', 'value', nil]
611
+ end
612
+
613
+ it "should behave correctly when only referenced in the view" do
614
+ view "(<%= flash[:key] %>)"
615
+ action do
616
+ flash[:key] = "value" if params[:set]
617
+ render :stream => true
618
+ end
619
+ run('QUERY_STRING' => 'set=1')
620
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
621
+ received.should == chunks('(value)', :end => true)
622
+
623
+ run('HTTP_COOKIE' => session_cookie)
624
+ received.should == chunks('(value)', :end => true)
625
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
626
+
627
+ run('HTTP_COOKIE' => session_cookie)
628
+ received.should == chunks('()', :end => true)
629
+ end
630
+
631
+ it "should be frozen in the view if the session is sent with the headers" do
632
+ view "<% data.frozen = flash.frozen? %>"
633
+ action { render :stream => true }
634
+ run
635
+ data.frozen.should be_true
636
+ end
637
+
638
+ it "should not be frozen in the view if the session is not sent with the headers" do
639
+ with_attribute_value ActionController::Base, :session_store, BlackHoleSessionStore do
640
+ view "<% data.frozen = flash.frozen? %>"
641
+ action { render :stream => true }
642
+ run
643
+ data.frozen.should be_false
644
+ end
645
+ end
646
+ end
647
+
648
+ describe "when not streaming" do
649
+ it "should behave correctly when referenced in the controller" do
650
+ values = []
651
+ view ""
652
+ action do
653
+ flash[:key] = "value" if params[:set]
654
+ values << flash[:key]
655
+ render :stream => false
656
+ end
657
+ run('QUERY_STRING' => 'set=1')
658
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
659
+
660
+ run('HTTP_COOKIE' => session_cookie)
661
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
662
+
663
+ run('HTTP_COOKIE' => session_cookie)
664
+ values.should == ['value', 'value', nil]
665
+ end
666
+ end
667
+
668
+ it "should behave correctly when only referenced in the view" do
669
+ view "(<%= flash[:key] %>)"
670
+ action do
671
+ flash[:key] = "value" if params[:set]
672
+ end
673
+ run('QUERY_STRING' => 'set=1')
674
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
675
+ received.should == '(value)'
676
+
677
+ run('HTTP_COOKIE' => session_cookie)
678
+ received.should == '(value)'
679
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
680
+
681
+ run('HTTP_COOKIE' => session_cookie)
682
+ received.should == '()'
683
+ end
684
+
685
+ it "should not be frozen in the view" do
686
+ view "<% data.frozen = flash.frozen? %>"
687
+ action { render :stream => false }
688
+ run
689
+ data.frozen.should be_false
690
+ end
691
+ end
692
+
693
+ describe "#flash.now" do
694
+ describe "when streaming" do
695
+ it "should behave correctly when referenced in the controller" do
696
+ values = []
697
+ view ""
698
+ action do
699
+ flash.now[:key] = "value" if params[:set]
700
+ values << flash[:key]
701
+ render :stream => true
702
+ end
703
+ run('QUERY_STRING' => 'set=1')
704
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
705
+
706
+ run('HTTP_COOKIE' => session_cookie)
707
+ values.should == ['value', nil]
708
+ end
709
+
710
+ it "should behave correctly when only referenced in the view" do
711
+ view "(<%= flash[:key] %>)"
712
+ action do
713
+ flash.now[:key] = "value" if params[:set]
714
+ render :stream => true
715
+ end
716
+ run('QUERY_STRING' => 'set=1')
717
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
718
+ received.should == chunks('(value)', :end => true)
719
+
720
+ run('HTTP_COOKIE' => session_cookie)
721
+ received.should == chunks('()', :end => true)
722
+ end
723
+ end
724
+
725
+ describe "when not streaming" do
726
+ it "should behave correctly when referenced in the controller" do
727
+ values = []
728
+ view ""
729
+ action do
730
+ flash.now[:key] = "value" if params[:set]
731
+ values << flash[:key]
732
+ render :stream => false
733
+ end
734
+ run('QUERY_STRING' => 'set=1')
735
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
736
+
737
+ run('HTTP_COOKIE' => session_cookie)
738
+ values.should == ['value', nil]
739
+ end
740
+ end
741
+
742
+ it "should behave correctly when only referenced in the view" do
743
+ view "(<%= flash[:key] %>)"
744
+ action do
745
+ flash.now[:key] = "value" if params[:set]
746
+ end
747
+ run('QUERY_STRING' => 'set=1')
748
+ session_cookie = headers['Set-Cookie'].scan(/^(session=[^;]*)/).first.first
749
+ received.should == '(value)'
750
+
751
+ run('HTTP_COOKIE' => session_cookie)
752
+ received.should == '()'
753
+ end
754
+ end
755
+
756
+ describe "#cookies" do
757
+ describe "when streaming" do
758
+ it "should be frozen in the view" do
759
+ view "<% data.frozen = cookies.frozen? %>"
760
+ action { render :stream => true }
761
+ run
762
+ data.frozen.should be_true
763
+ end
764
+
765
+ it "should be frozen in the view irrespective of session store" do
766
+ with_attribute_value ActionController::Base, :session_store, BlackHoleSessionStore do
767
+ view "<% data.frozen = cookies.frozen? %>"
768
+ action { render :stream => true }
769
+ run
770
+ data.frozen.should be_true
771
+ end
772
+ end
773
+ end
774
+
775
+ describe "when not streaming" do
776
+ it "should not be frozen in the view" do
777
+ view "<% data.frozen = session.frozen? %>"
778
+ action { render :stream => false }
779
+ run
780
+ data.frozen.should be_false
781
+ end
782
+ end
783
+ end
784
+
785
+ describe "#session" do
786
+ describe "when streaming" do
787
+ it "should be frozen in the view if the session is sent with the headers" do
788
+ view "<% data.frozen = session.frozen? %>"
789
+ action { render :stream => true }
790
+ run
791
+ data.frozen.should be_true
792
+ end
793
+
794
+ it "should not be frozen in the view if the session is not sent with the headers" do
795
+ with_attribute_value ActionController::Base, :session_store, BlackHoleSessionStore do
796
+ view "<% data.frozen = session.frozen? %>"
797
+ action { render :stream => true }
798
+ run
799
+ data.frozen.should be_false
800
+ end
801
+ end
802
+ end
803
+
804
+ describe "when not streaming" do
805
+ it "should not be frozen in the view" do
806
+ view "<% data.frozen = session.frozen? %>"
807
+ action { render :stream => false }
808
+ run
809
+ data.frozen.should be_false
810
+ end
811
+ end
812
+ end
813
+
814
+ describe "#form_authenticity_token" do
815
+ describe "when streaming" do
816
+ it "should match what is in the session when referenced in the controller" do
817
+ view ''
818
+ value = nil
819
+ action do
820
+ value = form_authenticity_token
821
+ render :stream => true
822
+ end
823
+ run
824
+ session[:_csrf_token].should == value
825
+ end
826
+
827
+ it "should match what is in the session when only referenced in the view" do
828
+ view "<%= form_authenticity_token %>"
829
+ action do
830
+ render :stream => true
831
+ end
832
+ run
833
+ received.should == chunks(session[:_csrf_token], :end => true)
834
+ end
835
+ end
836
+
837
+ describe "when not streaming" do
838
+ it "should match what is in the session when referenced in the controller" do
839
+ view ''
840
+ value = nil
841
+ action do
842
+ value = form_authenticity_token
843
+ render :stream => false
844
+ end
845
+ run
846
+ session[:_csrf_token].should == value
847
+ end
848
+
849
+ it "should match what is in the session when only referenced in the view" do
850
+ view "<%= form_authenticity_token %>"
851
+ action do
852
+ render :stream => false
853
+ end
854
+ run
855
+ received.should == session[:_csrf_token]
856
+ end
857
+ end
858
+ end
859
+
860
+ describe "rendering" do
861
+ def render_call(layout, partial, style)
862
+ if style == :block
863
+ "<% render :layout => '#{layout}' do %><%= render :partial => '#{partial}' %><% end %>"
864
+ else
865
+ "<%= render :layout => '#{layout}', :partial => '#{partial}' %>"
866
+ end
867
+ end
868
+
869
+ describe "a partial with a layout inside another partial with a layout" do
870
+ [:block, :partial].each do |outer_style|
871
+ [:block, :partial].each do |inner_style|
872
+ it "should work when the outer partial layout is specified with a #{outer_style} and the inner one with a #{inner_style}" do
873
+ layout "layout[<% flush %><%= yield %>]"
874
+ view "view[<% flush %>#{render_call 'outer_layout', 'outer', outer_style}]"
875
+ template 'test/_outer_layout', 'outer_layout[<% flush %><%= yield %>]'
876
+ template 'test/_inner_layout', 'inner_layout[<% flush %><%= yield %>]'
877
+ template 'test/_outer', "outer[<% flush %>#{render_call 'inner_layout', 'inner', inner_style}]"
878
+ template 'test/_inner', "inner"
879
+ action do
880
+ render :layout => 'layout', :stream => true
881
+ end
882
+ run
883
+ received.should == chunks('layout[', 'view[', 'outer_layout[', 'outer[', 'inner_layout[', 'inner]]]]]', :end => true)
884
+ end
885
+ end
886
+ end
887
+ end
888
+
889
+ [:block, :partial].each do |style|
890
+ describe "a partial with a layout inside the toplevel layout" do
891
+ it "should render correctly when the partial layout is specified with a #{style}" do
892
+ layout "layout[<% flush %>#{render_call 'partial_layout', 'partial', style}<%= yield %>]"
893
+ view "view"
894
+ partial "partial"
895
+ template 'test/_partial_layout', 'partial_layout[<% flush %><%= yield %>]'
896
+ action do
897
+ render :layout => 'layout', :stream => true
898
+ end
899
+ run
900
+ received.should == chunks('layout[', 'partial_layout[', 'partial]view]', :end => true)
901
+ end
902
+ end
903
+ end
904
+
905
+ [:block, :partial].each do |outer_style|
906
+ [:block, :partial].each do |inner_style|
907
+ describe "a partial with a layout inside a partial layout" do
908
+ it "should render correctly when the outer partial layout is specified with a #{outer_style} and the inner one with a #{inner_style}" do
909
+ layout "layout[<% flush %><%= yield %>]"
910
+ view "view[<% flush %>#{render_call 'outer_layout', 'outer', outer_style}]"
911
+ template 'test/_outer_layout', "outer_layout[<% flush %>#{render_call 'inner_layout', 'inner', inner_style}<%= yield %>]"
912
+ template 'test/_outer', 'outer'
913
+ template 'test/_inner_layout', "inner_layout[<% flush %><%= yield %>]"
914
+ template 'test/_inner', 'inner'
915
+ partial "partial"
916
+ action do
917
+ render :layout => 'layout', :stream => true
918
+ end
919
+ run
920
+ received.should == chunks('layout[', 'view[', 'outer_layout[', 'inner_layout[', 'inner]outer]]]', :end => true)
921
+ end
922
+ end
923
+ end
924
+ end
925
+ end
926
+ end