Why is rack response body an array not a string?
I think rack originated on python's wsgi. Here is the explanation for python:
http://www.python.org/dev/peps/pep-3333/#buffering-and-streaming
Rack Error Rack::Lint::LintError: Response body must respond to each
This is a change in Ruby 1.9.2 and, as has been suggested, if you surround your string with ["brackets"] it will turn "Hello World" into an array with a single value. Sounds silly, but that's the deal :).
It used to be that a String in Ruby would respond to each with an iteration of characters. Evidently there have been changes that way.
render :partial returning an array when defined in ApplicationController but string when in ApplicationHelper
Simply put, don't call the controller's render
in helpers. It just does not work that way
render
in the controller and render
in a helper can't be used interchangeably. This isn't new in Rails 3.1.
When you call render
in the controller it eventually does call render
on the view, the result of which is stored as its response_body
. The response body is eventually returned in the way Rack expects, as a string array (what you see as your output).
These links may shed some more light on how this works:
- The controller's definition of render (metal)
- It's superclass method, where response_body
is set (abstract_controller)
Rack::Deflater and Rack::URLMap
It looks like Rack::BodyProxy
does provide an #each
method as a workaround of issue rack/rack#434. This method returns an enumerator over the elements of the private @body
ivar. Adding an .each
into the call chain resolves this problem for Rack::BodyProxy
objects, and it's a no-op for more ordinary array-based bodies, so for now this gets me where I need to be. However, I would still like to understand why I'm getting these objects and if there's a better way to deal with them.
Here's the modified and working solution:
use Rack::Deflater, :if => lambda { |*, body|
body.each.map(&:bytesize).reduce(0, :+) > 512
}
UPDATE: Hrrm, sometimes body
is a Rack::BodyProxy
object, and body.body
is a Rack::Response
object. This is getting a little out of hand...! Here's the solution I'm using now:
use Rack::Deflater, :if => lambda { |*, body|
body.map(&:bytesize).reduce(0, :+) > 512 \
if body.respond_to?(:map) \
or body.respond_to?(:each) and (body = body.each).respond_to?(:map)
}
Far from elegant...
HelloWorld Rack Program : NoMethodError: undefined method `each'
change:
def response
[200, {}, 'Hello World']
end
to:
def response
[200, {}, ['Hello World']]
end
The following program prints the value of the variable. Why?
In case of ambiguity when compiler finds a variable and method with same name in same scope, it gives precedence to the variable.
To call the method explicitly, send empty parens ()
hello_world = 'Hello Ruby World'
def hello_world
'Hello World'
end
puts hello_world()
or provider an explicit receiver to the method, in this case, using self
self.hello_world
Edit:
As sepp2k
advised in comments below, self.hello_world
would not work with a ruby(.rb
) file. Just to try, you can dynamically dispatch the method with send
:
send(:hello_world) #or
method(:hello_world).call
Alter response.body in Rack Middleware
The problem was that it expects an Array for the 3rd argument in the call
method. This pattern got me working again.
# not real code, just a pattern to follow
class MyMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
new_response = make_new_response(response.body)
# also must reset the Content-Length header if changing body
headers['Content-Length'] = new_response.bytesize.to_s
[status, headers, [new_response]]
end
end
Related Topics
Result.Credit_Card_Verification Is Returning Nil Even on Error in Braintree
Gem Install Dm-Postgres-Adapter Build Error
Setting the Environment in Gemfile for Bundling Install/Update Based on a Customize File
Running a Shell Command from Ruby: Capturing the Output While Displaying the Output
What Ide/Editor Do You Use for Ruby on Linux
How to Access a Variable Within a Heredoc in Ruby
Using Rubyzip to Add Files and Nested Directories to a Zipoutputstream
Ror, Can't Iterate from Datetime/Timewithzone
Application.CSS Not Being Served as an Asset
Generate a 'Link_To' to the Controller Action 'Edit', Dynamically
How to Split a Directory String in Ruby
Turn a Ruby Hash into HTML List
Devise Not Working Well with Multiple Subdomains on Ror3 Application