wycats-merb-core 0.9.8 → 0.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. data/CHANGELOG +136 -2
  2. data/CONTRIBUTORS +6 -0
  3. data/PUBLIC_CHANGELOG +15 -0
  4. data/Rakefile +12 -14
  5. data/lib/merb-core.rb +82 -43
  6. data/lib/merb-core/bootloader.rb +268 -60
  7. data/lib/merb-core/config.rb +119 -34
  8. data/lib/merb-core/controller/abstract_controller.rb +58 -18
  9. data/lib/merb-core/controller/exceptions.rb +2 -15
  10. data/lib/merb-core/controller/merb_controller.rb +28 -1
  11. data/lib/merb-core/controller/mime.rb +4 -0
  12. data/lib/merb-core/controller/mixins/controller.rb +14 -17
  13. data/lib/merb-core/controller/mixins/render.rb +23 -28
  14. data/lib/merb-core/controller/mixins/responder.rb +0 -1
  15. data/lib/merb-core/controller/template.rb +44 -20
  16. data/lib/merb-core/core_ext/kernel.rb +8 -3
  17. data/lib/merb-core/dispatch/default_exception/default_exception.rb +1 -1
  18. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +3 -1
  19. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +71 -67
  20. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +6 -2
  21. data/lib/merb-core/dispatch/dispatcher.rb +5 -9
  22. data/lib/merb-core/dispatch/request.rb +46 -57
  23. data/lib/merb-core/dispatch/router.rb +83 -6
  24. data/lib/merb-core/dispatch/router/behavior.rb +87 -27
  25. data/lib/merb-core/dispatch/router/resources.rb +281 -167
  26. data/lib/merb-core/dispatch/router/route.rb +141 -27
  27. data/lib/merb-core/logger.rb +213 -202
  28. data/lib/merb-core/rack.rb +3 -1
  29. data/lib/merb-core/rack/adapter.rb +7 -4
  30. data/lib/merb-core/rack/adapter/ebb.rb +12 -13
  31. data/lib/merb-core/rack/adapter/evented_mongrel.rb +2 -15
  32. data/lib/merb-core/rack/adapter/irb.rb +3 -2
  33. data/lib/merb-core/rack/adapter/mongrel.rb +22 -15
  34. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +4 -16
  35. data/lib/merb-core/rack/adapter/thin.rb +21 -22
  36. data/lib/merb-core/rack/adapter/thin_turbo.rb +4 -11
  37. data/lib/merb-core/rack/adapter/webrick.rb +54 -18
  38. data/lib/merb-core/rack/handler/mongrel.rb +12 -13
  39. data/lib/merb-core/rack/middleware/csrf.rb +1 -1
  40. data/lib/merb-core/server.rb +135 -98
  41. data/lib/merb-core/tasks/gem_management.rb +50 -12
  42. data/lib/merb-core/tasks/merb.rb +1 -0
  43. data/lib/merb-core/tasks/merb_rake_helper.rb +9 -38
  44. data/lib/merb-core/tasks/stats.rake +2 -2
  45. data/lib/merb-core/test.rb +9 -3
  46. data/lib/merb-core/test/helpers.rb +1 -0
  47. data/lib/merb-core/test/helpers/multipart_request_helper.rb +3 -2
  48. data/lib/merb-core/test/helpers/request_helper.rb +40 -372
  49. data/lib/merb-core/test/helpers/route_helper.rb +15 -7
  50. data/lib/merb-core/test/matchers.rb +1 -0
  51. data/lib/merb-core/test/matchers/controller_matchers.rb +4 -247
  52. data/lib/merb-core/test/matchers/view_matchers.rb +22 -4
  53. data/lib/merb-core/test/run_specs.rb +117 -25
  54. data/lib/merb-core/version.rb +1 -1
  55. metadata +1 -1
  56. data/lib/merb-core/vendor/facets.rb +0 -2
  57. data/lib/merb-core/vendor/facets/dictionary.rb +0 -433
  58. data/lib/merb-core/vendor/facets/inflect.rb +0 -342
@@ -14,15 +14,18 @@ module Merb
14
14
  :port => "4000",
