Skip to content
This repository
Browse code

Added :alert, :notice, and :flash as options to ActionController::Bas…

…e#redirect_to that'll automatically set the proper flash before the redirection [DHH] Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH]
  • Loading branch information...
commit e6cadd422b72ba9818cc2f3b22243a6aa754c9f8 1 parent 48cd7df
David Heinemeier Hansson authored December 17, 2009
11  actionpack/CHANGELOG
... ...
@@ -1,5 +1,16 @@
1 1
 *Edge*
2 2
 
  3
+* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples:
  4
+
  5
+    flash[:notice] = 'Post was created'
  6
+    redirect_to(@post)
  7
+  
  8
+  ...becomes:
  9
+  
  10
+    redirect_to(@post, :notice => 'Post was created')
  11
+
  12
+* Added ActionController::Base#notice/= and ActionController::Base#alert/= as a convenience accessors in both the controller and the view for flash[:notice]/= and flash[:alert]/= [DHH]
  13
+
3 14
 * Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples:
4 15
 
5 16
     cookies.permanent[:prefers_open_id] = true
19  actionpack/lib/action_controller/base.rb
@@ -1092,10 +1092,19 @@ def default_url_options(options = nil)
1092 1092
       # The redirection happens as a "302 Moved" header unless otherwise specified.
1093 1093
       #
1094 1094
       # Examples:
1095  
-      #   redirect_to post_url(@post), :status=>:found
1096  
-      #   redirect_to :action=>'atom', :status=>:moved_permanently
1097  
-      #   redirect_to post_url(@post), :status=>301
1098  
-      #   redirect_to :action=>'atom', :status=>302
  1095
+      #   redirect_to post_url(@post), :status => :found
  1096
+      #   redirect_to :action=>'atom', :status => :moved_permanently
  1097
+      #   redirect_to post_url(@post), :status => 301
  1098
+      #   redirect_to :action=>'atom', :status => 302
  1099
+      #
  1100
+      # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
  1101
+      # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
  1102
+      #
  1103
+      # Examples:
  1104
+      #   redirect_to post_url(@post), :alert => "Watch it, mister!"
  1105
+      #   redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road"
  1106
+      #   redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
  1107
+      #   redirect_to { :action=>'atom' }, :alert => "Something serious happened"
1099 1108
       #
1100 1109
       # When using <tt>redirect_to :back</tt>, if there is no referrer,
1101 1110
       # RedirectBackError will be raised. You may specify some fallback
@@ -1412,7 +1421,7 @@ def process_cleanup
1412 1421
   end
1413 1422
 
1414 1423
   Base.class_eval do
1415  
-    [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
  1424
+    [ Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, Flash,
1416 1425
       Cookies, Caching, Verification, Streaming, SessionManagement,
1417 1426
       HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods,
1418 1427
       RecordIdentifier, RequestForgeryProtection, Translation
42  actionpack/lib/action_controller/flash.rb
@@ -29,8 +29,13 @@ module Flash
29 29
     def self.included(base)
30 30
       base.class_eval do
31 31
         include InstanceMethods
  32
+
32 33
         alias_method_chain :perform_action, :flash
33 34
         alias_method_chain :reset_session,  :flash
  35
+        alias_method_chain :redirect_to,    :flash
  36
+
  37
+        helper_method :alert
  38
+        helper_method :notice
34 39
       end
35 40
     end
36 41
 
@@ -155,6 +160,22 @@ def reset_session_with_flash
155 160
           remove_instance_variable(:@_flash) if defined? @_flash
156 161
         end
157 162
 
  163
+        def redirect_to_with_flash(options = {}, response_status_and_flash = {}) #:doc:
  164
+          if alert = response_status_and_flash.delete(:alert)
  165
+            flash[:alert] = alert
  166
+          end
  167
+
  168
+          if notice = response_status_and_flash.delete(:notice)
  169
+            flash[:notice] = notice
  170
+          end
  171
+          
  172
+          if other_flashes = response_status_and_flash.delete(:flash)
  173
+            flash.update(other_flashes)
  174
+          end
  175
+          
  176
+          redirect_to_without_flash(options, response_status_and_flash)
  177
+        end
  178
+
158 179
         # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
159 180
         # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
160 181
         # to put a new one.
@@ -166,6 +187,27 @@ def flash #:doc:
166 187
 
167 188
           @_flash
168 189
         end
  190
+
  191
+        
  192
+        # Convenience accessor for flash[:alert]
  193
+        def alert
  194
+          flash[:alert]
  195
+        end
  196
+        
  197
+        # Convenience accessor for flash[:alert]=
  198
+        def alert=(message)
  199
+          flash[:alert] = message
  200
+        end
  201
+
  202
+        # Convenience accessor for flash[:notice]
  203
+        def notice
  204
+          flash[:notice]
  205
+        end
  206
+        
  207
+        # Convenience accessor for flash[:notice]=
  208
+        def notice=(message)
  209
+          flash[:notice] = message
  210
+        end
169 211
     end
170 212
   end
171 213
 end
29  actionpack/test/controller/flash_test.rb
@@ -71,6 +71,18 @@ def halt_and_redir
71 71
       redirect_to :action => "std_action"
72 72
       @flash_copy = {}.update(flash)
73 73
     end
  74
+    
  75
+    def redirect_with_alert
  76
+      redirect_to '/nowhere', :alert => "Beware the nowheres!"
  77
+    end
  78
+    
  79
+    def redirect_with_notice
  80
+      redirect_to '/somewhere', :notice => "Good luck in the somewheres!"
  81
+    end
  82
+
  83
+    def redirect_with_other_flashes
  84
+      redirect_to '/wonderland', :flash => { :joyride => "Horses!" }
  85
+    end
74 86
   end
75 87
 
76 88
   tests TestController
@@ -144,4 +156,19 @@ def test_does_not_set_the_session_if_the_flash_is_empty
144 156
     get :std_action
145 157
     assert_nil session["flash"]
146 158
   end
147  
-end
  159
+  
  160
+  def test_redirect_to_with_alert
  161
+    get :redirect_with_alert
  162
+    assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert]
  163
+  end
  164
+  
  165
+  def test_redirect_to_with_notice
  166
+    get :redirect_with_notice
  167
+    assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice]
  168
