Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
dhh committed Dec 17, 2009
1 parent 48cd7df commit e6cadd4
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 11 deletions.
11 changes: 11 additions & 0 deletions actionpack/CHANGELOG
@@ -1,5 +1,16 @@
*Edge*

* Added :alert, :notice, and :flash as options to ActionController::Base#redirect_to that'll automatically set the proper flash before the redirection [DHH]. Examples:

flash[:notice] = 'Post was created'
redirect_to(@post)

...becomes:

redirect_to(@post, :notice => 'Post was created')

* 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]

* Added cookies.permanent, cookies.signed, and cookies.permanent.signed accessor for common cookie actions [DHH]. Examples:

cookies.permanent[:prefers_open_id] = true
Expand Down
19 changes: 14 additions & 5 deletions actionpack/lib/action_controller/base.rb
Expand Up @@ -1092,10 +1092,19 @@ def default_url_options(options = nil)
# The redirection happens as a "302 Moved" header unless otherwise specified.
#
# Examples:
# redirect_to post_url(@post), :status=>:found
# redirect_to :action=>'atom', :status=>:moved_permanently
# redirect_to post_url(@post), :status=>301
# redirect_to :action=>'atom', :status=>302
# redirect_to post_url(@post), :status => :found
# redirect_to :action=>'atom', :status => :moved_permanently
# redirect_to post_url(@post), :status => 301
# redirect_to :action=>'atom', :status => 302
#
# 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
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
#
# Examples:
# redirect_to post_url(@post), :alert => "Watch it, mister!"
# redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road"
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
Expand Down Expand Up @@ -1412,7 +1421,7 @@ def process_cleanup
end

Base.class_eval do
[ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
[ Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers, Flash,
Cookies, Caching, Verification, Streaming, SessionManagement,
HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods,
RecordIdentifier, RequestForgeryProtection, Translation
Expand Down
42 changes: 42 additions & 0 deletions actionpack/lib/action_controller/flash.rb
Expand Up @@ -29,8 +29,13 @@ module Flash
def self.included(base)
base.class_eval do
include InstanceMethods

alias_method_chain :perform_action, :flash
alias_method_chain :reset_session, :flash
alias_method_chain :redirect_to, :flash

helper_method :alert
helper_method :notice
end
end

Expand Down Expand Up @@ -155,6 +160,22 @@ def reset_session_with_flash
remove_instance_variable(:@_flash) if defined? @_flash
end

def redirect_to_with_flash(options = {}, response_status_and_flash = {}) #:doc:
if alert = response_status_and_flash.delete(:alert)
flash[:alert] = alert
end

if notice = response_status_and_flash.delete(:notice)
flash[:notice] = notice
end

if other_flashes = response_status_and_flash.delete(:flash)
flash.update(other_flashes)
end

redirect_to_without_flash(options, response_status_and_flash)
end

# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
Expand All @@ -166,6 +187,27 @@ def flash #:doc:

@_flash
end


# Convenience accessor for flash[:alert]
def alert
flash[:alert]
end

# Convenience accessor for flash[:alert]=
def alert=(message)
flash[:alert] = message
end

# Convenience accessor for flash[:notice]
def notice
flash[:notice]
end

# Convenience accessor for flash[:notice]=
def notice=(message)
flash[:notice] = message
end
end
end
end
29 changes: 28 additions & 1 deletion actionpack/test/controller/flash_test.rb
Expand Up @@ -71,6 +71,18 @@ def halt_and_redir
redirect_to :action => "std_action"
@flash_copy = {}.update(flash)
end

def redirect_with_alert
redirect_to '/nowhere', :alert => "Beware the nowheres!"
end

def redirect_with_notice
redirect_to '/somewhere', :notice => "Good luck in the somewheres!"
end

def redirect_with_other_flashes
redirect_to '/wonderland', :flash => { :joyride => "Horses!" }
end
end

tests TestController
Expand Down Expand Up @@ -144,4 +156,19 @@ def test_does_not_set_the_session_if_the_flash_is_empty
get :std_action
assert_nil session["flash"]
end
end

def test_redirect_to_with_alert
get :redirect_with_alert
assert_equal "Beware the nowheres!", @controller.send(:flash)[:alert]
end

def test_redirect_to_with_notice
get :redirect_with_notice
assert_equal "Good luck in the somewheres!", @controller.send(:flash)[:notice]
end

def test_redirect_to_with_other_flashes
get :redirect_with_other_flashes
assert_equal "Horses!", @controller.send(:flash)[:joyride]
end
end
Expand Up @@ -44,8 +44,7 @@ def create

respond_to do |format|
if @<%= file_name %>.save
flash[:notice] = '<%= class_name %> was successfully created.'
format.html { redirect_to(@<%= file_name %>) }
format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully created.') }
format.xml { render :xml => @<%= file_name %>, :status => :created, :location => @<%= file_name %> }
else
format.html { render :action => "new" }
Expand All @@ -61,8 +60,7 @@ def update

respond_to do |format|
if @<%= file_name %>.update_attributes(params[:<%= file_name %>])
flash[:notice] = '<%= class_name %> was successfully updated.'
format.html { redirect_to(@<%= file_name %>) }
format.html { redirect_to(@<%= file_name %>, :notice => '<%= class_name %> was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
Expand Down
Expand Up @@ -9,7 +9,7 @@
</head>
<body>

<p style="color: green"><%%= flash[:notice] %></p>
<p style="color: green"><%%= notice %></p>

<%%= yield %>

Expand Down

27 comments on commit e6cadd4

@robbyrussell
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@rmoriz
Copy link

@rmoriz rmoriz commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@millar
Copy link

@millar millar commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@scott-steadman
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@dhh
Copy link
Member Author

@dhh dhh commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@mikesax
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@westarete
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

@dallas dallas commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dhh, thanks :)

@jondruse
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@dhh
Copy link
Member Author

@dhh dhh commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@rmoriz
Copy link

@rmoriz rmoriz commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhh

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

@dhh
Copy link
Member Author

@dhh dhh commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@dallas
Copy link
Contributor

@dallas dallas commented on e6cadd4 Dec 17, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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

@justinko
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And why can't error and success be added?

Array has first, second, third, etc.

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

@dhh
Copy link
Member Author

@dhh dhh commented on e6cadd4 Dec 20, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link

@kris kris commented on e6cadd4 Dec 20, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@thibaudgg
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@labria
Copy link
Contributor

@labria labria commented on e6cadd4 Dec 20, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tenth ?

@nicolasblanco
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea guillaumegentil !

@RStankov
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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

@farski
Copy link
Contributor

@farski farski commented on e6cadd4 Dec 21, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@grimen
Copy link

@grimen grimen commented on e6cadd4 Dec 21, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. =)

@wycats
Copy link
Member

@wycats wycats commented on e6cadd4 Dec 21, 2009

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

@lawrencepit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if you could also do this:

render :notice => 'foo'

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

@jspooner
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jspooner
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

<%= controller.session[:flash][:notice] %>

Please sign in to comment.