15
15
  :adapter => "runner",
16
16
  :reload_classes => true,
17
+ :fork_for_class_load => !RUBY_PLATFORM.in?("windows", "java"),
17
18
  :environment => "development",
18
19
  :merb_root => Dir.pwd,
19
20
  :use_mutex => true,
20
21
  :log_delimiter => " ~ ",
21
22
  :log_auto_flush => false,
22
23
  :log_level => :info,
24
+ :log_stream => STDOUT,
23
25
  :disabled_components => [],
24
26
  :deferred_actions => [],
25
- :verbose => false
27
+ :verbose => false,
28
+ :name => "merb"
26
29
  }
27
30
  end
28
31
 
@@ -34,6 +37,7 @@ module Merb
34
37
  # ==== Examples
35
38
  # Merb::Config.use do |config|
36
39
  # config[:exception_details] = false
40
+ # config[:log_stream] = STDOUT
37
41
  # end
38
42
  def use
39
43
  @configuration ||= {}
@@ -55,14 +59,14 @@ module Merb
55
59
  # ==== Returns
56
60
  # Object:: The value of the configuration parameter.
57
61
  def [](key)
58
- (@configuration||={})[key]
62
+ (@configuration ||= setup)[key]
59
63
  end
60
64
 
61
65
  # ==== Parameters
62
66
  # key<Object>:: The key to set the parameter for.
63
67
  # val<Object>:: The value of the parameter.
64
- def []=(key,val)
65
- @configuration[key] = val
68
+ def []=(key, val)
69
+ (@configuration ||= setup)[key] = val
66
70
  end
67
71
 
68
72
  # ==== Parameters
@@ -102,6 +106,12 @@ module Merb
102
106
  # Configuration settings to use. These are merged with the defaults.
103
107
  def setup(settings = {})
104
108
  @configuration = defaults.merge(settings)
109
+
110
+ unless @configuration[:reload_classes]
111
+ @configuration[:fork_for_class_load] = false
112
+ end
113
+
114
+ @configuration
105
115
  end
106
116
 
107
117
  # Parses the command line arguments and stores them in the config.
@@ -122,99 +132,166 @@ module Merb
122
132
 
123
133
  opts.banner = "Usage: merb [uGdcIpPhmailLerkKX] [argument]"
124
134
  opts.define_head "Merb. Pocket rocket web framework"
125
- opts.separator '*'*80
126
- opts.separator 'If no flags are given, Merb starts in the foreground on port 4000.'
127
- opts.separator '*'*80
128
-
129
- opts.on("-u", "--user USER", "This flag is for having merb run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect.") do |user|
135
+ opts.separator '*' * 80
136
+ opts.separator "If no flags are given, Merb starts in the " \
137
+ "foreground on port 4000."
138
+ opts.separator '*' * 80
139
+
140
+ opts.on("-u", "--user USER", "This flag is for having merb run " \
141
+ "as a user other than the one currently logged in. Note: " \
142
+ "if you set this you must also provide a --group option " \
143
+ "for it to take effect.") do |user|
130
144
  options[:user] = user
131
145
  end
132
146
 
133
- opts.on("-G", "--group GROUP", "This flag is for having merb run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect.") do |group|
147
+ opts.on("-G", "--group GROUP", "This flag is for having merb run " \
148
+ "as a group other than the one currently logged in. Note: " \
149
+ "if you set this you must also provide a --user option " \
150
+ "for it to take effect.") do |group|
134
151
  options[:group] = group
135
152
  end
136
153
 
137
- opts.on("-d", "--daemonize", "This will run a single merb in the background.") do |daemon|
154
+ opts.on("-d", "--daemonize", "This will run a single merb in the " \
155
+ "background.") do |daemon|
138
156
  options[:daemonize] = true
139
157
  end
158
+
159
+ opts.on("-N", "--no-daemonize", "This will allow you to run a " \
160
+ "cluster in console mode") do |no_daemon|
161
+ options[:daemonize] = false
162
+ end
140
163
 
