Commit 844a188c authored by Ondrej Sury's avatar Ondrej Sury

New upstream version 4.0.0

parent 1e11155c
...@@ -60,11 +60,6 @@ platforms :jruby do ...@@ -60,11 +60,6 @@ platforms :jruby do
gem 'json' gem 'json'
gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7' gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7'
# This is needed by now to let tests work on JRuby
# TODO: When the JRuby guys merge jruby-openssl in
# jruby this will be removed
gem 'jruby-openssl'
group :db do group :db do
gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7' gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7'
gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7' gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7'
......
## Rails 4.0.0 (unreleased) ## ## Rails 4.0.0 (June 25, 2013) ##
## Rails 4.0.0.beta1 (February 25, 2013) ##
* Allow passing interpolations to `#default_i18n_subject`, e.g.: * Allow passing interpolations to `#default_i18n_subject`, e.g.:
......
...@@ -67,12 +67,12 @@ simply call the method and optionally call +deliver+ on the return value. ...@@ -67,12 +67,12 @@ simply call the method and optionally call +deliver+ on the return value.
Calling the method returns a Mail Message object: Calling the method returns a Mail Message object:
message = Notifier.welcome # => Returns a Mail::Message object message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object
message.deliver # => delivers the email message.deliver # => delivers the email
Or you can just chain the methods together like: Or you can just chain the methods together like:
Notifier.welcome.deliver # Creates the email and sends it immediately Notifier.welcome("david@loudthinking.com").deliver # Creates the email and sends it immediately
== Setting defaults == Setting defaults
...@@ -119,8 +119,7 @@ trivial case like this: ...@@ -119,8 +119,7 @@ trivial case like this:
rails runner 'Mailman.receive(STDIN.read)' rails runner 'Mailman.receive(STDIN.read)'
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
instance of Rails should be run within a daemon, if it is going to be utilized to process more than just instance of Rails should be run within a daemon, if it is going to process more than just a limited amount of email.
a limited number of email.
== Configuration == Configuration
......
...@@ -334,8 +334,8 @@ module ActionMailer ...@@ -334,8 +334,8 @@ module ActionMailer
# and starts to use it. # and starts to use it.
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is # * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name # really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
# of an OpenSSL verify constant ('none', 'peer', 'client_once','fail_if_no_peer_cert') or directly the # of an OpenSSL verify constant ('none', 'peer', 'client_once', 'fail_if_no_peer_cert') or directly the
# constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...). # constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER, ...).
# #
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method. # * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>. # * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
...@@ -596,9 +596,9 @@ def attachments ...@@ -596,9 +596,9 @@ def attachments
# class method: # class method:
# #
# class Notifier < ActionMailer::Base # class Notifier < ActionMailer::Base
# self.default from: 'no-reply@test.lindsaar.net', # default from: 'no-reply@test.lindsaar.net',
# bcc: 'email_logger@test.lindsaar.net', # bcc: 'email_logger@test.lindsaar.net',
# reply_to: 'bounces@test.lindsaar.net' # reply_to: 'bounces@test.lindsaar.net'
# end # end
# #
# If you need other headers not listed above, you can either pass them in # If you need other headers not listed above, you can either pass them in
......
module ActionMailer module ActionMailer
# Returns the version of the currently loaded ActionMailer as a Gem::Version # Returns the version of the currently loaded ActionMailer as a Gem::Version
def self.version def self.version
Gem::Version.new "4.0.0.rc1" Gem::Version.new "4.0.0"
end end
module VERSION #:nodoc: module VERSION #:nodoc:
......
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>
<html>
<body>
HTML formatted message to <strong><%= @recipient %></strong>.
</body>
</html>
Ignored when searching for implicitly multipart parts.
...@@ -38,7 +38,7 @@ def attachment_with_content(hash = {}) ...@@ -38,7 +38,7 @@ def attachment_with_content(hash = {})
end end
def attachment_with_hash def attachment_with_hash
attachments['invoice.jpg'] = { data: "\312\213\254\232)b", attachments['invoice.jpg'] = { data: ::Base64.encode64("\312\213\254\232)b"),
mime_type: "image/x-jpg", mime_type: "image/x-jpg",
transfer_encoding: "base64" } transfer_encoding: "base64" }
mail mail
......
## Rails 4.0.0 (unreleased) ## ## Rails 4.0.0 (June 25, 2013) ##
* Merge `:action` from routing scope and assign endpoint if both `:controller`
and `:action` are present. The endpoint assignment only occurs if there is
no `:to` present in the options hash so should only affect routes using the
shorthand syntax (i.e. endpoint is inferred from the the path).
Fixes #9856
*Yves Senn*, *Andrew White*
* Use a case insensitive URI Regexp for #asset_path.
This fix a problem where the same asset path using different case are generating
different URIs.
Before:
image_tag("HTTP://google.com")
# => "<img alt=\"Google\" src=\"/assets/HTTP://google.com\" />"
image_tag("http://google.com")
# => "<img alt=\"Google\" src=\"http://google.com\" />"
After:
image_tag("HTTP://google.com")
# => "<img alt=\"Google\" src=\"HTTP://google.com\" />"
image_tag("http://google.com")
# => "<img alt=\"Google\" src=\"http://google.com\" />"
*David Celis*
* Fix an issue where partials with a number in the filename weren't being digested for cache dependencies.
*Bryan Ricker*
* Add support for passing custom url options other than `:host` and custom * Add support for passing custom url options other than `:host` and custom
status and flash options to `force_ssl`. status and flash options to `force_ssl`.
...@@ -181,9 +215,6 @@ ...@@ -181,9 +215,6 @@
*Thierry Zires* *Thierry Zires*
## Rails 4.0.0.beta1 (February 25, 2013) ##
* Fix `respond_to` not using formats that have no block if all is present. *Michael Grosser* * Fix `respond_to` not using formats that have no block if all is present. *Michael Grosser*
* New applications use an encrypted session store by default. * New applications use an encrypted session store by default.
...@@ -1058,6 +1089,14 @@ ...@@ -1058,6 +1089,14 @@
*Andrew White* *Andrew White*
* In the routes DSL the `:via` option of `match` is now mandatory.
For routes that respond to one single verb it is recommended to use the more specific
macros `get`, `post`, etc. instead. You can still map all HTTP verbs to one action
with `match`, but it has to be explictly configured using `:via => :all`.
*José Valim and Yehuda Katz*
* Add `index` method to FormBuilder class. *Jorge Bejar* * Add `index` method to FormBuilder class. *Jorge Bejar*
* Remove the leading \n added by textarea on `assert_select`. *Santiago Pastorino* * Remove the leading \n added by textarea on `assert_select`. *Santiago Pastorino*
...@@ -1106,8 +1145,6 @@ ...@@ -1106,8 +1145,6 @@
*dlee* *dlee*
* Integration tests support the `OPTIONS` method. *Jeremy Kemper*
* `expires_in` accepts a `must_revalidate` flag. If true, "must-revalidate" * `expires_in` accepts a `must_revalidate` flag. If true, "must-revalidate"
is added to the Cache-Control header. *fxn* is added to the Cache-Control header. *fxn*
......
...@@ -32,7 +32,8 @@ def url_options ...@@ -32,7 +32,8 @@ def url_options
if (same_origin = _routes.equal?(env["action_dispatch.routes"])) || if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) || (script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
(original_script_name = env['SCRIPT_NAME']) (original_script_name = env['ORIGINAL_SCRIPT_NAME'])
@_url_options.dup.tap do |options| @_url_options.dup.tap do |options|
if original_script_name if original_script_name
options[:original_script_name] = original_script_name options[:original_script_name] = original_script_name
......
...@@ -499,12 +499,6 @@ def head(action, *args) ...@@ -499,12 +499,6 @@ def head(action, *args)
process(action, "HEAD", *args) process(action, "HEAD", *args)
end end
# Simulate a OPTIONS request with the given parameters and set/volley the response.
# See +get+ for more details.
def options(action, *args)
process(action, "OPTIONS", *args)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
@request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
......
...@@ -65,7 +65,7 @@ def normalize_encode_params(params) ...@@ -65,7 +65,7 @@ def normalize_encode_params(params)
new_hash = {} new_hash = {}
params.each do |k, v| params.each do |k, v|
new_key = k.is_a?(String) ? k.dup.force_encoding("UTF-8").encode! : k new_key = k.is_a?(String) ? k.dup.force_encoding(Encoding::UTF_8).encode! : k
new_hash[new_key] = new_hash[new_key] =
case v case v
when Hash when Hash
......
...@@ -77,7 +77,7 @@ def cookie_jar ...@@ -77,7 +77,7 @@ def cookie_jar
# domain and subdomains. # domain and subdomains.
# #
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object. # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
# * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers. # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
# Default is +false+. # Default is +false+.
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
# only HTTP. Defaults to +false+. # only HTTP. Defaults to +false+.
......
...@@ -13,6 +13,7 @@ class ExceptionWrapper ...@@ -13,6 +13,7 @@ class ExceptionWrapper
'ActionController::NotImplemented' => :not_implemented, 'ActionController::NotImplemented' => :not_implemented,
'ActionController::UnknownFormat' => :not_acceptable, 'ActionController::UnknownFormat' => :not_acceptable,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity, 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
'ActionDispatch::ParamsParser::ParseError' => :bad_request,
'ActionController::BadRequest' => :bad_request, 'ActionController::BadRequest' => :bad_request,
'ActionController::ParameterMissing' => :bad_request 'ActionController::ParameterMissing' => :bad_request
) )
......
...@@ -7,11 +7,10 @@ def initialize(public_path) ...@@ -7,11 +7,10 @@ def initialize(public_path)
end end
def call(env) def call(env)
exception = env["action_dispatch.exception"]
status = env["PATH_INFO"][1..-1] status = env["PATH_INFO"][1..-1]
request = ActionDispatch::Request.new(env) request = ActionDispatch::Request.new(env)
content_type = request.formats.first content_type = request.formats.first
body = { :status => status, :error => exception.message } body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) }
render(status, content_type, body) render(status, content_type, body)
end end
...@@ -19,7 +18,7 @@ def call(env) ...@@ -19,7 +18,7 @@ def call(env)
private private
def render(status, content_type, body) def render(status, content_type, body)
format = content_type && "to_#{content_type.to_sym}" format = "to_#{content_type.to_sym}" if content_type
if format && body.respond_to?(format) if format && body.respond_to?(format)
render_format(status, content_type, body.public_send(format)) render_format(status, content_type, body.public_send(format))
else else
......
...@@ -34,6 +34,12 @@ ...@@ -34,6 +34,12 @@
padding: 0.5em 1.5em; padding: 0.5em 1.5em;
} }
h1 {
margin: 0.2em 0;
line-height: 1.1em;
font-size: 2em;
}
h2 { h2 {
color: #C52F24; color: #C52F24;
line-height: 25px; line-height: 25px;
......
...@@ -69,7 +69,7 @@ def action ...@@ -69,7 +69,7 @@ def action
end end
def internal? def internal?
controller =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}} controller.to_s =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
end end
def engine? def engine?
......
...@@ -11,8 +11,8 @@ module Routing ...@@ -11,8 +11,8 @@ module Routing
class Mapper class Mapper
URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port] URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
SCOPE_OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module, SCOPE_OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
:controller, :path_names, :constraints, :defaults, :controller, :action, :path_names, :constraints,
:shallow, :blocks, :options] :shallow, :blocks, :defaults, :options]
class Constraints #:nodoc: class Constraints #:nodoc:
def self.new(app, constraints, request = Rack::Request) def self.new(app, constraints, request = Rack::Request)
...@@ -299,7 +299,7 @@ def default_action ...@@ -299,7 +299,7 @@ def default_action
end end
end end
# Invokes Rack::Mount::Utils.normalize path and ensure that # Invokes Journey::Router::Utils.normalize_path and ensure that
# (:locale) becomes (/:locale) instead of /(:locale). Except # (:locale) becomes (/:locale) instead of /(:locale). Except
# for root cases, where the latter is the correct one. # for root cases, where the latter is the correct one.
def self.normalize_path(path) def self.normalize_path(path)
...@@ -869,6 +869,10 @@ def merge_controller_scope(parent, child) #:nodoc: ...@@ -869,6 +869,10 @@ def merge_controller_scope(parent, child) #:nodoc:
child child
end end
def merge_action_scope(parent, child) #:nodoc:
child
end
def merge_path_names_scope(parent, child) #:nodoc: def merge_path_names_scope(parent, child) #:nodoc:
merge_options_scope(parent, child) merge_options_scope(parent, child)
end end
...@@ -1378,6 +1382,10 @@ def match(path, *rest) ...@@ -1378,6 +1382,10 @@ def match(path, *rest)
raise ArgumentError, "Unknown scope #{on.inspect} given to :on" raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
end end
if @scope[:controller] && @scope[:action]
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
end
paths.each do |_path| paths.each do |_path|
route_options = options.dup route_options = options.dup
route_options[:path] ||= _path if _path.is_a?(String) route_options[:path] ||= _path if _path.is_a?(String)
......
...@@ -218,6 +218,7 @@ def handle_positional_args(t, args, options, keys) ...@@ -218,6 +218,7 @@ def handle_positional_args(t, args, options, keys)
keys -= t.url_options.keys if t.respond_to?(:url_options) keys -= t.url_options.keys if t.respond_to?(:url_options)
keys -= options.keys keys -= options.keys
end end
keys -= inner_options.keys
result.merge!(Hash[keys.zip(args)]) result.merge!(Hash[keys.zip(args)])
end end
......
...@@ -62,12 +62,6 @@ def head(path, parameters = nil, headers_or_env = nil) ...@@ -62,12 +62,6 @@ def head(path, parameters = nil, headers_or_env = nil)
process :head, path, parameters, headers_or_env process :head, path, parameters, headers_or_env
end end
# Performs a OPTIONS request with the given parameters. See +#get+ for
# more details.
def options(path, parameters = nil, headers_or_env = nil)
process :options, path, parameters, headers_or_env
end
# Performs an XMLHttpRequest request with the given parameters, mirroring # Performs an XMLHttpRequest request with the given parameters, mirroring
# a request from the Prototype library. # a request from the Prototype library.
# #
...@@ -342,7 +336,7 @@ def reset! ...@@ -342,7 +336,7 @@ def reset!
@integration_session = Integration::Session.new(app) @integration_session = Integration::Session.new(app)
end end
%w(get post patch put head delete options cookies assigns %w(get post patch put head delete cookies assigns
xml_http_request xhr get_via_redirect post_via_redirect).each do |method| xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
define_method(method) do |*args| define_method(method) do |*args|
reset! unless integration_session reset! unless integration_session
......
module ActionPack module ActionPack
# Returns the version of the currently loaded ActionPack as a Gem::Version # Returns the version of the currently loaded ActionPack as a Gem::Version
def self.version def self.version
Gem::Version.new "4.0.0.rc1" Gem::Version.new "4.0.0"
end end
module VERSION #:nodoc: module VERSION #:nodoc:
......
...@@ -39,7 +39,7 @@ class ERBTracker ...@@ -39,7 +39,7 @@ class ERBTracker
render\s* # render, followed by optional whitespace render\s* # render, followed by optional whitespace
\(? # start an optional parenthesis for the render call \(? # start an optional parenthesis for the render call
(partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture (partial:|:partial\s+=>)?\s* # naming the partial, used with collection -- 1st capture
([@a-z"'][@a-z_\/\."']+) # the template name itself -- 2nd capture ([@a-z"'][@\w\/\."']+) # the template name itself -- 2nd capture
/x /x
def self.call(name, template) def self.call(name, template)
......
...@@ -105,7 +105,7 @@ module Helpers ...@@ -105,7 +105,7 @@ module Helpers
# ) # )
# #
module AssetUrlHelper module AssetUrlHelper
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//} URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}i
# Computes the path to asset in public directory. If :type # Computes the path to asset in public directory. If :type
# options is set, a file extension will be appended and scoped # options is set, a file extension will be appended and scoped
...@@ -193,7 +193,6 @@ def compute_asset_host(source = "", options = {}) ...@@ -193,7 +193,6 @@ def compute_asset_host(source = "", options = {})
request = self.request if respond_to?(:request) request = self.request if respond_to?(:request)
host = config.asset_host if defined? config.asset_host host = config.asset_host if defined? config.asset_host
host ||= request.base_url if request && options[:protocol] == :request host ||= request.base_url if request && options[:protocol] == :request
return unless host
if host.respond_to?(:call) if host.respond_to?(:call)
arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
...@@ -204,6 +203,8 @@ def compute_asset_host(source = "", options = {}) ...@@ -204,6 +203,8 @@ def compute_asset_host(source = "", options = {})
host = host % (Zlib.crc32(source) % 4) host = host % (Zlib.crc32(source) % 4)
end end
return unless host
if host =~ URI_REGEXP if host =~ URI_REGEXP
host host
else else
......
...@@ -81,7 +81,7 @@ def form_tag(url_for_options = {}, options = {}, &block) ...@@ -81,7 +81,7 @@ def form_tag(url_for_options = {}, options = {}, &block)
# ==== Options # ==== Options
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices. # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
# * <tt>:include_blank</tt> - If set to true, an empty option will be create # * <tt>:include_blank</tt> - If set to true, an empty option will be created.
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
# * Any other key creates standard HTML attributes for the tag. # * Any other key creates standard HTML attributes for the tag.
# #
......
require 'thread_safe' require 'thread_safe'
require 'active_support/core_ext/module/remove_method' require 'active_support/core_ext/module/remove_method'
require 'active_support/core_ext/module/attribute_accessors'
module ActionView module ActionView
# = Action View Lookup Context # = Action View Lookup Context
......
...@@ -117,12 +117,6 @@ def test_head ...@@ -117,12 +117,6 @@ def test_head
@session.head(path,params,headers) @session.head(path,params,headers)
end end
def test_options
path = "/index"; params = "blah"; headers = {:location => 'blah'}
@session.expects(:process).with(:options,path,params,headers)
@session.options(path,params,headers)
end
def test_xml_http_request_get def test_xml_http_request_get
path = "/index"; params = "blah"; headers = {:location => 'blah'} path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge( headers_after_xhr = headers.merge(
...@@ -183,16 +177,6 @@ def test_xml_http_request_head ...@@ -183,16 +177,6 @@ def test_xml_http_request_head
@session.xml_http_request(:head,path,params,headers) @session.xml_http_request(:head,path,params,headers)
end end
def test_xml_http_request_options
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:options,path,params,headers_after_xhr)
@session.xml_http_request(:options,path,params,headers)
end
def test_xml_http_request_override_accept def test_xml_http_request_override_accept
path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"} path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"}
headers_after_xhr = headers.merge( headers_after_xhr = headers.merge(
...@@ -250,7 +234,7 @@ def test_integration_methods_called ...@@ -250,7 +234,7 @@ def test_integration_methods_called
@integration_session.stubs(:generic_url_rewriter) @integration_session.stubs(:generic_url_rewriter)
@integration_session.stubs(:process) @integration_session.stubs(:process)
%w( get post head patch put delete options ).each do |verb| %w( get post head patch put delete ).each do |verb|
assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') }
end end
end end
......
...@@ -75,7 +75,7 @@ def test_render_json_exception ...@@ -75,7 +75,7 @@ def test_render_json_exception
get "/", {}, 'HTTP_ACCEPT' => 'application/json' get "/", {}, 'HTTP_ACCEPT' => 'application/json'
assert_response :internal_server_error assert_response :internal_server_error
assert_equal 'application/json', response.content_type.to_s assert_equal 'application/json', response.content_type.to_s
assert_equal({ :status => '500', :error => 'boom!' }.to_json, response.body) assert_equal({ :status => '500', :error => 'Internal Server Error' }.to_json, response.body)