Skip to content

Commit

Permalink
Added alert/notice from 2-3-stable and refactored redirect_to into ju…
Browse files Browse the repository at this point in the history
…st living in Redirector [DHH]
  • Loading branch information
dhh committed Dec 18, 2009
1 parent 2bfa477 commit fa57597
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 77 deletions.
12 changes: 12 additions & 0 deletions actionpack/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
*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]


* Introduce grouped_collection_select helper. #1249 [Dan Codeape, Erik Ostrom]

* Make sure javascript_include_tag/stylesheet_link_tag does not append ".js" or ".css" onto external urls. #1664 [Matthew Rudy Jacobs]
Expand Down
66 changes: 0 additions & 66 deletions actionpack/lib/action_controller/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,71 +106,5 @@ def render_to_string(action = nil, options = {}, &blk)
options = _normalize_options(action, options, &blk)
super(options)
end

# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
# Examples:
# redirect_to :action => "show", :id => 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
#
# 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
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?

status = if options.is_a?(Hash) && options.key?(:status)
_interpret_status(options.delete(:status))
elsif response_status.key?(:status)
_interpret_status(response_status[:status])
else
302
end

url = case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
when %r{^\w[\w\d+.-]*:.*}
options
when String
request.protocol + request.host_with_port + options
when :back
raise RedirectBackError unless refer = request.headers["Referer"]
refer
else
url_for(options)
end

super(url, status)
end

private
def _interpret_status(status)
if status.is_a?(Symbol)
(ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500)
else
status.to_i
end
end
end
end
41 changes: 41 additions & 0 deletions actionpack/lib/action_controller/metal/flash.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ module Flash

include Session

included do
helper_method :alert, :notice
end

class FlashNow #:nodoc:
def initialize(flash)
@flash = flash
Expand Down Expand Up @@ -147,6 +151,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


protected
def process_action(method_name)
@_flash = nil
Expand All @@ -159,5 +184,21 @@ def reset_session
super
@_flash = nil
end

def redirect_to(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

super(options, response_status_and_flash)
end
end
end
86 changes: 81 additions & 5 deletions actionpack/lib/action_controller/metal/redirector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,88 @@ module Redirector
extend ActiveSupport::Concern
include AbstractController::Logger

def redirect_to(url, status) #:doc:
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
# Examples:
# redirect_to :action => "show", :id => 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
#
# 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
#
# 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
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
raise AbstractController::DoubleRenderError if response_body
logger.info("Redirected to #{url}") if logger && logger.info?
self.status = status
self.location = url.gsub(/[\r\n]/, '')
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(url)}\">redirected</a>.</body></html>"

self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"

logger.info("Redirected to #{location}") if logger && logger.info?
end

private
def _extract_redirect_to_status(options, response_status)
status = if options.is_a?(Hash) && options.key?(:status)
_interpret_status(options.delete(:status))
elsif response_status.key?(:status)
_interpret_status(response_status[:status])
else
302
end
end

def _compute_redirect_to_location(options)
case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
when %r{^\w[\w\d+.-]*:.*}
options
when String
request.protocol + request.host_with_port + options
when :back
raise RedirectBackError unless refer = request.headers["Referer"]
refer
else
url_for(options)
end.gsub(/[\r\n]/, '')
end

def _interpret_status(status)
if status.is_a?(Symbol)
(ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500)
else
status.to_i
end
end
end
end
29 changes: 28 additions & 1 deletion actionpack/test/controller/flash_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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 @@ -160,4 +172,19 @@ def test_keep_and_discard_return_values
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep()) # nothing passed
assert_equal({:foo => :foo_indeed, :bar => :bar_indeed}, flash.keep(nil)) # nothing passed
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</head>
<body>

<p class="notice"><%%= flash[:notice] %></p>
<p class="notice"><%%= notice %></p>

<%%= yield %>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ def create

respond_to do |format|
if @<%= orm_instance.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 @@ -63,8 +62,7 @@ def update

respond_to do |format|
if @<%= orm_instance.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

0 comments on commit fa57597

Please sign in to comment.