+  end
  169
+  
  170
+  def test_redirect_to_with_other_flashes
  171
+    get :redirect_with_other_flashes
  172
+    assert_equal "Horses!", @controller.send(:flash)[:joyride]
  173
+  end
  174
+end
6  railties/lib/rails_generator/generators/components/scaffold/templates/controller.rb
@@ -44,8 +44,7 @@ def create
44 44
 
45 45
     respond_to do |format|
46 46
       if @<%= file_name %>.save
47  
-        flash[:notice] = '<%= class_name %> was successfully created.'
48  
-        format.html { redirect_to(@<%= file_name %>) }
  47
+        format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully created.') }
49 48
         format.xml  { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
50 49
       else
51 50
         format.html { render :action => "new" }
@@ -61,8 +60,7 @@ def update
61 60
 
62 61
     respond_to do |format|
63 62
       if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
64  
-        flash[:notice] = '<%= class_name %> was successfully updated.'
65  
-        format.html { redirect_to(@<%= file_name %>) }
  63
+        format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully updated.') }
66 64
         format.xml  { head :ok }
67 65
       else
68 66
         format.html { render :action => "edit" }
2  railties/lib/rails_generator/generators/components/scaffold/templates/layout.html.erb
@@ -9,7 +9,7 @@
9 9
 </head>
10 10
 <body>
11 11
 
12  
-<p style="color: green"><%%= flash[:notice] %></p>
  12
+<p style="color: green"><%%= notice %></p>
13 13
 
14 14
 <%%= yield %>
15 15
 

27 notes on commit e6cadd4

Robby Russell

While I like the helper methods for notice/alert, I'm not sure that I'd end up using that. Most of the time that we are display flash messages, we want to make sure there are any flashes set before we attempt to render the notice/alert out.

We follow this pattern so that the HTML markup is consistent across our projects: http://github.com/planetargon/flash-message-conductor

I assume that we can still check on whether flash has anything in it?

Roland Moriz

What about flash arrays? e.g. when you have several messages that belong into the same flash key.

Sam Millar

Well I use flash[:error] not flash[:alert] - naughty me.

Scott Steadman

Why not use @response.flash[:notice] instead of @controller.send(:flash)[:notice] ?

David Heinemeier Hansson
Owner

robby, you can keep using whatever you're doing now just fine. This pattern will allow me to extract tons of code from all my apps though.
ss, because it's a protected method.

Mike Sax

Nice, but I wonder if the :flash => {} option isn't a bit over the top. It doesn't really save much code or make things more readable.

West Arete

Great addition. Not only is it more succinct, I think it actually does a better job than the usual method of expressing how the flash behaves -- it's usually a bit confusing to beginners that the flash applies to the next request, and not the current request (almost no other setting in rails behaves that way). People usually seem to be surprised that working in the current request is the special case (flash.now).

Tying it in to the redirect makes the actual behavior a natural assumption.

Dallas Reedy

Can any light be shed on the naming of :alert versus :error ?
For some reason I've only ever seen :error (I've not seen the use of :alert until now).

