From b2ede64a89a5837a047e75f21a1522324a614514 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Thu, 28 Sep 2006 19:13:55 +0000 Subject: Add ActionController::Base#head for rendering empty responses. Add support for symbolic status codes, as well as for having raw integer statuses expand with their default messages. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@5199 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/CHANGELOG | 11 ++++ actionpack/lib/action_controller/base.rb | 27 +++++++- actionpack/lib/action_controller/status_codes.rb | 79 ++++++++++++++++++++++++ actionpack/test/controller/new_render_test.rb | 59 ++++++++++++++++++ actionpack/test/controller/send_file_test.rb | 2 +- 5 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 actionpack/lib/action_controller/status_codes.rb diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 96493f47cb..72e1d79eff 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,16 @@ *SVN* +* Make the :status parameter expand to the default message for that status code if it is an integer. Also support symbol statuses. [Jamis Buck]. Examples: + + head :status => 404 # expands to "404 Not Found" + head :status => :not_found # expands to "404 Not Found" + head :status => :created # expands to "201 Created" + +* Add head(options = {}) for responses that have no body. [Jamis Buck]. Examples: + + head :status => 404 # return an empty response with a 404 status + head :location => person_path(@person), :status => 201 + * Fix bug that kept any before_filter except the first one from being able to halt the before_filter chain. [Rick Olson] * strip_links is case-insensitive. #6285 [tagoh, Bob Silva] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index e9d415085a..bd2256f44d 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -4,6 +4,7 @@ require 'action_controller/response' require 'action_controller/routing' require 'action_controller/resources' require 'action_controller/url_rewriter' +require 'action_controller/status_codes' require 'drb' require 'set' @@ -209,6 +210,7 @@ module ActionController #:nodoc: DEFAULT_RENDER_STATUS_CODE = "200 OK" include Reloadable::Deprecated + include StatusCodes # Determines whether the view has access to controller internals @request, @response, @session, and @template. # By default, it does. @@ -793,7 +795,7 @@ module ActionController #:nodoc: def render_text(text = nil, status = nil) #:nodoc: @performed_render = true - response.headers['Status'] = (status || DEFAULT_RENDER_STATUS_CODE).to_s + response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE) response.body = text end @@ -830,6 +832,29 @@ module ActionController #:nodoc: end + # Return a response that has no content (merely headers). The options + # argument is interpreted to be a hash of header names and values. + # This allows you to easily return a response that consists only of + # significant headers: + # + # head :status => :created, :location => person_path(@person) + # + # It can also be used to return exceptional conditions: + # + # return head(:status => :method_not_allowed) unless request.post? + # return head(:status => :bad_request) unless valid_request? + # render + def head(options = {}) + status = interpret_status(options.delete(:status) || :ok) + + options.each do |key, value| + headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s + end + + render :nothing => true, :status => status + end + + # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: response.body = nil diff --git a/actionpack/lib/action_controller/status_codes.rb b/actionpack/lib/action_controller/status_codes.rb new file mode 100644 index 0000000000..c6461914ae --- /dev/null +++ b/actionpack/lib/action_controller/status_codes.rb @@ -0,0 +1,79 @@ +module ActionController + module StatusCodes + + # Defines the standard HTTP status codes, by integer, with their + # corresponding default message texts. + STATUS_CODES = { + 100 => "Continue", + 101 => "Switching Protocols", + + 200 => "OK", + 201 => "Created", + 202 => "Accepted", + 203 => "Non-Authoritative Information", + 204 => "No Content", + 205 => "Reset Content", + 206 => "Partial Content", + + 300 => "Multiple Choices", + 301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 304 => "Not Modified", + 305 => "Use Proxy", + 307 => "Temporary Redirect", + + 400 => "Bad Request", + 401 => "Unauthorized", + 402 => "Payment Required", + 403 => "Forbidden", + 404 => "Not Found", + 405 => "Method Not Allowed", + 406 => "Not Acceptable", + 407 => "Proxy Authentication Required", + 408 => "Request Timeout", + 409 => "Conflict", + 410 => "Gone", + 411 => "Length Required", + 412 => "Precondition Failed", + 413 => "Request Entity Too Large", + 414 => "Request-URI Too Long", + 415 => "Unsupported Media Type", + 416 => "Requested Range Not Satisfiable", + 417 => "Expectation Failed", + + 500 => "Internal Server Error", + 501 => "Not Implemented", + 502 => "Bad Gateway", + 503 => "Service Unavailable", + 504 => "Gateway Timeout", + 505 => "HTTP Version Not Supported" + } + + # Provides a symbol-to-fixnum lookup for converting a symbol (like + # :created or :not_implemented) into its corresponding HTTP status + # code (like 200 or 501). + SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)| + hash[message.gsub(/ /, "").underscore.to_sym] = code + hash + end + + # Given a status parameter, determine whether it needs to be converted + # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup + # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE + # hash to convert it. + def interpret_status(status) + case status + when Fixnum then + "#{status} #{STATUS_CODES[status]}".strip + when Symbol then + interpret_status(SYMBOL_TO_STATUS_CODE[status] || + "500 Unknown Status #{status.inspect}") + else + status.to_s + end + end + private :interpret_status + + end +end \ No newline at end of file diff --git a/actionpack/test/controller/new_render_test.rb b/actionpack/test/controller/new_render_test.rb index 936e75d04f..5463df3c60 100644 --- a/actionpack/test/controller/new_render_test.rb +++ b/actionpack/test/controller/new_render_test.rb @@ -202,6 +202,26 @@ class NewRenderTestController < ActionController::Base render :template => "test/hello_world.rxml" end + def head_with_location_header + head :location => "/foo" + end + + def head_with_symbolic_status + head :status => params[:status].intern + end + + def head_with_integer_status + head :status => params[:status].to_i + end + + def head_with_string_status + head :status => params[:status] + end + + def head_with_custom_header + head :x_custom_header => "something" + end + helper NewRenderTestHelper helper do def rjs_helper_method(value) @@ -602,4 +622,43 @@ EOS get :hello_world_from_rxml_using_action assert_equal "\n

Hello

\n\n", @response.body end + + + def test_head_with_location_header + get :head_with_location_header + assert @response.body.blank? + assert_equal "/foo", @response.headers["Location"] + end + + def test_head_with_custom_header + get :head_with_custom_header + assert @response.body.blank? + assert_equal "something", @response.headers["X-Custom-Header"] + end + + def test_head_with_symbolic_status + get :head_with_symbolic_status, :status => "ok" + assert_equal "200 OK", @response.headers["Status"] + + get :head_with_symbolic_status, :status => "not_found" + assert_equal "404 Not Found", @response.headers["Status"] + + ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.each do |status, code| + get :head_with_symbolic_status, :status => status.to_s + assert_equal code, @response.response_code + end + end + + def test_head_with_integer_status + ActionController::StatusCodes::STATUS_CODES.each do |code, message| + get :head_with_integer_status, :status => code.to_s + assert_equal message, @response.message + end + end + + def head_with_string_status + get :head_with_string_status, :status => "404 Eat Dirt" + assert_equal 404, @response.response_code + assert_equal "Eat Dirt", @response.message + end end diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 4c97e2d5d2..a4617cbf5a 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -97,7 +97,7 @@ class SendFileTest < Test::Unit::TestCase define_method "test_send_#{method}_status" do @controller.options = { :stream => false, :status => 500 } assert_nothing_raised { assert_not_nil process(method) } - assert_equal '500', @controller.headers['Status'] + assert_equal '500 Internal Server Error', @controller.headers['Status'] end define_method "test_default_send_#{method}_status" do -- cgit v1.2.3