1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #21671 from kaspth/integration-request-encoding-helpers

Add `as` to encode a request as a specific mime type.
This commit is contained in:
David Heinemeier Hansson 2016-02-10 17:46:14 +01:00
commit 688996da7b
2 changed files with 122 additions and 4 deletions

View file

@ -321,7 +321,9 @@ module ActionDispatch
end
# Performs the actual request.
def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
request_encoder = RequestEncoder.encoder(as)
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@ -330,14 +332,17 @@ module ActionDispatch
url_host += ":#{location.port}" if default != location.port
host! url_host
end
path = location.query ? "#{location.path}?#{location.query}" : location.path
path = request_encoder.append_format_to location.path
path = location.query ? "#{path}?#{location.query}" : path
else
path = request_encoder.append_format_to path
end
hostname, port = host.split(':')
request_env = {
:method => method,
:params => params,
:params => request_encoder.encode_params(params),
"SERVER_NAME" => hostname,
"SERVER_PORT" => port || (https? ? "443" : "80"),
@ -347,7 +352,7 @@ module ActionDispatch
"REQUEST_URI" => path,
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
"CONTENT_TYPE" => request_encoder.content_type,
"HTTP_ACCEPT" => accept
}
@ -387,6 +392,48 @@ module ActionDispatch
def build_full_uri(path, env)
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
end
class RequestEncoder # :nodoc:
@encoders = {}
def initialize(mime_name, param_encoder, url_encoded_form = false)
@mime = Mime[mime_name]
unless @mime
raise ArgumentError, "Can't register a request encoder for " \
"unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
end
@url_encoded_form = url_encoded_form
@path_format = ".#{@mime.symbol}" unless @url_encoded_form
@param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
end
def append_format_to(path)
path << @path_format unless @url_encoded_form
path
end
def content_type
@mime.to_s
end
def encode_params(params)
@param_encoder.call(params)
end
def self.encoder(name)
@encoders[name] || WWWFormEncoder
end
def self.register_encoder(mime_name, &param_encoder)
@encoders[mime_name] = new(mime_name, param_encoder)
end
register_encoder :json
WWWFormEncoder = new(:url_encoded_form, -> params { params }, true)
end
end
module Runner
@ -643,6 +690,30 @@ module ActionDispatch
# end
# end
#
# You can also test your JSON API easily by setting what the request should
# be encoded as:
#
# require 'test_helper'
#
# class ApiTest < ActionDispatch::IntegrationTest
# test "creates articles" do
# assert_difference -> { Article.count } do
# post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
# end
#
# assert_response :success
# end
# end
#
# The `as` option sets the format to JSON, sets the content type to
# 'application/json' and encodes the parameters as JSON.
#
# For any custom MIME Types you've registered, you can even add your own encoders with:
#
# ActionDispatch::IntegrationTest.register_encoder :wibble do |params|
# params.to_wibble
# end
#
# Consult the Rails Testing Guide for more.
class IntegrationTest < ActiveSupport::TestCase
@ -671,5 +742,9 @@ module ActionDispatch
def document_root_element
html_document.root
end
def self.register_encoder(*args, &param_encoder)
Integration::Session::RequestEncoder.register_encoder(*args, &param_encoder)
end
end
end

View file

@ -1126,3 +1126,46 @@ class IntegrationRequestsWithSessionSetup < ActionDispatch::IntegrationTest
assert_equal({"user_name"=>"david"}, cookies.to_hash)
end
end
class IntegrationRequestEncodersTest < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
def foos
render plain: 'ok'
end
end
def test_encoding_as_json
assert_encoded_as :json, content_type: 'application/json'
end
def test_encoding_as_without_mime_registration
assert_raise ArgumentError do
ActionDispatch::IntegrationTest.register_encoder :wibble
end
end
def test_registering_custom_encoder
Mime::Type.register 'text/wibble', :wibble
ActionDispatch::IntegrationTest.register_encoder(:wibble, &:itself)
assert_encoded_as :wibble, content_type: 'text/wibble',
parsed_parameters: Hash.new # Unregistered MIME Type can't be parsed
ensure
Mime::Type.unregister :wibble
end
private
def assert_encoded_as(format, content_type:, parsed_parameters: { 'foo' => 'fighters' })
with_routing do |routes|
routes.draw { post ':action' => FooController }
post '/foos', params: { foo: 'fighters' }, as: format
assert_response :success
assert_match "foos.#{format}", request.path
assert_equal content_type, request.content_type
assert_equal parsed_parameters, request.request_parameters
end
end
end