rubycut-sinatra-contrib 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +136 -0
  3. data/Rakefile +75 -0
  4. data/ideas.md +29 -0
  5. data/lib/sinatra/capture.rb +124 -0
  6. data/lib/sinatra/config_file.rb +167 -0
  7. data/lib/sinatra/content_for.rb +125 -0
  8. data/lib/sinatra/contrib.rb +39 -0
  9. data/lib/sinatra/contrib/all.rb +2 -0
  10. data/lib/sinatra/contrib/setup.rb +53 -0
  11. data/lib/sinatra/contrib/version.rb +17 -0
  12. data/lib/sinatra/cookies.rb +331 -0
  13. data/lib/sinatra/decompile.rb +120 -0
  14. data/lib/sinatra/engine_tracking.rb +96 -0
  15. data/lib/sinatra/extension.rb +95 -0
  16. data/lib/sinatra/json.rb +130 -0
  17. data/lib/sinatra/link_header.rb +132 -0
  18. data/lib/sinatra/multi_route.rb +87 -0
  19. data/lib/sinatra/namespace.rb +284 -0
  20. data/lib/sinatra/reloader.rb +394 -0
  21. data/lib/sinatra/respond_with.rb +249 -0
  22. data/lib/sinatra/streaming.rb +267 -0
  23. data/lib/sinatra/test_helpers.rb +87 -0
  24. data/sinatra-contrib.gemspec +127 -0
  25. data/spec/capture_spec.rb +93 -0
  26. data/spec/config_file/key_value.yml +6 -0
  27. data/spec/config_file/key_value.yml.erb +6 -0
  28. data/spec/config_file/key_value_override.yml +2 -0
  29. data/spec/config_file/missing_env.yml +4 -0
  30. data/spec/config_file/with_envs.yml +7 -0
  31. data/spec/config_file/with_nested_envs.yml +11 -0
  32. data/spec/config_file_spec.rb +63 -0
  33. data/spec/content_for/different_key.erb +1 -0
  34. data/spec/content_for/different_key.erubis +1 -0
  35. data/spec/content_for/different_key.haml +2 -0
  36. data/spec/content_for/different_key.slim +2 -0
  37. data/spec/content_for/layout.erb +1 -0
  38. data/spec/content_for/layout.erubis +1 -0
  39. data/spec/content_for/layout.haml +1 -0
  40. data/spec/content_for/layout.slim +1 -0
  41. data/spec/content_for/multiple_blocks.erb +4 -0
  42. data/spec/content_for/multiple_blocks.erubis +4 -0
  43. data/spec/content_for/multiple_blocks.haml +8 -0
  44. data/spec/content_for/multiple_blocks.slim +8 -0
  45. data/spec/content_for/multiple_yields.erb +3 -0
  46. data/spec/content_for/multiple_yields.erubis +3 -0
  47. data/spec/content_for/multiple_yields.haml +3 -0
  48. data/spec/content_for/multiple_yields.slim +3 -0
  49. data/spec/content_for/passes_values.erb +1 -0
  50. data/spec/content_for/passes_values.erubis +1 -0
  51. data/spec/content_for/passes_values.haml +1 -0
  52. data/spec/content_for/passes_values.slim +1 -0
  53. data/spec/content_for/same_key.erb +1 -0
  54. data/spec/content_for/same_key.erubis +1 -0
  55. data/spec/content_for/same_key.haml +2 -0
  56. data/spec/content_for/same_key.slim +2 -0
  57. data/spec/content_for/takes_values.erb +1 -0
  58. data/spec/content_for/takes_values.erubis +1 -0
  59. data/spec/content_for/takes_values.haml +3 -0
  60. data/spec/content_for/takes_values.slim +3 -0
  61. data/spec/content_for_spec.rb +213 -0
  62. data/spec/cookies_spec.rb +802 -0
  63. data/spec/decompile_spec.rb +44 -0
  64. data/spec/extension_spec.rb +33 -0
  65. data/spec/json_spec.rb +117 -0
  66. data/spec/link_header_spec.rb +100 -0
  67. data/spec/multi_route_spec.rb +60 -0
  68. data/spec/namespace/foo.erb +1 -0
  69. data/spec/namespace/nested/foo.erb +1 -0
  70. data/spec/namespace_spec.rb +676 -0
  71. data/spec/okjson.rb +581 -0
  72. data/spec/reloader/app.rb.erb +40 -0
  73. data/spec/reloader_spec.rb +441 -0
  74. data/spec/respond_with/bar.erb +1 -0
  75. data/spec/respond_with/bar.json.erb +1 -0
  76. data/spec/respond_with/foo.html.erb +1 -0
  77. data/spec/respond_with/not_html.sass +2 -0
  78. data/spec/respond_with_spec.rb +297 -0
  79. data/spec/spec_helper.rb +7 -0
  80. data/spec/streaming_spec.rb +436 -0
  81. metadata +313 -0