141
- opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run.") do |nodes|
164
+ opts.on("-c", "--cluster-nodes NUM_MERBS", Integer,
165
+ "Number of merb daemons to run.") do |nodes|
166
+ options[:daemonize] = true unless options.key?(:daemonize)
142
167
  options[:cluster] = nodes
143
168
  end
144
169
 
145
- opts.on("-I", "--init-file FILE", "File to use for initialization on load, defaults to config/init.rb") do |init_file|
170
+ opts.on("-I", "--init-file FILE", "File to use for initialization " \
171
+ "on load, defaults to config/init.rb") do |init_file|
146
172
  options[:init_file] = init_file
147
173
  end
148
174
 
149
- opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port|
175
+ opts.on("-p", "--port PORTNUM", Integer, "Port to run merb on, " \
176
+ "defaults to 4000.") do |port|
150
177
  options[:port] = port
151
178
  end
152
179
 
153
- opts.on("-o", "--socket-file FILE", "Socket file to run merb on, defaults to [Merb.root]/log/merb.sock") do |port|
180
+ opts.on("-o", "--socket-file FILE", "Socket file to run merb on, " \
181
+ "defaults to [Merb.root]/log/merb.sock. This is for " \
182
+ "web servers, like thin, that use sockets." \
183
+ "Specify this *only* if you *must*.") do |port|
154
184
  options[:socket_file] = port
155
185
  end
156
186
 
157
- opts.on("-s", "--socket SOCKNUM", "Socket number to run merb on, defaults to 0.") do |port|
187
+ opts.on("-s", "--socket SOCKNUM", Integer, "Socket number to run " \
188
+ "merb on, defaults to 0.") do |port|
158
189
  options[:socket] = port
159
190
  end
160
191
 
161
- opts.on("-P", "--pid PIDFILE", "PID file, defaults to [Merb.root]/log/merb.[port_number].pid") do |pid_file|
192
+ opts.on("-n", "--name NAME", String, "Set the name of the application. "\
193
+ "This is used in the process title and log file names.") do |name|
194
+ options[:name] = name
195
+ end
196
+
197
+ opts.on("-P", "--pid PIDFILE", "PID file, defaults to " \
198
+ "[Merb.root]/log/merb.main.pid for the master process and" \
199
+ "[Merb.root]/log/merb.[port number].pid for worker " \
200
+ "processes. For clusters, use %s to specify where " \
201
+ "in the file merb should place the port number. For " \
202
+ "instance: -P myapp.%s.pid") do |pid_file|
162
203
  options[:pid_file] = pid_file
163
204
  end
164
205
 
165
- opts.on("-h", "--host HOSTNAME", "Host to bind to (default is 0.0.0.0).") do |host|
206
+ opts.on("-h", "--host HOSTNAME", "Host to bind to " \
207
+ "(default is 0.0.0.0).") do |host|
166
208
  options[:host] = host
167
209
  end
168
210
 
169
- opts.on("-m", "--merb-root /path/to/approot", "The path to the Merb.root for the app you want to run (default is current working dir).") do |root|
211
+ opts.on("-m", "--merb-root /path/to/approot", "The path to the " \
212
+ "Merb.root for the app you want to run " \
213
+ "(default is current working directory).") do |root|
170
214
  options[:merb_root] = File.expand_path(root)
171
215
  end
172
216
 
173
- opts.on("-a", "--adapter mongrel", "The rack adapter to use to run merb[mongrel, emongrel, thin, ebb, fastcgi, webrick, runner, irb]") do |adapter|
174
- options[:adapter] = adapter
217
+ adapters = [:mongrel, :emongrel, :thin, :ebb, :fastcgi, :webrick]
218
+
219
+ opts.on("-a", "--adapter ADAPTER",
220
+ "The rack adapter to use to run merb (default is mongrel)" \
221
+ "[#{adapters.join(', ')}]") do |adapter|
222
+ options[:adapter] ||= adapter
175
223
  end
176
224
 
177
- opts.on("-R", "--rackup FILE", "Load an alternate Rack config file (default is config/rack.rb)") do |rackup|
225
+ opts.on("-R", "--rackup FILE", "Load an alternate Rack config " \
226
+ "file (default is config/rack.rb)") do |rackup|
178
227
  options[:rackup] = rackup
