Jason Swett
Jason Swett

Reputation: 45094

Rails flash missing from session

I did an upgrade from Rails 3.2 to 4.0 and now none of my flash messages work after a redirect.

I checked to make sure it's not a problem with the way I'm displaying the flash messages. Flash messages that don't involve a redirect work fine.

Flash missing from session

I dug into it and noticed something interesting. If I put a Pry on the controller action I redirect to, I can see that session["flash"] is nil. I don't believe session["flash"] should be nil at that point.

If I enter session.keys into the console, it shows me this:

["session_id", "kmq", "warden.user.user.key", "_csrf_token", "foo"]
# "foo" is something I added myself as a test

Shouldn't there be a session key called "flash"?

Flash missing from env

I look at the FlashHash source code and I see that flash messages apparently get saved not only to the session, but also to @env. So I performed the following experiment.

# I'm manually setting a flash message
> flash[:notice] = "hello"

# Now I'm checking for the flash message in env
> env["action_dispatch.request.flash_hash"]

=> #<ActionDispatch::Flash::FlashHash:0x007fda544718b0
@discard=#<Set: {}>,
@flashes={:notice=>"hello"},
@now=nil>

# Now I'm checking for it in the session
> session["flash"]
=> nil

And then if I do a Pry on the controller action to which my redirect goes, I can do this:

> env["action_dispatch.request.flash_hash"]

And it returns nil.

Env sometimes, session never

My HashFlash evidently makes it into env sometimes, session never. The fact that it never gets into session strikes me as probably incorrect behavior.

My redirect code

def update
  user_params.delete(:password) if user_params && user_params[:password].blank?
  if user.update_attributes(user_params)
    respond_to do |format|
      format.html do
        redirect_to(user_account_path(user, anchor: params[:tab]), notice: "Your account has been updated")
      end
      format.js
    end
  else
    render :show
  end
end

I know for a fact that control is getting to the redirect. I've also tried changing the code to this with no luck.

def update
  # Redirect right away to kill the extra moving parts
  redirect_to(user_account_path(user, anchor: params[:tab]), notice: "Your account has been updated")
  return

  user_params.delete(:password) if user_params && user_params[:password].blank?
  if user.update_attributes(user_params)
    respond_to do |format|
      format.html do
        redirect_to(user_account_path(user, anchor: params[:tab]), notice: "Your account has been updated")
      end
      format.js
    end
  else
    render :show
  end
end

So my question is: how can I get my flash messages to work again?

Edit: When I manually set session["flash"] to flash.to_session_value, the flash message comes across, although then it's permanently "stuck on". That seems like a clue, though.

Edit 2: I've learned, by doing some inspection on a known-working project, that session["flash"] can never be expected to be present immediately after assigning a value to flash[:notice] (or whatever other key). Apparently the flash messages are only plugged into session after the controller action returns. So that solves that part of the mystery.

Edit 3: FWIW, here's the output of rake middleware:

use Airbrake::UserInformer
use Rack::Sendfile
use ActionDispatch::Static
use Rack::Lock
use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fcc23268eb8>
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use BetterErrors::Middleware
use Airbrake::Rails::Middleware
use ActionDispatch::RemoteIp
use ActionDispatch::Reloader
use ActionDispatch::Callbacks
use ActiveRecord::Migration::CheckPending
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
use ActionDispatch::Cookies
use ActionDispatch::Session::ActiveRecordStore
use ActionDispatch::Flash
use ActionDispatch::ParamsParser
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Warden::Manager
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use OmniAuth::Builder
run DueProps::Application.routes

I don't know if that provides any clues or not.

Upvotes: 4

Views: 2077

Answers (1)

Jason Swett
Jason Swett

Reputation: 45094

I found the culprit. Someone had added this in config/application.rb:

use ActionDispatch::Cookies
use ActionDispatch::Session::ActiveRecordStore

Thus, rake middleware was showing two each of ActionDispatch::Cookies and ActionDispatch::Session:

use ActionDispatch::Cookies
use ActionDispatch::Session::ActiveRecordStore

use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore

Once we removed those two lines, flash messages worked once again.

I have to thank @jbgo because his comment is what led me to the solution.

Upvotes: 3

Related Questions