template_streaming 0.0.11 → 0.1.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.
@@ -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