179
228
  end
180
229
 
181
- opts.on("-i", "--irb-console", "This flag will start merb in irb console mode. All your models and other classes will be available for you in an irb session.") do |console|
230
+ opts.on("-i", "--irb-console", "This flag will start merb in " \
231
+ "irb console mode. All your models and other classes will " \
232
+ "be available for you in an irb session.") do |console|
182
233
  options[:adapter] = 'irb'
183
234
  end
184
235
 
185
- opts.on("-S", "--sandbox", "This flag will enable a sandboxed irb console. If your ORM supports transactions, all edits will be rolled back on exit.") do |sandbox|
236
+ opts.on("-S", "--sandbox", "This flag will enable a sandboxed irb " \
237
+ "console. If your ORM supports transactions, all edits will " \
238
+ "be rolled back on exit.") do |sandbox|
186
239
  options[:sandbox] = true
187
240
  end
188
241
 
189
- opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: debug < info < warn < error < fatal") do |log_level|
242
+ opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of " \
243
+ "these options: debug < info < warn < error < " \
244
+ "fatal (default is info)") do |log_level|
190
245
  options[:log_level] = log_level.to_sym
246
+ options[:force_logging] = true
191
247
  end
192
248
 
193
- opts.on("-L", "--log LOGFILE", "A string representing the logfile to use.") do |log_file|
249
+ opts.on("-L", "--log LOGFILE", "A string representing the logfile to " \
250
+ "use. Defaults to [Merb.root]/log/merb.[main].log for the " \
251
+ "master process and [Merb.root]/log/merb[port number].log" \
252
+ "for worker processes") do |log_file|
194
253
  options[:log_file] = log_file
254
+ options[:force_logging] = true
195
255
  end
196
256
 
197
- opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env|
257
+ opts.on("-e", "--environment STRING", "Environment to run Merb " \
258
+ "under [development, production, testing] " \
259
+ "(default is development)") do |env|
198
260
  options[:environment] = env
199
261
  end
200
262
 
201
263
  opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]",
202
- "Command-line option to run scripts and/or code in the merb app.") do |code_or_file|
264
+ "Command-line option to run scripts and/or code in the " \
265
+ "merb app.") do |code_or_file|
203
266
  options[:runner_code] = code_or_file
204
267
  options[:adapter] = 'runner'
205
268
  end
206
269
 
207
- opts.on("-K", "--graceful PORT or all", "Gracefully kill one merb proceses by port number. Use merb -K all to gracefully kill all merbs.") do |ports|
270
+ opts.on("-K", "--graceful PORT or all", "Gracefully kill one " \
271
+ "merb proceses by port number. Use merb -K all to " \
272
+ "gracefully kill all merbs.") do |ports|
208
273
  options[:action] = :kill
274
+ ports = "main" if ports == "all"
209
275
  options[:port] = ports
210
276
  end
211
277
 
212
- opts.on("-k", "--kill PORT or all", "Kill one merb proceses by port number. Use merb -k all to kill all merbs.") do |port|
278
+ opts.on("-k", "--kill PORT", "Force kill one merb worker " \
279
+ "by port number. This will cause the worker to" \
280
+ "be respawned. If you want to kill ") do |port|
213
281
  options[:action] = :kill_9
282
+ port = "main" if port == "all"
214
283
  options[:port] = port
215
284
  end
285
+
286
+ opts.on("--fast-deploy", "Reload the code, but not your" \
287
+ "init.rb or gems") do
288
+ options[:action] = :fast_deploy
289
+ end
216
290
 
217
- opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
291
+ # @todo Do we really need this flag? It seems unlikely to want to
292
+ # change the mutex from the command-line.
293
+ opts.on("-X", "--mutex on/off", "This flag is for turning the " \
294
+ "mutex lock on and off.") do |mutex|
218
295
  if mutex == "off"
219
296
  options[:use_mutex] = false
220
297
  else
@@ -226,10 +303,13 @@ module Merb
226
303
  begin
227
304
  require "ruby-debug"
228
305
  Debugger.start
