Devise: Allow configuring custom flash keys

Created on 15 Feb 2013  路  5Comments  路  Source: heartcombo/devise

This is a somewhat trivial request, and I'm curious how many others might be interested in it, whether it's a worthy addition, etc...

I've got a couple apps that use custom flash keys for various types of messages. Along with the default :notice and :alert keys, we also use :success and :error keys. :success is used for things like successful form submissions (such as logging in). :notice is used to display useful messages and info that are not considered warnings or errors. :alert is essentially a warning, and :error messages are (surprise!) used for form errors and other failures. Each type of message might be displayed differently depending on the situation (generally this just means colors/styles, and in some specific cases we render the messages in different areas on the page).

It would be great to be able to configure the flash keys used for success and failure messages in Devise (:notice and :alert by default), ideally without having to override a ton of stuff unnecessarily - i.e. without overriding the entire SessionsController.

I'm thinking a config option (with :notice and :alert as the defaults of course) that allows you to override the two flash keys used would be fantastic.

I'd be happy to take a crack at adding the config options, utilizing the variables in place of the current keys, and writing tests for everything... Just wanted to try to gauge interest first, and mostly make sure no one was opposed to the idea!

Most helpful comment

Just in case anyone is ever interested, here's what I ended up with in a config initializer. There were a couple spots in Devise::FailureApp that also referenced flash directly in addition to the DeviseController#set_flash_message method.

I suppose in this particular case there's no real reason to alias the methods rather than just overriding them, and it could probably be broken out into mixins if you really wanted to.

Anyway, hopefully it's helpful if anyone else ever wants to adjust this in one of their own apps! Thanks again for the suggestions.

FooApp::Application.configure do
  config.after_initialize do
    DeviseController.class_eval do
      def set_flash_message_with_custom_keys(key, kind, options={})
        case key 
          when :notice
            key = :success
          when :alert
            key = :error
        end 
        options[:scope] = "devise.#{controller_name}"
        options[:default] = Array(options[:default]).unshift(kind.to_sym)
        options[:resource_name] = resource_name
        options = devise_i18n_options(options) if respond_to?(:devise_i18n_options, true)
        message = I18n.t("#{options[:resource_name]}.#{kind}", options)
        flash[key] = message if message.present?
      end 
      alias_method_chain :set_flash_message, :custom_keys
    end 

    Devise::FailureApp.class_eval do
      def recall_with_custom_flash_keys
        env["PATH_INFO"]  = attempted_path
        flash.now[:error] = i18n_message(:invalid)
        self.response = recall_app(warden_options[:recall]).call(env)
      end 
      alias_method_chain :recall, :custom_flash_keys

      def redirect_with_custom_flash_keys
        store_location!
        if flash[:timedout] && flash[:error]
          flash.keep(:timedout)
          flash.keep(:error)
        else
          flash[:error] = i18n_message
        end 
        redirect_to redirect_url
      end 
      alias_method_chain :redirect, :custom_flash_keys
    end 
  end 
end

All 5 comments

Thanks for pinging us! However, this would make the code more complex and given Rails already picked up notice and alert as convention, I don't think the trade-offs are worthy in this case.

One way you could work around this in your app is to override DeviseController#set_flash_message to change the keys used. I have not tested this strategy but it would be my first attempt at fixing it in my own app if I were in your situation.

That's an excellent suggestion! I completely missed that somehow, and I'll give it a shot.

My other thought, if that doesn't pan out (just in case anyone with a similar situation ever stumbles on this again), was that we might rework the internal keys used for our flash messages so that instead of :success, :notice, :alert and :error we use :notice and :alert per the Rails convention and add 2 new keys:

:notice == "success"
:info == "info, messaging that's NOT an error/warning"
:warn == "warning"
:alert == "error"

Thanks for the feedback!

Just in case anyone is ever interested, here's what I ended up with in a config initializer. There were a couple spots in Devise::FailureApp that also referenced flash directly in addition to the DeviseController#set_flash_message method.

I suppose in this particular case there's no real reason to alias the methods rather than just overriding them, and it could probably be broken out into mixins if you really wanted to.

Anyway, hopefully it's helpful if anyone else ever wants to adjust this in one of their own apps! Thanks again for the suggestions.

FooApp::Application.configure do
  config.after_initialize do
    DeviseController.class_eval do
      def set_flash_message_with_custom_keys(key, kind, options={})
        case key 
          when :notice
            key = :success
          when :alert
            key = :error
        end 
        options[:scope] = "devise.#{controller_name}"
        options[:default] = Array(options[:default]).unshift(kind.to_sym)
        options[:resource_name] = resource_name
        options = devise_i18n_options(options) if respond_to?(:devise_i18n_options, true)
        message = I18n.t("#{options[:resource_name]}.#{kind}", options)
        flash[key] = message if message.present?
      end 
      alias_method_chain :set_flash_message, :custom_keys
    end 

    Devise::FailureApp.class_eval do
      def recall_with_custom_flash_keys
        env["PATH_INFO"]  = attempted_path
        flash.now[:error] = i18n_message(:invalid)
        self.response = recall_app(warden_options[:recall]).call(env)
      end 
      alias_method_chain :recall, :custom_flash_keys

      def redirect_with_custom_flash_keys
        store_location!
        if flash[:timedout] && flash[:error]
          flash.keep(:timedout)
          flash.keep(:error)
        else
          flash[:error] = i18n_message
        end 
        redirect_to redirect_url
      end 
      alias_method_chain :redirect, :custom_flash_keys
    end 
  end 
end

@markrebec Thanks we were also wondering why Devise shows alert messages instead of error messages on failed login attempts.

Was this page helpful?
0 / 5 - 0 ratings