Scott Steadman

dhh, thanks :)

Jon Druse

This is a great change! I always hated that it took two lines to accomplish such a simple thing.

David Heinemeier Hansson
Owner

dallas, we've been using alert for everything at 37signals. It doesn't really matter. Just that there is one accessor for go and one for stop.

Roland Moriz

@dhh

Your AWDROR ("21.3 Flash—Communicating between Actions") uses flash[:notice], flash[:error] and flash[:warning]

David Heinemeier Hansson
Owner

rmoriz, I'll make sure to get that updated for the next version of the book.

Dallas Reedy

@dhh

Thanks for your answer on that. I've basically learned the majority of what I know of Rails from dev teams I've been a part of and from the Agile Web Development with Rails book. :error, :alert; tom·ay·to, tom·ah·to

Justin Ko

And why can't error and success be added?

Array has first, second, third, etc.

Go (green), Stop (red)....... how about Notice (yellow) ?

David Heinemeier Hansson
Owner

jko, there's not enough difference to justify the extensions. If you're using :success now and want to use the built-in helpers, just switch to :notice. Or if you're married to :success, just overwrite redirect_to in your application.rb to pickup :success as well.

kris

These two don't belong in the same function, it's just more bloat. For those of you crying about having to have an "extra" line of code, are you serious? If it's that big of a deal, write a helper that will deal with your flash then redirect.

Thibaud Guillaume-Gentil

It’s would be nice if

class UsersController < ApplicationController
  def create
     @user = User.create(params[:user])
     redirect_to user_path(@user), :notice => true
  end
end

automatically look at ‘users.notice.flash’ in I18n locales.

Dimitri Krassovski

#tenth ?

Nicolas Blanco

Great idea guillaumegentil !

Radoslav Stankov

@guillaumegentil really good idea,
Most of my flashes are wrapped with t() and such, behavior will really help to clean up the controllers code more

Chris Kalafarski

While I don't really care about the specific syntax/lexicon in this particular case, if there is going to be an established convention for something like this, it does seem like alert/notice are really very ambiguous. In fact, to me they almost seem synonymous (which isn't just not very ruby, it's not very smart). I'm sure they work fine at 37S, and they don't even seem to be causing any confusion here (we all know alert is a stop, so it's not an argument of 'which one means what?!'), but that doesn't mean they're the best option to use as the convention. Even if this is the only place these terms are hardcoded in, that's enough impetus to sway probably 90% of new projects to name their flashes this way.

I would think ten minutes of looking through a thesaurus could yield a couple words that clearly mean what they represent, which would result in most new Rails projects having meaningfully named flashes, rather than having flashes that everyone-knows-what-they-mean-even-though-it's-not-really-that-obvious. This seems like it's a no brainer; why go with option 1, which is fine but not great, when option 2 is just as easy to implement and better? If 37S is married to the crappy option, 'just overwrite redirect_to in your application.rb', no?

Jonas Grimfelt

I personally really like the convention José using in InheritedResources: :success and :failure

http://github.com/josevalim/inherited_resources

I use that one in all my projects, so I'm not that effected but this change to the Rails core - but I do agree with @farski et.al. that these flashes are a bit of "confusing naming convention". That's my 2 cents. =)

Yehuda Katz
Owner

The important thing about this commit is the combination of redirect with flash. The notice and alert helpers are mostly sugar (and they're not really synonymous with #tenth -- flash[:notice] and flash[:alert] are pretty idiomatic in the apps I've ever done).

Lawrence Pit

Would be nice if you could also do this:

render :notice => 'foo'

which would set flash.now[:notice] before rendering the template.

Jonathan Spooner

We'll I upgraded from 2.3.6 to 2.3.10 and I'm nolonger able to access the notice like this ( flash[:notice] )

Now I can only access it via the controller like this ( controller.session[:flash][:notice] )

And notice does not work.

Jonathan Spooner

It's very annoying that something as basic as flash is such a headache.

This is now the only way to access your flash notice? flash[:notice] and notice do not work.

Please sign in to comment.
Something went wrong with that request. Please try again.