actionpack 5.0.0.1 → 5.0.1.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +195 -0
- data/README.rdoc +1 -1
- data/lib/abstract_controller/callbacks.rb +3 -1
- data/lib/action_controller/metal/data_streaming.rb +1 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +7 -1
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/implicit_render.rb +1 -1
- data/lib/action_controller/metal/live.rb +6 -1
- data/lib/action_controller/metal/renderers.rb +0 -2
- data/lib/action_controller/metal/rendering.rb +1 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +3 -3
- data/lib/action_controller/metal/strong_parameters.rb +53 -15
- data/lib/action_controller/test_case.rb +39 -21
- data/lib/action_dispatch.rb +1 -0
- data/lib/action_dispatch/http/headers.rb +1 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +2 -2
- data/lib/action_dispatch/http/parameters.rb +8 -1
- data/lib/action_dispatch/http/request.rb +0 -12
- data/lib/action_dispatch/http/response.rb +15 -8
- data/lib/action_dispatch/journey/formatter.rb +3 -1
- data/lib/action_dispatch/journey/parser.rb +2 -0
- data/lib/action_dispatch/journey/parser_extras.rb +4 -2
- data/lib/action_dispatch/journey/route.rb +5 -3
- data/lib/action_dispatch/journey/router.rb +3 -1
- data/lib/action_dispatch/journey/visitors.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +3 -3
- data/lib/action_dispatch/middleware/debug_exceptions.rb +5 -1
- data/lib/action_dispatch/middleware/debug_locks.rb +122 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +2 -2
- data/lib/action_dispatch/middleware/ssl.rb +14 -5
- data/lib/action_dispatch/middleware/static.rb +3 -3
- data/lib/action_dispatch/routing/mapper.rb +74 -61
- data/lib/action_dispatch/routing/redirection.rb +0 -1
- data/lib/action_dispatch/testing/integration.rb +150 -196
- data/lib/action_dispatch/testing/request_encoder.rb +53 -0
- data/lib/action_dispatch/testing/test_process.rb +2 -1
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +7 -2
- data/lib/action_pack/gem_version.rb +2 -2
- metadata +13 -12
@@ -46,7 +46,7 @@ module ActionDispatch
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def call(env)
|
49
|
-
serve
|
49
|
+
serve(Rack::Request.new(env))
|
50
50
|
end
|
51
51
|
|
52
52
|
def serve(request)
|
@@ -82,7 +82,7 @@ module ActionDispatch
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def gzip_encoding_accepted?(request)
|
85
|
-
request.accept_encoding =~ /\bgzip\b/i
|
85
|
+
request.accept_encoding.any? { |enc, quality| enc =~ /\bgzip\b/i }
|
86
86
|
end
|
87
87
|
|
88
88
|
def gzip_file_path(path)
|
@@ -123,7 +123,7 @@ module ActionDispatch
|
|
123
123
|
end
|
124
124
|
|
125
125
|
def call(env)
|
126
|
-
req =
|
126
|
+
req = Rack::Request.new env
|
127
127
|
|
128
128
|
if req.get? || req.head?
|
129
129
|
path = req.path_info.chomp('/'.freeze)
|
@@ -716,11 +716,7 @@ module ActionDispatch
|
|
716
716
|
def map_method(method, args, &block)
|
717
717
|
options = args.extract_options!
|
718
718
|
options[:via] = method
|
719
|
-
|
720
|
-
defaults(options.delete(:defaults)) { match(*args, options, &block) }
|
721
|
-
else
|
722
|
-
match(*args, options, &block)
|
723
|
-
end
|
719
|
+
match(*args, options, &block)
|
724
720
|
self
|
725
721
|
end
|
726
722
|
end
|
@@ -1258,11 +1254,11 @@ module ActionDispatch
|
|
1258
1254
|
# the plural):
|
1259
1255
|
#
|
1260
1256
|
# GET /profile/new
|
1261
|
-
# POST /profile
|
1262
1257
|
# GET /profile
|
1263
1258
|
# GET /profile/edit
|
1264
1259
|
# PATCH/PUT /profile
|
1265
1260
|
# DELETE /profile
|
1261
|
+
# POST /profile
|
1266
1262
|
#
|
1267
1263
|
# === Options
|
1268
1264
|
# Takes same options as +resources+.
|
@@ -1280,15 +1276,15 @@ module ActionDispatch
|
|
1280
1276
|
|
1281
1277
|
concerns(options[:concerns]) if options[:concerns]
|
1282
1278
|
|
1283
|
-
collection do
|
1284
|
-
post :create
|
1285
|
-
end if parent_resource.actions.include?(:create)
|
1286
|
-
|
1287
1279
|
new do
|
1288
1280
|
get :new
|
1289
1281
|
end if parent_resource.actions.include?(:new)
|
1290
1282
|
|
1291
1283
|
set_member_mappings_for_resource
|
1284
|
+
|
1285
|
+
collection do
|
1286
|
+
post :create
|
1287
|
+
end if parent_resource.actions.include?(:create)
|
1292
1288
|
end
|
1293
1289
|
end
|
1294
1290
|
|
@@ -1566,11 +1562,17 @@ module ActionDispatch
|
|
1566
1562
|
# match 'path' => 'controller#action', via: patch
|
1567
1563
|
# match 'path', to: 'controller#action', via: :post
|
1568
1564
|
# match 'path', 'otherpath', on: :member, via: :get
|
1569
|
-
def match(path, *rest)
|
1565
|
+
def match(path, *rest, &block)
|
1570
1566
|
if rest.empty? && Hash === path
|
1571
1567
|
options = path
|
1572
1568
|
path, to = options.find { |name, _value| name.is_a?(String) }
|
1573
1569
|
|
1570
|
+
if path.nil?
|
1571
|
+
ActiveSupport::Deprecation.warn 'Omitting the route path is deprecated. '\
|
1572
|
+
'Specify the path with a String or a Symbol instead.'
|
1573
|
+
path = ''
|
1574
|
+
end
|
1575
|
+
|
1574
1576
|
case to
|
1575
1577
|
when Symbol
|
1576
1578
|
options[:action] = to
|
@@ -1591,55 +1593,11 @@ module ActionDispatch
|
|
1591
1593
|
paths = [path] + rest
|
1592
1594
|
end
|
1593
1595
|
|
1594
|
-
if options
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
if @scope[:to]
|
1599
|
-
options[:to] ||= @scope[:to]
|
1600
|
-
end
|
1601
|
-
|
1602
|
-
if @scope[:controller] && @scope[:action]
|
1603
|
-
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
|
1604
|
-
end
|
1605
|
-
|
1606
|
-
controller = options.delete(:controller) || @scope[:controller]
|
1607
|
-
option_path = options.delete :path
|
1608
|
-
to = options.delete :to
|
1609
|
-
via = Mapping.check_via Array(options.delete(:via) {
|
1610
|
-
@scope[:via]
|
1611
|
-
})
|
1612
|
-
formatted = options.delete(:format) { @scope[:format] }
|
1613
|
-
anchor = options.delete(:anchor) { true }
|
1614
|
-
options_constraints = options.delete(:constraints) || {}
|
1615
|
-
|
1616
|
-
path_types = paths.group_by(&:class)
|
1617
|
-
path_types.fetch(String, []).each do |_path|
|
1618
|
-
route_options = options.dup
|
1619
|
-
if _path && option_path
|
1620
|
-
ActiveSupport::Deprecation.warn <<-eowarn
|
1621
|
-
Specifying strings for both :path and the route path is deprecated. Change things like this:
|
1622
|
-
|
1623
|
-
match #{_path.inspect}, :path => #{option_path.inspect}
|
1624
|
-
|
1625
|
-
to this:
|
1626
|
-
|
1627
|
-
match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{path.inspect}
|
1628
|
-
eowarn
|
1629
|
-
route_options[:action] = _path
|
1630
|
-
route_options[:as] = _path
|
1631
|
-
_path = option_path
|
1632
|
-
end
|
1633
|
-
to = get_to_from_path(_path, to, route_options[:action])
|
1634
|
-
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
1635
|
-
end
|
1636
|
-
|
1637
|
-
path_types.fetch(Symbol, []).each do |action|
|
1638
|
-
route_options = options.dup
|
1639
|
-
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
1596
|
+
if options.key?(:defaults)
|
1597
|
+
defaults(options.delete(:defaults)) { map_match(paths, options, &block) }
|
1598
|
+
else
|
1599
|
+
map_match(paths, options, &block)
|
1640
1600
|
end
|
1641
|
-
|
1642
|
-
self
|
1643
1601
|
end
|
1644
1602
|
|
1645
1603
|
def get_to_from_path(path, to, action)
|
@@ -1915,6 +1873,7 @@ to this:
|
|
1915
1873
|
def api_only?
|
1916
1874
|
@set.api_only?
|
1917
1875
|
end
|
1876
|
+
|
1918
1877
|
private
|
1919
1878
|
|
1920
1879
|
def path_scope(path)
|
@@ -1924,9 +1883,63 @@ to this:
|
|
1924
1883
|
@scope = @scope.parent
|
1925
1884
|
end
|
1926
1885
|
|
1886
|
+
def map_match(paths, options)
|
1887
|
+
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
|
1888
|
+
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
|
1889
|
+
end
|
1890
|
+
|
1891
|
+
if @scope[:to]
|
1892
|
+
options[:to] ||= @scope[:to]
|
1893
|
+
end
|
1894
|
+
|
1895
|
+
if @scope[:controller] && @scope[:action]
|
1896
|
+
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
|
1897
|
+
end
|
1898
|
+
|
1899
|
+
controller = options.delete(:controller) || @scope[:controller]
|
1900
|
+
option_path = options.delete :path
|
1901
|
+
to = options.delete :to
|
1902
|
+
via = Mapping.check_via Array(options.delete(:via) {
|
1903
|
+
@scope[:via]
|
1904
|
+
})
|
1905
|
+
formatted = options.delete(:format) { @scope[:format] }
|
1906
|
+
anchor = options.delete(:anchor) { true }
|
1907
|
+
options_constraints = options.delete(:constraints) || {}
|
1908
|
+
|
1909
|
+
path_types = paths.group_by(&:class)
|
1910
|
+
path_types.fetch(String, []).each do |_path|
|
1911
|
+
route_options = options.dup
|
1912
|
+
if _path && option_path
|
1913
|
+
ActiveSupport::Deprecation.warn <<-eowarn
|
1914
|
+
Specifying strings for both :path and the route path is deprecated. Change things like this:
|
1915
|
+
|
1916
|
+
match #{_path.inspect}, :path => #{option_path.inspect}
|
1917
|
+
|
1918
|
+
to this:
|
1919
|
+
|
1920
|
+
match #{option_path.inspect}, :as => #{_path.inspect}, :action => #{_path.inspect}
|
1921
|
+
eowarn
|
1922
|
+
route_options[:action] = _path
|
1923
|
+
route_options[:as] = _path
|
1924
|
+
_path = option_path
|
1925
|
+
end
|
1926
|
+
to = get_to_from_path(_path, to, route_options[:action])
|
1927
|
+
decomposed_match(_path, controller, route_options, _path, to, via, formatted, anchor, options_constraints)
|
1928
|
+
end
|
1929
|
+
|
1930
|
+
path_types.fetch(Symbol, []).each do |action|
|
1931
|
+
route_options = options.dup
|
1932
|
+
decomposed_match(action, controller, route_options, option_path, to, via, formatted, anchor, options_constraints)
|
1933
|
+
end
|
1934
|
+
|
1935
|
+
self
|
1936
|
+
end
|
1937
|
+
|
1927
1938
|
def match_root_route(options)
|
1928
|
-
name = has_named_route?(:root) ? nil : :root
|
1929
|
-
|
1939
|
+
name = has_named_route?(name_for_action(:root, nil)) ? nil : :root
|
1940
|
+
args = ["/", { as: name, via: :get }.merge!(options)]
|
1941
|
+
|
1942
|
+
match(*args)
|
1930
1943
|
end
|
1931
1944
|
end
|
1932
1945
|
|
@@ -6,65 +6,40 @@ require 'active_support/core_ext/string/strip'
|
|
6
6
|
require 'rack/test'
|
7
7
|
require 'minitest'
|
8
8
|
|
9
|
+
require 'action_dispatch/testing/request_encoder'
|
10
|
+
|
9
11
|
module ActionDispatch
|
10
12
|
module Integration #:nodoc:
|
11
13
|
module RequestHelpers
|
12
|
-
# Performs a GET request with the given parameters.
|
13
|
-
#
|
14
|
-
# - +path+: The URI (as a String) on which you want to perform a GET
|
15
|
-
# request.
|
16
|
-
# - +params+: The HTTP parameters that you want to pass. This may
|
17
|
-
# be +nil+,
|
18
|
-
# a Hash, or a String that is appropriately encoded
|
19
|
-
# (<tt>application/x-www-form-urlencoded</tt> or
|
20
|
-
# <tt>multipart/form-data</tt>).
|
21
|
-
# - +headers+: Additional headers to pass, as a Hash. The headers will be
|
22
|
-
# merged into the Rack env hash.
|
23
|
-
# - +env+: Additional env to pass, as a Hash. The headers will be
|
24
|
-
# merged into the Rack env hash.
|
25
|
-
#
|
26
|
-
# This method returns a Response object, which one can use to
|
27
|
-
# inspect the details of the response. Furthermore, if this method was
|
28
|
-
# called from an ActionDispatch::IntegrationTest object, then that
|
29
|
-
# object's <tt>@response</tt> instance variable will point to the same
|
30
|
-
# response object.
|
31
|
-
#
|
32
|
-
# You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
|
33
|
-
# +#post+, +#patch+, +#put+, +#delete+, and +#head+.
|
34
|
-
#
|
35
|
-
# Example:
|
36
|
-
#
|
37
|
-
# get '/feed', params: { since: 201501011400 }
|
38
|
-
# post '/profile', headers: { "X-Test-Header" => "testvalue" }
|
39
14
|
def get(path, *args)
|
40
15
|
process_with_kwargs(:get, path, *args)
|
41
16
|
end
|
42
17
|
|
43
|
-
# Performs a POST request with the given parameters. See +#
|
18
|
+
# Performs a POST request with the given parameters. See +#process+ for more
|
44
19
|
# details.
|
45
20
|
def post(path, *args)
|
46
21
|
process_with_kwargs(:post, path, *args)
|
47
22
|
end
|
48
23
|
|
49
|
-
# Performs a PATCH request with the given parameters. See +#
|
24
|
+
# Performs a PATCH request with the given parameters. See +#process+ for more
|
50
25
|
# details.
|
51
26
|
def patch(path, *args)
|
52
27
|
process_with_kwargs(:patch, path, *args)
|
53
28
|
end
|
54
29
|
|
55
|
-
# Performs a PUT request with the given parameters. See +#
|
30
|
+
# Performs a PUT request with the given parameters. See +#process+ for more
|
56
31
|
# details.
|
57
32
|
def put(path, *args)
|
58
33
|
process_with_kwargs(:put, path, *args)
|
59
34
|
end
|
60
35
|
|
61
|
-
# Performs a DELETE request with the given parameters. See +#
|
36
|
+
# Performs a DELETE request with the given parameters. See +#process+ for
|
62
37
|
# more details.
|
63
38
|
def delete(path, *args)
|
64
39
|
process_with_kwargs(:delete, path, *args)
|
65
40
|
end
|
66
41
|
|
67
|
-
# Performs a HEAD request with the given parameters. See +#
|
42
|
+
# Performs a HEAD request with the given parameters. See +#process+ for more
|
68
43
|
# details.
|
69
44
|
def head(path, *args)
|
70
45
|
process_with_kwargs(:head, path, *args)
|
@@ -79,26 +54,14 @@ module ActionDispatch
|
|
79
54
|
#
|
80
55
|
# Example:
|
81
56
|
#
|
82
|
-
# xhr :get, '/feed',
|
83
|
-
def xml_http_request(request_method, path,
|
84
|
-
if kwarg_request?(args)
|
85
|
-
params, headers, env = args.first.values_at(:params, :headers, :env)
|
86
|
-
else
|
87
|
-
params = args[0]
|
88
|
-
headers = args[1]
|
89
|
-
env = {}
|
90
|
-
|
91
|
-
if params.present? || headers.present?
|
92
|
-
non_kwarg_request_warning
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
57
|
+
# xhr :get, '/feed', since: 201501011400
|
58
|
+
def xml_http_request(request_method, path, parameters = nil, headers_or_env = nil)
|
96
59
|
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
97
|
-
xhr and xml_http_request
|
98
|
-
`
|
60
|
+
`xhr` and `xml_http_request` are deprecated and will be removed in Rails 5.1.
|
61
|
+
Switch to e.g. `post comments_path, params: { comment: { body: 'Honey bunny' } }, xhr: true`.
|
99
62
|
MSG
|
100
63
|
|
101
|
-
process(request_method, path, params:
|
64
|
+
process(request_method, path, params: parameters, headers: headers_or_env, xhr: true)
|
102
65
|
end
|
103
66
|
alias xhr :xml_http_request
|
104
67
|
|
@@ -177,7 +140,7 @@ module ActionDispatch
|
|
177
140
|
DEFAULT_HOST = "www.example.com"
|
178
141
|
|
179
142
|
include Minitest::Assertions
|
180
|
-
include
|
143
|
+
include RequestHelpers, Assertions
|
181
144
|
|
182
145
|
%w( status status_message headers body redirect? ).each do |method|
|
183
146
|
delegate method, :to => :response, :allow_nil => true
|
@@ -281,6 +244,108 @@ module ActionDispatch
|
|
281
244
|
@https
|
282
245
|
end
|
283
246
|
|
247
|
+
# Performs the actual request.
|
248
|
+
#
|
249
|
+
# - +method+: The HTTP method (GET, POST, PATCH, PUT, DELETE, HEAD, OPTIONS)
|
250
|
+
# as a symbol.
|
251
|
+
# - +path+: The URI (as a String) on which you want to perform the
|
252
|
+
# request.
|
253
|
+
# - +params+: The HTTP parameters that you want to pass. This may
|
254
|
+
# be +nil+,
|
255
|
+
# a Hash, or a String that is appropriately encoded
|
256
|
+
# (<tt>application/x-www-form-urlencoded</tt> or
|
257
|
+
# <tt>multipart/form-data</tt>).
|
258
|
+
# - +headers+: Additional headers to pass, as a Hash. The headers will be
|
259
|
+
# merged into the Rack env hash.
|
260
|
+
# - +env+: Additional env to pass, as a Hash. The headers will be
|
261
|
+
# merged into the Rack env hash.
|
262
|
+
#
|
263
|
+
# This method is rarely used directly. Use +#get+, +#post+, or other standard
|
264
|
+
# HTTP methods in integration tests. +#process+ is only required when using a
|
265
|
+
# request method that doesn't have a method defined in the integrations tests.
|
266
|
+
#
|
267
|
+
# This method returns a Response object, which one can use to
|
268
|
+
# inspect the details of the response. Furthermore, if this method was
|
269
|
+
# called from an ActionDispatch::IntegrationTest object, then that
|
270
|
+
# object's <tt>@response</tt> instance variable will point to the same
|
271
|
+
# response object.
|
272
|
+
#
|
273
|
+
# Example:
|
274
|
+
#
|
275
|
+
# process :get, '/author', params: { since: 201501011400 }
|
276
|
+
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
277
|
+
request_encoder = RequestEncoder.encoder(as)
|
278
|
+
headers ||= {}
|
279
|
+
|
280
|
+
if method == :get && as == :json && params
|
281
|
+
headers['X-Http-Method-Override'] = 'GET'
|
282
|
+
method = :post
|
283
|
+
end
|
284
|
+
|
285
|
+
if path =~ %r{://}
|
286
|
+
location = URI.parse(path)
|
287
|
+
https! URI::HTTPS === location if location.scheme
|
288
|
+
if url_host = location.host
|
289
|
+
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
290
|
+
url_host += ":#{location.port}" if default != location.port
|
291
|
+
host! url_host
|
292
|
+
end
|
293
|
+
path = location.query ? "#{location.path}?#{location.query}" : location.path
|
294
|
+
end
|
295
|
+
|
296
|
+
hostname, port = host.split(':')
|
297
|
+
|
298
|
+
request_env = {
|
299
|
+
:method => method,
|
300
|
+
:params => request_encoder.encode_params(params),
|
301
|
+
|
302
|
+
"SERVER_NAME" => hostname,
|
303
|
+
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
304
|
+
"HTTPS" => https? ? "on" : "off",
|
305
|
+
"rack.url_scheme" => https? ? "https" : "http",
|
306
|
+
|
307
|
+
"REQUEST_URI" => path,
|
308
|
+
"HTTP_HOST" => host,
|
309
|
+
"REMOTE_ADDR" => remote_addr,
|
310
|
+
"CONTENT_TYPE" => request_encoder.content_type,
|
311
|
+
"HTTP_ACCEPT" => request_encoder.accept_header || accept
|
312
|
+
}
|
313
|
+
|
314
|
+
wrapped_headers = Http::Headers.from_hash({})
|
315
|
+
wrapped_headers.merge!(headers) if headers
|
316
|
+
|
317
|
+
if xhr
|
318
|
+
wrapped_headers["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"
|
319
|
+
wrapped_headers["HTTP_ACCEPT"] ||= [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
|
320
|
+
end
|
321
|
+
|
322
|
+
# this modifies the passed request_env directly
|
323
|
+
if wrapped_headers.present?
|
324
|
+
Http::Headers.from_hash(request_env).merge!(wrapped_headers)
|
325
|
+
end
|
326
|
+
if env.present?
|
327
|
+
Http::Headers.from_hash(request_env).merge!(env)
|
328
|
+
end
|
329
|
+
|
330
|
+
session = Rack::Test::Session.new(_mock_session)
|
331
|
+
|
332
|
+
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
333
|
+
# Make sure requested path is always a full uri
|
334
|
+
session.request(build_full_uri(path, request_env), request_env)
|
335
|
+
|
336
|
+
@request_count += 1
|
337
|
+
@request = ActionDispatch::Request.new(session.last_request.env)
|
338
|
+
response = _mock_session.last_response
|
339
|
+
@response = ActionDispatch::TestResponse.from_response(response)
|
340
|
+
@response.request = @request
|
341
|
+
@html_document = nil
|
342
|
+
@url_options = nil
|
343
|
+
|
344
|
+
@controller = @request.controller_instance
|
345
|
+
|
346
|
+
response.status
|
347
|
+
end
|
348
|
+
|
284
349
|
# Set the host name to use in the next request.
|
285
350
|
#
|
286
351
|
# session.host! "www.example.com"
|
@@ -307,144 +372,20 @@ module ActionDispatch
|
|
307
372
|
|
308
373
|
def non_kwarg_request_warning
|
309
374
|
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
310
|
-
|
311
|
-
|
312
|
-
#{REQUEST_KWARGS.join(', ')}
|
313
|
-
|
314
|
-
Examples:
|
315
|
-
|
316
|
-
get '/profile',
|
317
|
-
params: { id: 1 },
|
318
|
-
headers: { 'X-Extra-Header' => '123' },
|
319
|
-
env: { 'action_dispatch.custom' => 'custom' },
|
320
|
-
xhr: true,
|
321
|
-
as: :json
|
322
|
-
MSG
|
323
|
-
end
|
324
|
-
|
325
|
-
# Performs the actual request.
|
326
|
-
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
327
|
-
request_encoder = RequestEncoder.encoder(as)
|
328
|
-
|
329
|
-
if path =~ %r{://}
|
330
|
-
location = URI.parse(path)
|
331
|
-
https! URI::HTTPS === location if location.scheme
|
332
|
-
if url_host = location.host
|
333
|
-
default = Rack::Request::DEFAULT_PORTS[location.scheme]
|
334
|
-
url_host += ":#{location.port}" if default != location.port
|
335
|
-
host! url_host
|
336
|
-
end
|
337
|
-
path = request_encoder.append_format_to location.path
|
338
|
-
path = location.query ? "#{path}?#{location.query}" : path
|
339
|
-
else
|
340
|
-
path = request_encoder.append_format_to path
|
341
|
-
end
|
342
|
-
|
343
|
-
hostname, port = host.split(':')
|
344
|
-
|
345
|
-
request_env = {
|
346
|
-
:method => method,
|
347
|
-
:params => request_encoder.encode_params(params),
|
348
|
-
|
349
|
-
"SERVER_NAME" => hostname,
|
350
|
-
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
351
|
-
"HTTPS" => https? ? "on" : "off",
|
352
|
-
"rack.url_scheme" => https? ? "https" : "http",
|
353
|
-
|
354
|
-
"REQUEST_URI" => path,
|
355
|
-
"HTTP_HOST" => host,
|
356
|
-
"REMOTE_ADDR" => remote_addr,
|
357
|
-
"CONTENT_TYPE" => request_encoder.content_type,
|
358
|
-
"HTTP_ACCEPT" => accept
|
359
|
-
}
|
360
|
-
|
361
|
-
if xhr
|
362
|
-
headers ||= {}
|
363
|
-
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
364
|
-
headers['HTTP_ACCEPT'] ||= [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
|
365
|
-
end
|
366
|
-
|
367
|
-
# this modifies the passed request_env directly
|
368
|
-
if headers.present?
|
369
|
-
Http::Headers.from_hash(request_env).merge!(headers)
|
370
|
-
end
|
371
|
-
if env.present?
|
372
|
-
Http::Headers.from_hash(request_env).merge!(env)
|
373
|
-
end
|
374
|
-
|
375
|
-
session = Rack::Test::Session.new(_mock_session)
|
376
|
-
|
377
|
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
|
378
|
-
# Make sure requested path is always a full uri
|
379
|
-
session.request(build_full_uri(path, request_env), request_env)
|
380
|
-
|
381
|
-
@request_count += 1
|
382
|
-
@request = ActionDispatch::Request.new(session.last_request.env)
|
383
|
-
response = _mock_session.last_response
|
384
|
-
@response = ActionDispatch::TestResponse.from_response(response)
|
385
|
-
@response.request = @request
|
386
|
-
@response.response_parser = RequestEncoder.parser(@response.content_type)
|
387
|
-
@html_document = nil
|
388
|
-
@url_options = nil
|
375
|
+
Using positional arguments in integration tests has been deprecated,
|
376
|
+
in favor of keyword arguments, and will be removed in Rails 5.1.
|
389
377
|
|
390
|
-
|
378
|
+
Deprecated style:
|
379
|
+
get "/profile", { id: 1 }, { "X-Extra-Header" => "123" }
|
391
380
|
|
392
|
-
|
381
|
+
New keyword style:
|
382
|
+
get "/profile", params: { id: 1 }, headers: { "X-Extra-Header" => "123" }
|
383
|
+
MSG
|
393
384
|
end
|
394
385
|
|
395
386
|
def build_full_uri(path, env)
|
396
387
|
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
|
397
388
|
end
|
398
|
-
|
399
|
-
class RequestEncoder # :nodoc:
|
400
|
-
@encoders = {}
|
401
|
-
|
402
|
-
attr_reader :response_parser
|
403
|
-
|
404
|
-
def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
|
405
|
-
@mime = Mime[mime_name]
|
406
|
-
|
407
|
-
unless @mime
|
408
|
-
raise ArgumentError, "Can't register a request encoder for " \
|
409
|
-
"unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
|
410
|
-
end
|
411
|
-
|
412
|
-
@url_encoded_form = url_encoded_form
|
413
|
-
@path_format = ".#{@mime.symbol}" unless @url_encoded_form
|
414
|
-
@response_parser = response_parser || -> body { body }
|
415
|
-
@param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
|
416
|
-
end
|
417
|
-
|
418
|
-
def append_format_to(path)
|
419
|
-
path << @path_format unless @url_encoded_form
|
420
|
-
path
|
421
|
-
end
|
422
|
-
|
423
|
-
def content_type
|
424
|
-
@mime.to_s
|
425
|
-
end
|
426
|
-
|
427
|
-
def encode_params(params)
|
428
|
-
@param_encoder.call(params)
|
429
|
-
end
|
430
|
-
|
431
|
-
def self.parser(content_type)
|
432
|
-
mime = Mime::Type.lookup(content_type)
|
433
|
-
encoder(mime ? mime.ref : nil).response_parser
|
434
|
-
end
|
435
|
-
|
436
|
-
def self.encoder(name)
|
437
|
-
@encoders[name] || WWWFormEncoder
|
438
|
-
end
|
439
|
-
|
440
|
-
def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
|
441
|
-
@encoders[mime_name] = new(mime_name, param_encoder, response_parser)
|
442
|
-
end
|
443
|
-
|
444
|
-
register_encoder :json, response_parser: -> body { JSON.parse(body) }
|
445
|
-
|
446
|
-
WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
|
447
|
-
end
|
448
389
|
end
|
449
390
|
|
450
391
|
module Runner
|
@@ -512,6 +453,7 @@ module ActionDispatch
|
|
512
453
|
# simultaneously.
|
513
454
|
def open_session
|
514
455
|
dup.tap do |session|
|
456
|
+
session.reset!
|
515
457
|
yield session if block_given?
|
516
458
|
end
|
517
459
|
end
|
@@ -701,42 +643,50 @@ module ActionDispatch
|
|
701
643
|
# end
|
702
644
|
# end
|
703
645
|
#
|
646
|
+
# See the {request helpers documentation}[rdoc-ref:ActionDispatch::Integration::RequestHelpers] for help on how to
|
647
|
+
# use +get+, etc.
|
648
|
+
#
|
649
|
+
# === Changing the request encoding
|
650
|
+
#
|
704
651
|
# You can also test your JSON API easily by setting what the request should
|
705
652
|
# be encoded as:
|
706
653
|
#
|
707
|
-
# require
|
654
|
+
# require "test_helper"
|
708
655
|
#
|
709
656
|
# class ApiTest < ActionDispatch::IntegrationTest
|
710
|
-
# test
|
657
|
+
# test "creates articles" do
|
711
658
|
# assert_difference -> { Article.count } do
|
712
|
-
# post articles_path, params: { article: { title:
|
659
|
+
# post articles_path, params: { article: { title: "Ahoy!" } }, as: :json
|
713
660
|
# end
|
714
661
|
#
|
715
662
|
# assert_response :success
|
716
|
-
# assert_equal({ id: Arcticle.last.id, title:
|
663
|
+
# assert_equal({ id: Arcticle.last.id, title: "Ahoy!" }, response.parsed_body)
|
717
664
|
# end
|
718
665
|
# end
|
719
666
|
#
|
720
|
-
# The
|
721
|
-
#
|
667
|
+
# The +as+ option passes an "application/json" Accept header (thereby setting
|
668
|
+
# the request format to JSON unless overridden), sets the content type to
|
669
|
+
# "application/json" and encodes the parameters as JSON.
|
722
670
|
#
|
723
|
-
# Calling
|
724
|
-
#
|
725
|
-
# it's the same as calling `body`.
|
671
|
+
# Calling +parsed_body+ on the response parses the response body based on the
|
672
|
+
# last response MIME type.
|
726
673
|
#
|
727
|
-
#
|
674
|
+
# Out of the box, only <tt>:json</tt> is supported. But for any custom MIME
|
675
|
+
# types you've registered, you can add your own encoders with:
|
728
676
|
#
|
729
677
|
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
730
678
|
# param_encoder: -> params { params.to_wibble },
|
731
679
|
# response_parser: -> body { body }
|
732
680
|
#
|
733
|
-
# Where
|
734
|
-
#
|
735
|
-
#
|
681
|
+
# Where +param_encoder+ defines how the params should be encoded and
|
682
|
+
# +response_parser+ defines how the response body should be parsed through
|
683
|
+
# +parsed_body+.
|
736
684
|
#
|
737
685
|
# Consult the Rails Testing Guide for more.
|
738
686
|
|
739
687
|
class IntegrationTest < ActiveSupport::TestCase
|
688
|
+
include TestProcess
|
689
|
+
|
740
690
|
module UrlOptions
|
741
691
|
extend ActiveSupport::Concern
|
742
692
|
def url_options
|
@@ -759,7 +709,11 @@ module ActionDispatch
|
|
759
709
|
|
760
710
|
module ClassMethods
|
761
711
|
def app
|
762
|
-
defined?(@@app)
|
712
|
+
if defined?(@@app) && @@app
|
713
|
+
@@app
|
714
|
+
else
|
715
|
+
ActionDispatch.test_app
|
716
|
+
end
|
763
717
|
end
|
764
718
|
|
765
719
|
def app=(app)
|
@@ -767,7 +721,7 @@ module ActionDispatch
|
|
767
721
|
end
|
768
722
|
|
769
723
|
def register_encoder(*args)
|
770
|
-
|
724
|
+
RequestEncoder.register_encoder(*args)
|
771
725
|
end
|
772
726
|
end
|
773
727
|
|