229
- Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
306
+ if Debugger.respond_to?(:settings)
307
+ Debugger.settings[:autoeval] = true
308
+ end
230
309
  puts "Debugger enabled"
231
310
  rescue LoadError
232
- puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
311
+ puts "You need to install ruby-debug to run the server in " \
312
+ "debugging mode. With gems, use `gem install ruby-debug'"
233
313
  exit
234
314
  end
235
315
  end
@@ -249,7 +329,11 @@ module Merb
249
329
  end
250
330
 
251
331
  # Parse what we have on the command line
252
- opts.parse!(argv)
332
+ begin
333
+ opts.parse!(argv)
334
+ rescue OptionParser::InvalidOption => e
335
+ Merb.fatal! e.message, e
336
+ end
253
337
  Merb::Config.setup(options)
254
338
  end
255
339
 
@@ -266,6 +350,7 @@ module Merb
266
350
  # Merb::Config.configure do
267
351
  # environment "development"
268
352
  # log_level "debug"
353
+ # log_file Merb.root / "log" / "special.log"
269
354
  # end
270
355
  def configure(&block)
271
356
  ConfigBlock.new(self, &block) if block_given?
@@ -227,7 +227,6 @@ class Merb::AbstractController
227
227
  def initialize(*args)
228
228
  @_benchmarks = {}
229
229
  @_caught_content = {}
230
- @_template_stack = []
231
230
  end
232
231
 
233
232
  # This will dispatch the request, calling internal before/after dispatch_callbacks
@@ -430,7 +429,8 @@ class Merb::AbstractController
430
429
  # ====
431
430
  # TODO: Update this documentation
432
431
  def url(name, *args)
433
- request.generate_url(name, *args)
432
+ args << {}
433
+ Merb::Router.url(name, *args)
434
434
  end
435
435
 
436
436
  alias_method :relative_url, :url
@@ -451,20 +451,71 @@ class Merb::AbstractController
451
451
  # ==== Alternatives
452
452
  # If a hash is used as the first argument, a default route will be
453
453
  # generated based on it and rparams.
454
- def absolute_url(name, rparams={})
455
- request.generate_absolute_url(name,rparams)
454
+ def absolute_url(name, *args)
455
+ # FIXME: arrgh, why request.protocol returns http://?
456
+ # :// is not part of protocol name
457
+ options = extract_options_from_args!(args) || {}
458
+ protocol = options.delete(:protocol)
459
+ host = options.delete(:host)
460
+
461
+ raise ArgumentError, "The :protocol option must be specified" unless protocol
462
+ raise ArgumentError, "The :host option must be specified" unless host
463
+
464
+ args << options
465
+
466
+ protocol + "://" + host + url(name, *args)
467
+ end
468
+
469
+ # Generates a URL for a single or nested resource.
470
+ #
471
+ # ==== Parameters
472
+ # resources<Symbol,Object>:: The resources for which the URL
473
+ # should be generated. These resources should be specified
474
+ # in the router.rb file using #resources and #resource.
475
+ #
476
+ # options<Hash>:: Any extra parameters that are needed to
477
+ # generate the URL.
478
+ #
479
+ # ==== Returns
480
+ # String:: The generated URL.
481
+ #
482
+ # ==== Examples
483
+ #
484
+ # Merb::Router.prepare do
485
+ # resources :users do
486
+ # resources :comments
487
+ # end
488
+ # end
489
+ #
490
+ # resource(:users) # => /users
491
+ # resource(@user) # => /users/10
492
+ # resource(@user, :comments) # => /users/10/comments
493
+ # resource(@user, @comment) # => /users/10/comments/15
494
+ # resource(:users, :new) # => /users/new
495
+ # resource(:@user, :edit) # => /users/10/edit
496
+ #
497
+ def resource(*args)
498
+ args << params
499
+ Merb::Router.resource(*args)
456
500
  end
457
501
 
458
502
  # Calls the capture method for the selected template engine.
459
503
  #
460
504
  # ==== Parameters
461
505
  # *args:: Arguments to pass to the block.
462
- # &block:: The template block to call.
506
+ # &block:: The block to call.
463
507
  #
464
508
  # ==== Returns