@@ -0,0 +1 @@
1
+ Girl! I wanna take you to a ... bar!
@@ -0,0 +1 @@
1
+ json!
@@ -0,0 +1 @@
1
+ Hello <%= name %>!
@@ -0,0 +1,2 @@
1
+ body
2
+ color: red
@@ -0,0 +1,297 @@
1
+ require 'backports'
2
+ require 'multi_json'
3
+
4
+ require_relative 'spec_helper'
5
+ require_relative 'okjson'
6
+
7
+ describe Sinatra::RespondWith do
8
+ def provides(*args)
9
+ @provides = args
10
+ end
11
+
12
+ def respond_app(&block)
13
+ types = @provides
14
+ mock_app do
15
+ set :app_file, __FILE__
16
+ set :views, root + '/respond_with'
17
+ register Sinatra::RespondWith
18
+ respond_to(*types) if types
19
+ class_eval(&block)
20
+ end
21
+ end
22
+
23
+ def respond_to(*args, &block)
24
+ respond_app { get('/') { respond_to(*args, &block) } }
25
+ end
26
+
27
+ def respond_with(*args, &block)
28
+ respond_app { get('/') { respond_with(*args, &block) } }
29
+ end
30
+
31
+ def req(*types)
32
+ p = types.shift if types.first.is_a? String and types.first.start_with? '/'
33
+ accept = types.map { |t| Sinatra::Base.mime_type(t).to_s }.join ','
34
+ get (p || '/'), {}, 'HTTP_ACCEPT' => accept
35
+ end
36
+
37
+ describe "Helpers#respond_to" do
38
+ it 'allows defining handlers by file extensions' do
39
+ respond_to do |format|
40
+ format.html { "html!" }
41
+ format.json { "json!" }
42
+ end
43
+
44
+ req(:html).body.should == "html!"
45
+ req(:json).body.should == "json!"
46
+ end
47
+
48
+ it 'respects quality' do
49
+ respond_to do |format|
50
+ format.html { "html!" }
51
+ format.json { "json!" }
52
+ end
53
+
54
+ req("text/html;q=0.7, application/json;q=0.3").body.should == "html!"
55
+ req("text/html;q=0.3, application/json;q=0.7").body.should == "json!"
56
+ end
57
+
58
+ it 'allows using mime types' do
59
+ respond_to do |format|
60
+ format.on('text/html') { "html!" }
61
+ format.json { "json!" }
62
+ end
63
+
64
+ req(:html).body.should == "html!"
65
+ end
66
+
67
+ it 'allows using wildcards in format matchers' do
68
+ respond_to do |format|
69
+ format.on('text/*') { "text!" }
70
+ format.json { "json!" }
71
+ end
72
+
73
+ req(:html).body.should == "text!"
74
+ end
75
+
76
+ it 'allows using catch all wildcards in format matchers' do
77
+ respond_to do |format|
78
+ format.on('*/*') { "anything!" }
79
+ format.json { "json!" }
80
+ end
81
+
82
+ req(:html).body.should == "anything!"
83
+ end
84
+
85
+ it 'prefers concret over generic' do
86
+ respond_to do |format|
87
+ format.on('text/*') { "text!" }
88
+ format.on('*/*') { "anything!" }
89
+ format.json { "json!" }
90
+ end
91
+
92
+ req(:json).body.should == "json!"
93
+ req(:html).body.should == "text!"
94
+ end
95
+
96
+ it 'does not set up default handlers' do
97
+ respond_to
98
+ req.should_not be_ok
99
+ status.should == 406
100
+ end
101
+ end
102
+
103
+ describe "Helpers#respond_with" do
104
+ describe "matching" do
105
+ it 'allows defining handlers by file extensions' do
106
+ respond_with(:ignore) do |format|
107
+ format.html { "html!" }
108
+ format.json { "json!" }
109
+ end
110
+
111
+ req(:html).body.should == "html!"
112
+ req(:json).body.should == "json!"
113
+ end
114
+
115
+ it 'respects quality' do
116
+ respond_with(:ignore) do |format|
117
+ format.html { "html!" }
118
+ format.json { "json!" }
119
+ end
120
+
121
+ req("text/html;q=0.7, application/json;q=0.3").body.should == "html!"
122
+ req("text/html;q=0.3, application/json;q=0.7").body.should == "json!"
123
+ end
124
+
125
+ it 'allows using mime types' do
126
+ respond_with(:ignore) do |format|
127
+ format.on('text/html') { "html!" }
128
+ format.json { "json!" }
129
+ end
130
+
131
+ req(:html).body.should == "html!"
132
+ end
133
+
134
+ it 'allows using wildcards in format matchers' do
135
+ respond_with(:ignore) do |format|
136
+ format.on('text/*') { "text!" }
137
+ format.json { "json!" }
138
+ end
139
+
140
+ req(:html).body.should == "text!"
141
+ end
142
+
143
+ it 'allows using catch all wildcards in format matchers' do
144
+ respond_with(:ignore) do |format|
145
+ format.on('*/*') { "anything!" }
146
+ format.json { "json!" }
147
+ end
148
+
149
+ req(:html).body.should == "anything!"
150
+ end
151
+
152
+ it 'prefers concret over generic' do
153
+ respond_with(:ignore) do |format|
154
+ format.on('text/*') { "text!" }
155
+ format.on('*/*') { "anything!" }
156
+ format.json { "json!" }
157
+ end
158
+
159
+ req(:json).body.should == "json!"
160
+ req(:html).body.should == "text!"
161
+ end
162
+ end
163
+
164
+ describe "default behavior" do
165
+ it 'converts objects to json out of the box' do
166
+ respond_with 'a' => 'b'
167
+ OkJson.decode(req(:json).body).should == {'a' => 'b'}
168
+ end
169
+
170
+ it 'handles multiple routes correctly' do
171
+ respond_app do
172
+ get('/') { respond_with 'a' => 'b' }
173
+ get('/:name') { respond_with 'a' => params[:name] }
174
+ end
175
+ OkJson.decode(req('/', :json).body).should == {'a' => 'b'}
176
+ OkJson.decode(req('/b', :json).body).should == {'a' => 'b'}
177
+ OkJson.decode(req('/c', :json).body).should == {'a' => 'c'}
178
+ end
179
+
180
+ it "calls to_EXT if available" do
181
+ respond_with Struct.new(:to_pdf).new("hello")
182
+ req(:pdf).body.should == "hello"
183
+ end
184
+
185
+ it 'results in a 406 if format cannot be produced' do
186
+ respond_with({})
187
+ req(:html).should_not be_ok
188
+ status.should == 406
189
+ end
190
+ end
191
+
192
+ describe 'templates' do
193
+ it 'looks for templates with name.target.engine' do
194
+ respond_with :foo, :name => 'World'
195
+ req(:html).should be_ok
196
+ body.should == "Hello World!"
197
+ end
198
+
199
+ it 'looks for templates with name.engine for specific engines' do
200
+ respond_with :bar
201
+ req(:html).should be_ok
202
+ body.should == "Girl! I wanna take you to a ... bar!"
203
+ end
204
+
205
+ it 'does not use name.engine for engines producing other formats' do
206
+ respond_with :not_html
207
+ req(:html).should_not be_ok
208
+ status.should == 406
209
+ body.should be_empty
210
+ end
211
+
212
+ it 'falls back to #json if no template is found' do
213
+ respond_with :foo, :name => 'World'
214
+ req(:json).should be_ok
215
+ OkJson.decode(body).should == {'name' => 'World'}
216
+ end
217
+
218
+ it 'favors templates over #json' do
219
+ respond_with :bar, :name => 'World'
220
+ req(:json).should be_ok
221
+ body.should == 'json!'
222
+ end
223
+
224
+ it 'falls back to to_EXT if no template is found' do
225
+ object = {:name => 'World'}
226
+ def object.to_pdf; "hi" end
227
+ respond_with :foo, object
228
+ req(:pdf).should be_ok
229
+ body.should == "hi"
230
+ end
231
+
232
+ it 'uses yajl for json' do
233
+ respond_with :baz
234
+ req(:json).should be_ok
235
+ body.should == "\"yajl!\""
236
+ end
237
+ end
238
+
239
+ describe 'customizing' do
240
+ it 'allows customizing' do
241
+ respond_with(:foo, :name => 'World') { |f| f.html { 'html!' }}
242
+ req(:html).should be_ok
243
+ body.should == "html!"
244
+ end
245
+
246
+ it 'falls back to default behavior if none matches' do
247
+ respond_with(:foo, :name => 'World') { |f| f.json { 'json!' }}
248
+ req(:html).should be_ok
249
+ body.should == "Hello World!"
250
+ end
251
+
252
+ it 'favors generic rule over default behavior' do
253
+ respond_with(:foo, :name => 'World') { |f| f.on('*/*') { 'generic!' }}
254
+ req(:html).should be_ok
255
+ body.should == "generic!"
256
+ end
257
+ end
258
+ end
259
+
260
+ describe :respond_to do
261
+ it 'acts as global provides condition' do
262
+ respond_app do
263
+ respond_to :json, :html
264
+ get('/a') { 'ok' }
265
+ get('/b') { 'ok' }
266
+ end
267
+
268
+ req('/b', :xml).should_not be_ok
269
+ req('/b', :html).should be_ok
270
+ end
271
+
272
+ it 'still allows provides' do
273
+ respond_app do
274
+ respond_to :json, :html
275
+ get('/a') { 'ok' }
276
+ get('/b', :provides => :json) { 'ok' }
277
+ end
278
+
279
+ req('/b', :html).should_not be_ok
280
+ req('/b', :json).should be_ok
281
+ end
282
+
283
+ it 'plays well with namespaces' do
284
+ respond_app do
285
+ register Sinatra::Namespace
286
+ namespace '/a' do
287
+ respond_to :json
288
+ get { 'json' }
289
+ end
290
+ get('/b') { 'anything' }
291
+ end
292
+
293
+ req('/a', :html).should_not be_ok
294
+ req('/b', :html).should be_ok
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,7 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ require 'sinatra/contrib'
3
+
4
+ RSpec.configure do |config|
5
+ config.expect_with :rspec, :stdlib
6
+ config.include Sinatra::TestHelpers
7
+ end
@@ -0,0 +1,436 @@
1
+ require 'backports'
2
+ require_relative 'spec_helper'
3
+
4
+ describe Sinatra::Streaming do
5
+ def stream(&block)
6
+ rack_middleware = @use
7
+ out = nil
8
+ mock_app do
9
+ rack_middleware.each { |args| use(*args) }
10
+ helpers Sinatra::Streaming
11
+ get('/') { out = stream(&block) }
12
+ end
13
+ get('/')
14
+ out
15
+ end
16
+
17
+ def use(*args)
18
+ @use << args
19
+ end
20
+
21
+ before do
22
+ @use = []
23
+ end
24
+
25
+ context 'stream test helper' do
26
+ it 'runs the given block' do
27
+ ran = false
28
+ stream { ran = true }
29
+ ran.should be_true
30
+ end
31
+
32
+ it 'returns the stream object' do
33
+ out = stream { }
34
+ out.should be_a(Sinatra::Helpers::Stream)
35
+ end
36
+
37
+ it 'fires a request against that stream' do
38
+ stream { |out| out << "Hello World!" }
39
+ last_response.should be_ok
40
+ body.should be == "Hello World!"
41
+ end
42
+
43
+ it 'passes the stream object to the block' do
44
+ passed = nil
45
+ returned = stream { |out| passed = out }
46
+ passed.should be == returned
47
+ end
48
+ end
49
+
50
+ context Sinatra::Streaming::Stream do
51
+ it 'should extend the stream object' do
52
+ out = stream { }
53
+ out.should be_a(Sinatra::Streaming::Stream)
54
+ end
55
+
56
+ it 'should not extend stream objects of other apps' do
57
+ out = nil
58
+ mock_app { get('/') { out = stream { }}}
59
+ get('/')
60
+ out.should be_a(Sinatra::Helpers::Stream)
61
+ out.should_not be_a(Sinatra::Streaming::Stream)
62
+ end
63
+ end
64
+
65
+ context EventMachine::Deferrable do
66
+ it 'allows attaching more than one callback' do
67
+ a = b = false
68
+ stream do |out|
69
+ out.callback { a = true }
70
+ out.callback { b = true }
71
+ end
72
+ a.should be_true
73
+ b.should be_true
74
+ end
75
+
76
+ it 'triggers callbacks after streaming' do
77
+ triggered = false
78
+ stream do |out|
79
+ out.callback { triggered = true }
80
+ triggered.should be_false
81
+ end
82
+ triggered.should be_true
83
+ end
84
+ end
85
+
86
+ context 'app' do
87
+ it 'is the app instance the stream was created from' do
88
+ out = stream { }
89
+ out.app.should be_a(Sinatra::Base)
90
+ end
91
+ end
92
+
93
+ context 'lineno' do
94
+ it 'defaults to 0' do
95
+ stream { }.lineno.should be == 0
96
+ end
97
+
98
+ it 'does not increase on write' do
99
+ stream do |out|
100
+ out << "many\nlines\n"
101
+ out.lineno.should be == 0
102
+ end
103
+ end
104
+
105
+ it 'is writable' do
106
+ out = stream { }
107
+ out.lineno = 10
108
+ out.lineno.should be == 10
109
+ end
110
+ end
111
+
112
+ context 'pos' do
113
+ it 'defaults to 0' do
114
+ stream { }.pos.should be == 0
115
+ end
116
+
117
+ it 'increases when writing data' do
118
+ stream do |out|
119
+ out.pos.should be == 0
120
+ out << 'hi'
121
+ out.pos.should be == 2
122
+ end
123
+ end
124
+
125
+ it 'is writable' do
126
+ out = stream { }
127
+ out.pos = 10
128
+ out.pos.should be == 10
129
+ end
130
+
131
+ it 'aliased to #tell' do
132
+ out = stream { }
133
+ out.tell.should be == 0
134
+ out.pos = 10
135
+ out.tell.should be == 10
136
+ end
137
+ end
138
+
139
+ context 'closed' do
140
+ it 'returns false while streaming' do
141
+ stream { |out| out.should_not be_closed }
142
+ end
143
+
144
+ it 'returns true after streaming' do
145
+ stream {}.should be_closed
146
+ end
147
+ end
148
+
149
+ context 'map!' do
150
+ it 'applies transformations later' do
151
+ stream do |out|
152
+ out.map! { |s| s.upcase }
153
+ out << 'ok'
154
+ end
155
+ body.should be == "OK"
156
+ end
157
+
158
+ it 'is chainable' do
159
+ stream do |out|
160
+ out.map! { |s| s.upcase }
161
+ out.map! { |s| s.reverse }
162
+ out << 'ok'
163
+ end
164
+ body.should be == "KO"
165
+ end
166
+
167
+ it 'works with middleware' do
168
+ middleware = Class.new do
169
+ def initialize(app) @app = app end
170
+ def call(env)
171
+ status, headers, body = @app.call(env)
172
+ body.map! { |s| s.upcase }
173
+ [status, headers, body]
174
+ end
175
+ end
176
+
177
+ use middleware
178
+ stream { |out| out << "ok" }
179
+ body.should be == "OK"
180
+ end
181
+
182
+ it 'modifies each value separately' do
183
+ stream do |out|
184
+ out.map! { |s| s.reverse }
185
+ out << "ab" << "cd"
186
+ end
187
+ body.should be == "badc"
188
+ end
189
+ end
190
+
191
+ context 'map' do
192
+ it 'works with middleware' do
193
+ middleware = Class.new do
194
+ def initialize(app) @app = app end
195
+ def call(env)
196
+ status, headers, body = @app.call(env)
197
+ [status, headers, body.map(&:upcase)]
198
+ end
199
+ end
200
+
201
+ use middleware
202
+ stream { |out| out << "ok" }
203
+ body.should be == "OK"
204
+ end
205
+
206
+ it 'is chainable' do
207
+ middleware = Class.new do
208
+ def initialize(app) @app = app end
209
+ def call(env)
210
+ status, headers, body = @app.call(env)
211
+ [status, headers, body.map(&:upcase).map(&:reverse)]
212
+ end
213
+ end
214
+
215
+ use middleware
216
+ stream { |out| out << "ok" }
217
+ body.should be == "KO"
218
+ end
219
+
220
+ it 'can be written as each.map' do
221
+ middleware = Class.new do
222
+ def initialize(app) @app = app end
223
+ def call(env)
224
+ status, headers, body = @app.call(env)
225
+ [status, headers, body.each.map(&:upcase)]
226
+ end
227
+ end
228
+
229
+ use middleware
230
+ stream { |out| out << "ok" }
231
+ body.should be == "OK"
232
+ end
233
+
234
+ it 'does not modify the original body' do
235
+ stream do |out|
236
+ out.map { |s| s.reverse }
237
+ out << 'ok'
238
+ end
239
+ body.should be == 'ok'
240
+ end
241
+ end
242
+
243
+ context 'write' do
244
+ it 'writes to the stream' do
245
+ stream { |out| out.write 'hi' }
246
+ body.should be == 'hi'
247
+ end
248
+
249
+ it 'returns the number of bytes' do
250
+ stream do |out|
251
+ out.write('hi').should be == 2
252
+ out.write('hello').should be == 5
253
+ end
254
+ end
255
+
256
+ it 'accepts non-string objects' do
257
+ stream do |out|
258
+ out.write(12).should be == 2
259
+ end
260
+ end
261
+
262
+ it 'should be aliased to syswrite' do
263
+ stream { |out| out.syswrite('hi').should be == 2 }
264
+ body.should be == 'hi'
265
+ end
266
+
267
+ it 'should be aliased to write_nonblock' do
268
+ stream { |out| out.write_nonblock('hi').should be == 2 }
269
+ body.should be == 'hi'
270
+ end
271
+ end
272
+
273
+ context 'print' do
274
+ it 'writes to the stream' do
275
+ stream { |out| out.print('hi') }
276
+ body.should be == 'hi'
277
+ end
278
+
279
+ it 'accepts multiple arguments' do
280
+ stream { |out| out.print(1, 2, 3, 4) }
281
+ body.should be == '1234'
282
+ end
283
+
284
+ it 'returns nil' do
285
+ stream { |out| out.print('hi').should be_nil }
286
+ end
287
+ end
288
+
289
+ context 'printf' do
290
+ it 'writes to the stream' do
291
+ stream { |out| out.printf('hi') }
292
+ body.should be == 'hi'
293
+ end
294
+
295
+ it 'interpolates the format string' do
296
+ stream { |out| out.printf("%s: %d", "answer", 42) }
297
+ body.should be == 'answer: 42'
298
+ end
299
+
300
+ it 'returns nil' do
301
+ stream { |out| out.printf('hi').should be_nil }
302
+ end
303
+ end
304
+
305
+ context 'putc' do
306
+ it 'writes the first character of a string' do
307
+ stream { |out| out.putc('hi') }
308
+ body.should be == 'h'
309
+ end
310
+
311
+ it 'writes the character corresponding to an integer' do
312
+ stream { |out| out.putc(42) }
313
+ body.should be == '*'
314
+ end
315
+
316
+ it 'returns nil' do
317
+ stream { |out| out.putc('hi').should be_nil }
318
+ end
319
+ end
320
+
321
+ context 'puts' do
322
+ it 'writes to the stream' do
323
+ stream { |out| out.puts('hi') }
324
+ body.should be == "hi\n"
325
+ end
326
+
327
+ it 'accepts multiple arguments' do
328
+ stream { |out| out.puts(1, 2, 3, 4) }
329
+ body.should be == "1\n2\n3\n4\n"
330
+ end
331
+
332
+ it 'returns nil' do
333
+ stream { |out| out.puts('hi').should be_nil }
334
+ end
335
+ end
336
+
337
+ context 'close' do
338
+ it 'sets #closed? to true' do
339
+ stream do |out|
340
+ out.close
341
+ out.should be_closed
342
+ end
343
+ end
344
+
345
+ it 'sets #closed_write? to true' do
346
+ stream do |out|
347
+ out.should_not be_closed_write
348
+ out.close
349
+ out.should be_closed_write
350
+ end
351
+ end
352
+
353
+ it 'fires callbacks' do
354
+ stream do |out|
355
+ fired = false
356
+ out.callback { fired = true }
357
+ out.close
358
+ fired.should be_true
359
+ end
360
+ end
361
+
362
+ it 'prevents from further writing' do
363
+ stream do |out|
364
+ out.close
365
+ expect { out << 'hi' }.to raise_error(IOError, 'not opened for writing')
366
+ end
367
+ end
368
+ end
369
+
370
+ context 'close_read' do
371
+ it 'raises the appropriate exception' do
372
+ expect { stream { |out| out.close_read }}.
373
+ to raise_error(IOError, "closing non-duplex IO for reading")
374
+ end
375
+ end
376
+
377
+ context 'closed_read?' do
378
+ it('returns true') { stream { |out| out.should be_closed_read }}
379
+ end
380
+
381
+ context 'rewind' do
382
+ it 'resets pos' do
383
+ stream do |out|
384
+ out << 'hi'
385
+ out.rewind
386
+ out.pos.should be == 0
387
+ end
388
+ end
389
+
390
+ it 'resets lineno' do
391
+ stream do |out|
392
+ out.lineno = 10
393
+ out.rewind
394
+ out.lineno.should be == 0
395
+ end
396
+ end
397
+ end
398
+
399
+ raises = %w[
400
+ bytes eof? eof getbyte getc gets read read_nonblock readbyte readchar
401
+ readline readlines readpartial sysread ungetbyte ungetc
402
+ ]
403
+
404
+ enum = %w[chars each_line each_byte each_char lines]
405
+ dummies = %w[flush fsync internal_encoding pid]
406
+
407
+ raises.each do |method|
408
+ context method do
409
+ it 'raises the appropriate exception' do
410
+ expect { stream { |out| out.public_send(method) }}.
411
+ to raise_error(IOError, "not opened for reading")
412
+ end
413
+ end
414
+ end
415
+
416
+ enum.each do |method|
417
+ context method do
418
+ it 'creates an Enumerator' do
419
+ stream { |out| out.public_send(method).should be_a(Enumerator) }
420
+ end
421
+
422
+ it 'calling each raises the appropriate exception' do
423
+ expect { stream { |out| out.public_send(method).each { }}}.
424
+ to raise_error(IOError, "not opened for reading")
425
+ end
426
+ end
427
+ end
428
+
429
+ dummies.each do |method|
430
+ context method do
431
+ it 'returns nil' do
432
+ stream { |out| out.public_send(method).should be_nil }
433
+ end
434
+ end
435
+ end
436
+ end