How do I set a cookie with a (ruby) rack middleware component?

phaedryx picture phaedryx · Jul 21, 2010 · Viewed 11.3k times · Source

I'm writing a rack middleware component for a rails app that will need to conditionally set cookies. I am currently trying to figure out to set cookies. From googling around it seems like this should work:

class RackApp
  def initialize(app)
    @app = app
  end

  def call(env)
    @status, @headers, @response = @app.call(env)
    @response.set_cookie("foo", {:value => "bar", :path => "/", :expires => Time.now+24*60*60})
    [@status, @headers, @response]
  end
end

which doesn't give errors, but doesn't set a cookie either. What am I doing wrong?

Answer

BaroqueBobcat picture BaroqueBobcat · Jul 21, 2010

If you want to use the Response class, you need to instantiate it from the results of calling the middleware layer further down the stack. Also, you don't need instance variables for a middleware like this and probably don't want to use them that way(@status,etc would stay around in the middleware instance after the request is served)

class RackApp
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    # confusingly, response takes its args in a different order
    # than rack requires them to be passed on
    # I know it's because most likely you'll modify the body, 
    # and the defaults are fine for the others. But, it still bothers me.

    response = Rack::Response.new body, status, headers

    response.set_cookie("foo", {:value => "bar", :path => "/", :expires => Time.now+24*60*60})
    response.finish # finish writes out the response in the expected format.
  end
end

If you know what you are doing you could directly modify the cookie header, if you don't want to instantiate a new object.