465
- # String:: The output of the block.
509
+ # String:: The output of a template block or the return value of a non-template block converted to a string.
466
510
  def capture(*args, &block)
467
- send("capture_#{@_engine}", *args, &block)
511
+ ret = nil
512
+
513
+ captured = send("capture_#{@_engine}", *args) do |*args|
514
+ ret = yield *args
515
+ end
516
+
517
+ # return captured value only if it is not empty
518
+ captured.empty? ? ret.to_s : captured
468
519
  end
469
520
 
470
521
  # Calls the concatenate method for the selected template engine.
@@ -554,15 +605,4 @@ class Merb::AbstractController
554
605
  opts[:exclude] = Array(opts[:exclude]).map {|x| x.to_s} if opts[:exclude]
555
606
  return opts
556
607
  end
557
-
558
- # Attempts to return the partial local variable corresponding to sym.
559
- #
560
- # ==== Paramteres
561
- # sym<Symbol>:: Method name.
562
- # *arg:: Arguments to pass to the method.
563
- # &blk:: A block to pass to the method.
564
- def method_missing(sym, *args, &blk)
565
- return @_merb_partial_locals[sym] if @_merb_partial_locals && @_merb_partial_locals.key?(sym)
566
- super
567
- end
568
608
  end
@@ -278,6 +278,8 @@ module Merb
278
278
 
279
279
  class ServerError < Merb::ControllerExceptions::Base; end
280
280
 
281
+ class InternalServerError < Merb::ControllerExceptions::ServerError; self.status = 500; end
282
+
281
283
  class NotImplemented < Merb::ControllerExceptions::ServerError; self.status = 501; end
282
284
 
283
285
  class BadGateway < Merb::ControllerExceptions::ServerError; self.status = 502; end
@@ -287,21 +289,6 @@ module Merb
287
289
  class GatewayTimeout < Merb::ControllerExceptions::ServerError; self.status = 504; end
288
290
 
289
291
  class HTTPVersionNotSupported < Merb::ControllerExceptions::ServerError; self.status = 505; end
290
-
291
- class InternalServerError < Merb::ControllerExceptions::ServerError #:doc:
292
- self.status = 500;
293
- def initialize(exception = nil)
294
- @exception = exception
295
- end
296
-
297
- def backtrace
298
- @exception ? @exception.backtrace : backtrace
299
- end
300
-
301
- def message
302
- @exception ? @exception.message : message
303
- end
304
- end
305
292
  end
306
293
 
307
294
  # Required to show exceptions in the log file
@@ -218,6 +218,33 @@ class Merb::Controller < Merb::AbstractController
218
218
  # ==== Returns
219
219
  # Hash:: The parameters from the request object
220
220
  def params() request.params end
221
+
222
+ # ==== Parameters
223
+ # name<~to_sym, Hash>:: The name of the URL to generate.
224
+ # rparams<Hash>:: Parameters for the route generation.
225
+ #
226
+ # ==== Returns
227
+ # String:: The generated URL.
228
+ #
229
+ # ==== Alternatives
230
+ # If a hash is used as the first argument, a default route will be
231
+ # generated based on it and rparams.
232
+ # ====
233
+ # TODO: Update this documentation
234
+ def url(name, *args)
235
+ args << params
236
+ Merb::Router.url(name, *args)
237
+ end
238
+
239
+ alias_method :relative_url, :url
240
+
241
+ def absolute_url(*args)
242
+ options = extract_options_from_args!(args) || {}
243
+ options[:protocol] ||= request.protocol
244
+ options[:host] ||= request.host
245
+ args << options
246
+ super(args.first, *args[1..-1])
247
+ end
221
248
 
222
249
  # The results of the controller's render, to be returned to Rack.
223
250
  #
@@ -225,7 +252,7 @@ class Merb::Controller < Merb::AbstractController
225
252
  # Array[Integer, Hash, String]::
226
253
  # The controller's status code, headers, and body
227
254
  def rack_response
228
- [status, headers, body]
255
+ [status, headers, Merb::Rack::StreamWrapper.new(body)]
229
256
  end
230
257
 
231
258
  # Sets a controller to be "abstract"