actionpack 5.0.0.beta2 → 5.0.0.beta3
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 +70 -5
- data/lib/abstract_controller.rb +6 -0
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +3 -8
- data/lib/action_controller.rb +4 -6
- data/lib/action_controller/caching.rb +12 -55
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal.rb +1 -4
- data/lib/action_controller/metal/instrumentation.rb +2 -2
- data/lib/action_controller/metal/live.rb +41 -25
- data/lib/action_controller/metal/request_forgery_protection.rb +3 -1
- data/lib/action_controller/metal/strong_parameters.rb +22 -22
- data/lib/action_controller/test_case.rb +14 -3
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +15 -6
- data/lib/action_dispatch/http/request.rb +4 -0
- data/lib/action_dispatch/http/response.rb +1 -0
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +0 -3
- data/lib/action_dispatch/middleware/cookies.rb +6 -1
- data/lib/action_dispatch/middleware/debug_exceptions.rb +10 -5
- data/lib/action_dispatch/middleware/params_parser.rb +1 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/request/session.rb +10 -16
- data/lib/action_dispatch/routing.rb +2 -3
- data/lib/action_dispatch/routing/inspector.rb +4 -3
- data/lib/action_dispatch/routing/mapper.rb +3 -1
- data/lib/action_dispatch/routing/route_set.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +1 -1
- data/lib/action_dispatch/testing/assertions/routing.rb +1 -0
- data/lib/action_dispatch/testing/integration.rb +98 -5
- data/lib/action_dispatch/testing/test_response.rb +6 -0
- data/lib/action_pack/gem_version.rb +1 -1
- metadata +11 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2847271b11f9c0787c4d0468c83fac6b307cbce
|
4
|
+
data.tar.gz: b8df093bb286c2d9160a70cfd9d22a8343affcad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d456ad1bbbd96908af0c0febd7922af482d88e315b83e3debba4aafe603fa3d531481c63b4e88a973b7a1910d93462e181a1cb780715aadc6d7e2677594a411
|
7
|
+
data.tar.gz: 27395600e033a142f00a7550bd97d96003af16a9c81e5c1e128759e50cc5c6ca21be046f5fa7e6c77234778eb670e810c3ee9d40c7d2d22e022b5e032a2a9a19
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,75 @@
|
|
1
|
+
## Rails 5.0.0.beta3 (February 24, 2016) ##
|
2
|
+
|
3
|
+
* Update session to have indifferent access across multiple requests.
|
4
|
+
|
5
|
+
session[:deep][:hash] = "Magic"
|
6
|
+
|
7
|
+
session[:deep][:hash] == "Magic"
|
8
|
+
session[:deep]["hash"] == "Magic"
|
9
|
+
|
10
|
+
*Tom Prats*
|
11
|
+
|
12
|
+
* Add application/gzip as a default mime type.
|
13
|
+
|
14
|
+
*Mehmet Emin İNAÇ*
|
15
|
+
|
16
|
+
* Add request encoding and response parsing to integration tests.
|
17
|
+
|
18
|
+
What previously was:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'test_helper'
|
22
|
+
|
23
|
+
class ApiTest < ActionDispatch::IntegrationTest
|
24
|
+
test 'creates articles' do
|
25
|
+
assert_difference -> { Article.count } do
|
26
|
+
post articles_path(format: :json),
|
27
|
+
params: { article: { title: 'Ahoy!' } }.to_json,
|
28
|
+
headers: { 'Content-Type' => 'application/json' }
|
29
|
+
end
|
30
|
+
|
31
|
+
assert_equal({ 'id' => Article.last.id, 'title' => 'Ahoy!' }, JSON.parse(response.body))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
Can now be written as:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
require 'test_helper'
|
40
|
+
|
41
|
+
class ApiTest < ActionDispatch::IntegrationTest
|
42
|
+
test 'creates articles' do
|
43
|
+
assert_difference -> { Article.count } do
|
44
|
+
post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
|
45
|
+
end
|
46
|
+
|
47
|
+
assert_equal({ 'id' => Article.last.id, 'title' => 'Ahoy!' }, response.parsed_body)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
Passing `as: :json` to integration test request helpers will set the format,
|
53
|
+
content type and encode the parameters as JSON.
|
54
|
+
|
55
|
+
Then on the response side, `parsed_body` will parse the body according to the
|
56
|
+
content type the response has.
|
57
|
+
|
58
|
+
Currently JSON is the only supported MIME type. Add your own with
|
59
|
+
`ActionDispatch::IntegrationTest.register_encoder`.
|
60
|
+
|
61
|
+
*Kasper Timm Hansen*
|
62
|
+
|
63
|
+
* Add image/svg+xml as a default mime type.
|
64
|
+
|
65
|
+
*DHH*
|
66
|
+
|
1
67
|
## Rails 5.0.0.beta2 (February 01, 2016) ##
|
2
68
|
|
3
|
-
* Add `-g` and `-c`
|
4
|
-
to `bin/rake routes`. These options return the url `name`, `verb` and
|
69
|
+
* Add `-g` and `-c` options to `bin/rails routes`. These options return the url `name`, `verb` and
|
5
70
|
`path` field that match the pattern or match a specific controller.
|
6
71
|
|
7
|
-
Deprecate `CONTROLLER` env variable in `bin/
|
72
|
+
Deprecate `CONTROLLER` env variable in `bin/rails routes`.
|
8
73
|
|
9
74
|
See #18902.
|
10
75
|
|
@@ -183,11 +248,11 @@
|
|
183
248
|
* Accessing mime types via constants like `Mime::HTML` is deprecated. Please
|
184
249
|
change code like this:
|
185
250
|
|
186
|
-
|
251
|
+
Mime::HTML
|
187
252
|
|
188
253
|
To this:
|
189
254
|
|
190
|
-
|
255
|
+
Mime[:html]
|
191
256
|
|
192
257
|
This change is so that Rails will not manage a list of constants, and fixes
|
193
258
|
an issue where if a type isn't registered you could possibly get the wrong
|
data/lib/abstract_controller.rb
CHANGED
@@ -6,6 +6,7 @@ module AbstractController
|
|
6
6
|
extend ActiveSupport::Autoload
|
7
7
|
|
8
8
|
autoload :Base
|
9
|
+
autoload :Caching
|
9
10
|
autoload :Callbacks
|
10
11
|
autoload :Collector
|
11
12
|
autoload :DoubleRenderError, "abstract_controller/rendering"
|
@@ -15,4 +16,9 @@ module AbstractController
|
|
15
16
|
autoload :Translation
|
16
17
|
autoload :AssetPaths
|
17
18
|
autoload :UrlFor
|
19
|
+
|
20
|
+
def self.eager_load!
|
21
|
+
super
|
22
|
+
AbstractController::Caching.eager_load!
|
23
|
+
end
|
18
24
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module AbstractController
|
2
|
+
module Caching
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
extend ActiveSupport::Autoload
|
5
|
+
|
6
|
+
eager_autoload do
|
7
|
+
autoload :Fragments
|
8
|
+
end
|
9
|
+
|
10
|
+
module ConfigMethods
|
11
|
+
def cache_store
|
12
|
+
config.cache_store
|
13
|
+
end
|
14
|
+
|
15
|
+
def cache_store=(store)
|
16
|
+
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def cache_configured?
|
21
|
+
perform_caching && cache_store
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
include ConfigMethods
|
26
|
+
include AbstractController::Caching::Fragments
|
27
|
+
|
28
|
+
included do
|
29
|
+
extend ConfigMethods
|
30
|
+
|
31
|
+
config_accessor :default_static_extension
|
32
|
+
self.default_static_extension ||= '.html'
|
33
|
+
|
34
|
+
config_accessor :perform_caching
|
35
|
+
self.perform_caching = true if perform_caching.nil?
|
36
|
+
|
37
|
+
class_attribute :_view_cache_dependencies
|
38
|
+
self._view_cache_dependencies = []
|
39
|
+
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def view_cache_dependency(&dependency)
|
44
|
+
self._view_cache_dependencies += [dependency]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def view_cache_dependencies
|
49
|
+
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
# Convenience accessor.
|
54
|
+
def cache(key, options = {}, &block)
|
55
|
+
if cache_configured?
|
56
|
+
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
57
|
+
else
|
58
|
+
yield
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module AbstractController
|
2
2
|
module Caching
|
3
3
|
# Fragment caching is used for caching various blocks within
|
4
4
|
# views without caching the entire action as a whole. This is
|
@@ -135,13 +135,8 @@ module ActionController
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def instrument_fragment_cache(name, key) # :nodoc:
|
138
|
-
payload =
|
139
|
-
|
140
|
-
action: action_name,
|
141
|
-
key: key
|
142
|
-
}
|
143
|
-
|
144
|
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield }
|
138
|
+
payload = instrument_payload(key)
|
139
|
+
ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield }
|
145
140
|
end
|
146
141
|
end
|
147
142
|
end
|
data/lib/action_controller.rb
CHANGED
@@ -9,12 +9,15 @@ module ActionController
|
|
9
9
|
|
10
10
|
autoload :API
|
11
11
|
autoload :Base
|
12
|
-
autoload :Caching
|
13
12
|
autoload :Metal
|
14
13
|
autoload :Middleware
|
15
14
|
autoload :Renderer
|
16
15
|
autoload :FormBuilder
|
17
16
|
|
17
|
+
eager_autoload do
|
18
|
+
autoload :Caching
|
19
|
+
end
|
20
|
+
|
18
21
|
autoload_under "metal" do
|
19
22
|
autoload :ConditionalGet
|
20
23
|
autoload :Cookies
|
@@ -47,11 +50,6 @@ module ActionController
|
|
47
50
|
|
48
51
|
autoload :TestCase, 'action_controller/test_case'
|
49
52
|
autoload :TemplateAssertions, 'action_controller/test_case'
|
50
|
-
|
51
|
-
def self.eager_load!
|
52
|
-
super
|
53
|
-
ActionController::Caching.eager_load!
|
54
|
-
end
|
55
53
|
end
|
56
54
|
|
57
55
|
# Common Active Support usage in Action Controller
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'fileutils'
|
2
|
-
require 'uri'
|
3
|
-
|
4
1
|
module ActionController
|
5
2
|
# \Caching is a cheap way of speeding up slow applications by keeping the result of
|
6
3
|
# calculations, renderings, and database calls around for subsequent requests.
|
@@ -23,65 +20,25 @@ module ActionController
|
|
23
20
|
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
|
24
21
|
# config.action_controller.cache_store = MyOwnStore.new('parameter')
|
25
22
|
module Caching
|
26
|
-
extend ActiveSupport::Concern
|
27
23
|
extend ActiveSupport::Autoload
|
28
|
-
|
29
|
-
eager_autoload do
|
30
|
-
autoload :Fragments
|
31
|
-
end
|
32
|
-
|
33
|
-
module ConfigMethods
|
34
|
-
def cache_store
|
35
|
-
config.cache_store
|
36
|
-
end
|
37
|
-
|
38
|
-
def cache_store=(store)
|
39
|
-
config.cache_store = ActiveSupport::Cache.lookup_store(store)
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
def cache_configured?
|
44
|
-
perform_caching && cache_store
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
include AbstractController::Callbacks
|
49
|
-
|
50
|
-
include ConfigMethods
|
51
|
-
include Fragments
|
24
|
+
extend ActiveSupport::Concern
|
52
25
|
|
53
26
|
included do
|
54
|
-
|
55
|
-
|
56
|
-
config_accessor :default_static_extension
|
57
|
-
self.default_static_extension ||= '.html'
|
58
|
-
|
59
|
-
config_accessor :perform_caching
|
60
|
-
self.perform_caching = true if perform_caching.nil?
|
61
|
-
|
62
|
-
class_attribute :_view_cache_dependencies
|
63
|
-
self._view_cache_dependencies = []
|
64
|
-
helper_method :view_cache_dependencies if respond_to?(:helper_method)
|
27
|
+
include AbstractController::Caching
|
65
28
|
end
|
66
29
|
|
67
|
-
|
68
|
-
def view_cache_dependency(&dependency)
|
69
|
-
self._view_cache_dependencies += [dependency]
|
70
|
-
end
|
71
|
-
end
|
30
|
+
private
|
72
31
|
|
73
|
-
|
74
|
-
|
75
|
-
|
32
|
+
def instrument_payload(key)
|
33
|
+
{
|
34
|
+
controller: controller_name,
|
35
|
+
action: action_name,
|
36
|
+
key: key
|
37
|
+
}
|
38
|
+
end
|
76
39
|
|
77
|
-
|
78
|
-
|
79
|
-
def cache(key, options = {}, &block)
|
80
|
-
if cache_configured?
|
81
|
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
|
82
|
-
else
|
83
|
-
yield
|
84
|
-
end
|
40
|
+
def instrument_name
|
41
|
+
"action_controller"
|
85
42
|
end
|
86
43
|
end
|
87
44
|
end
|
@@ -25,7 +25,9 @@ module ActionController
|
|
25
25
|
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
26
26
|
end
|
27
27
|
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
|
28
|
-
message << " (#{additions.join(" | ".freeze)})" unless additions.
|
28
|
+
message << " (#{additions.join(" | ".freeze)})" unless additions.empty?
|
29
|
+
message << "\n\n" if defined?(Rails.env) && Rails.env.development?
|
30
|
+
|
29
31
|
message
|
30
32
|
end
|
31
33
|
end
|
@@ -175,10 +175,7 @@ module ActionController
|
|
175
175
|
body = [body] unless body.nil? || body.respond_to?(:each)
|
176
176
|
response.reset_body!
|
177
177
|
return unless body
|
178
|
-
body
|
179
|
-
next if part.empty?
|
180
|
-
response.write part
|
181
|
-
}
|
178
|
+
response.body = body
|
182
179
|
super
|
183
180
|
end
|
184
181
|
|
@@ -19,9 +19,9 @@ module ActionController
|
|
19
19
|
:controller => self.class.name,
|
20
20
|
:action => self.action_name,
|
21
21
|
:params => request.filtered_parameters,
|
22
|
-
:format => request.format.
|
22
|
+
:format => request.format.ref,
|
23
23
|
:method => request.request_method,
|
24
|
-
:path =>
|
24
|
+
:path => request.fullpath
|
25
25
|
}
|
26
26
|
|
27
27
|
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
|
@@ -237,39 +237,55 @@ module ActionController
|
|
237
237
|
# This processes the action in a child thread. It lets us return the
|
238
238
|
# response code and headers back up the rack stack, and still process
|
239
239
|
# the body in parallel with sending data to the client
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
240
|
+
new_controller_thread {
|
241
|
+
ActiveSupport::Dependencies.interlock.running do
|
242
|
+
t2 = Thread.current
|
243
|
+
|
244
|
+
# Since we're processing the view in a different thread, copy the
|
245
|
+
# thread locals from the main thread to the child thread. :'(
|
246
|
+
locals.each { |k,v| t2[k] = v }
|
247
|
+
|
248
|
+
begin
|
249
|
+
super(name)
|
250
|
+
rescue => e
|
251
|
+
if @_response.committed?
|
252
|
+
begin
|
253
|
+
@_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
|
254
|
+
@_response.stream.call_on_error
|
255
|
+
rescue => exception
|
256
|
+
log_error(exception)
|
257
|
+
ensure
|
258
|
+
log_error(e)
|
259
|
+
@_response.stream.close
|
260
|
+
end
|
261
|
+
else
|
262
|
+
error = e
|
260
263
|
end
|
261
|
-
|
262
|
-
|
264
|
+
ensure
|
265
|
+
@_response.commit!
|
263
266
|
end
|
264
|
-
ensure
|
265
|
-
@_response.commit!
|
266
267
|
end
|
267
268
|
}
|
268
269
|
|
269
|
-
|
270
|
+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
271
|
+
@_response.await_commit
|
272
|
+
end
|
273
|
+
|
270
274
|
raise error if error
|
271
275
|
end
|
272
276
|
|
277
|
+
# Spawn a new thread to serve up the controller in. This is to get
|
278
|
+
# around the fact that Rack isn't based around IOs and we need to use
|
279
|
+
# a thread to stream data from the response bodies. Nobody should call
|
280
|
+
# this method except in Rails internals. Seriously!
|
281
|
+
def new_controller_thread # :nodoc:
|
282
|
+
Thread.new {
|
283
|
+
t2 = Thread.current
|
284
|
+
t2.abort_on_exception = true
|
285
|
+
yield
|
286
|
+
}
|
287
|
+
end
|
288
|
+
|
273
289
|
def log_error(exception)
|
274
290
|
logger = ActionController::Base.logger
|
275
291
|
return unless logger
|
@@ -378,7 +378,9 @@ module ActionController #:nodoc:
|
|
378
378
|
end
|
379
379
|
|
380
380
|
def xor_byte_strings(s1, s2)
|
381
|
-
|
381
|
+
s2_bytes = s2.bytes
|
382
|
+
s1.each_byte.with_index { |c1, i| s2_bytes[i] ^= c1 }
|
383
|
+
s2_bytes.pack('C*')
|
382
384
|
end
|
383
385
|
|
384
386
|
# The form's authenticity parameter. Override to provide your own.
|
@@ -109,7 +109,7 @@ module ActionController
|
|
109
109
|
cattr_accessor :permit_all_parameters, instance_accessor: false
|
110
110
|
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
|
111
111
|
|
112
|
-
delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
|
112
|
+
delegate :keys, :key?, :has_key?, :values, :has_value?, :value?, :empty?, :include?,
|
113
113
|
:as_json, to: :@parameters
|
114
114
|
|
115
115
|
# By default, never raise an UnpermittedParameters exception if these
|
@@ -122,16 +122,6 @@ module ActionController
|
|
122
122
|
cattr_accessor :always_permitted_parameters
|
123
123
|
self.always_permitted_parameters = %w( controller action )
|
124
124
|
|
125
|
-
def self.const_missing(const_name)
|
126
|
-
return super unless const_name == :NEVER_UNPERMITTED_PARAMS
|
127
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
128
|
-
`ActionController::Parameters::NEVER_UNPERMITTED_PARAMS` has been deprecated.
|
129
|
-
Use `ActionController::Parameters.always_permitted_parameters` instead.
|
130
|
-
MSG
|
131
|
-
|
132
|
-
always_permitted_parameters
|
133
|
-
end
|
134
|
-
|
135
125
|
# Returns a new instance of <tt>ActionController::Parameters</tt>.
|
136
126
|
# Also, sets the +permitted+ attribute to the default value of
|
137
127
|
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
|
@@ -154,17 +144,21 @@ module ActionController
|
|
154
144
|
end
|
155
145
|
|
156
146
|
# Returns true if another +Parameters+ object contains the same content and
|
157
|
-
# permitted flag
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
147
|
+
# permitted flag.
|
148
|
+
def ==(other)
|
149
|
+
if other.respond_to?(:permitted?)
|
150
|
+
self.permitted? == other.permitted? && self.parameters == other.parameters
|
151
|
+
elsif other.is_a?(Hash)
|
152
|
+
ActiveSupport::Deprecation.warn <<-WARNING.squish
|
153
|
+
Comparing equality between `ActionController::Parameters` and a
|
154
|
+
`Hash` is deprecated and will be removed in Rails 5.1. Please only do
|
155
|
+
comparisons between instances of `ActionController::Parameters`. If
|
156
|
+
you need to compare to a hash, first convert it using
|
157
|
+
`ActionController::Parameters#new`.
|
158
|
+
WARNING
|
159
|
+
@parameters == other.with_indifferent_access
|
162
160
|
else
|
163
|
-
|
164
|
-
@parameters == other_hash.with_indifferent_access
|
165
|
-
else
|
166
|
-
@parameters == other_hash
|
167
|
-
end
|
161
|
+
@parameters == other
|
168
162
|
end
|
169
163
|
end
|
170
164
|
|
@@ -584,6 +578,10 @@ module ActionController
|
|
584
578
|
dup
|
585
579
|
end
|
586
580
|
|
581
|
+
def inspect
|
582
|
+
"<#{self.class} #{@parameters} permitted: #{@permitted}>"
|
583
|
+
end
|
584
|
+
|
587
585
|
def method_missing(method_sym, *args, &block)
|
588
586
|
if @parameters.respond_to?(method_sym)
|
589
587
|
message = <<-DEPRECATE.squish
|
@@ -603,12 +601,14 @@ module ActionController
|
|
603
601
|
end
|
604
602
|
|
605
603
|
protected
|
604
|
+
attr_reader :parameters
|
605
|
+
|
606
606
|
def permitted=(new_permitted)
|
607
607
|
@permitted = new_permitted
|
608
608
|
end
|
609
609
|
|
610
610
|
def fields_for_style?
|
611
|
-
@parameters.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
|
611
|
+
@parameters.all? { |k, v| k =~ /\A-?\d+\z/ && (v.is_a?(Hash) || v.is_a?(Parameters)) }
|
612
612
|
end
|
613
613
|
|
614
614
|
private
|
@@ -12,6 +12,17 @@ module ActionController
|
|
12
12
|
include Testing::Functional
|
13
13
|
end
|
14
14
|
|
15
|
+
module Live
|
16
|
+
# Disable controller / rendering threads in tests. User tests can access
|
17
|
+
# the database on the main thread, so they could open a txn, then the
|
18
|
+
# controller thread will open a new connection and try to access data
|
19
|
+
# that's only visible to the main thread's txn. This is the problem in #23483
|
20
|
+
remove_method :new_controller_thread
|
21
|
+
def new_controller_thread # :nodoc:
|
22
|
+
yield
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
15
26
|
# ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
|
16
27
|
# Please use ActionDispatch::IntegrationTest going forward.
|
17
28
|
class TestRequest < ActionDispatch::TestRequest #:nodoc:
|
@@ -41,7 +52,7 @@ module ActionController
|
|
41
52
|
self.session = session
|
42
53
|
self.session_options = TestSession::DEFAULT_OPTIONS
|
43
54
|
@custom_param_parsers = {
|
44
|
-
|
55
|
+
xml: lambda { |raw_post| Hash.from_xml(raw_post)['hash'] }
|
45
56
|
}
|
46
57
|
end
|
47
58
|
|
@@ -94,7 +105,7 @@ module ActionController
|
|
94
105
|
when :url_encoded_form
|
95
106
|
data = non_path_parameters.to_query
|
96
107
|
else
|
97
|
-
@custom_param_parsers[content_mime_type] = ->(_) { non_path_parameters }
|
108
|
+
@custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
|
98
109
|
data = non_path_parameters.to_query
|
99
110
|
end
|
100
111
|
end
|
@@ -165,7 +176,7 @@ module ActionController
|
|
165
176
|
def initialize(session = {})
|
166
177
|
super(nil, nil)
|
167
178
|
@id = SecureRandom.hex(16)
|
168
|
-
@data =
|
179
|
+
@data = session.with_indifferent_access
|
169
180
|
@loaded = true
|
170
181
|
end
|
171
182
|
|
@@ -14,6 +14,7 @@ Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
|
|
14
14
|
Mime::Type.register "image/gif", :gif, [], %w(gif)
|
15
15
|
Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
|
16
16
|
Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
|
17
|
+
Mime::Type.register "image/svg+xml", :svg
|
17
18
|
|
18
19
|
Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
|
19
20
|
|
@@ -27,7 +28,8 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
|
|
27
28
|
|
28
29
|
# http://www.ietf.org/rfc/rfc4627.txt
|
29
30
|
# http://www.json.org/JSONRequest.html
|
30
|
-
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest
|
31
|
+
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
31
32
|
|
32
33
|
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
|
33
34
|
Mime::Type.register "application/zip", :zip, [], %w(zip)
|
35
|
+
Mime::Type.register "application/gzip", :gzip, %w(application/x-gzip), %w(gz)
|
@@ -1,22 +1,31 @@
|
|
1
1
|
module ActionDispatch
|
2
2
|
module Http
|
3
3
|
module Parameters
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
4
6
|
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
|
5
7
|
|
6
8
|
DEFAULT_PARSERS = {
|
7
|
-
Mime[:json] =>
|
9
|
+
Mime[:json].symbol => -> (raw_post) {
|
8
10
|
data = ActiveSupport::JSON.decode(raw_post)
|
9
11
|
data.is_a?(Hash) ? data : {:_json => data}
|
10
12
|
}
|
11
13
|
}
|
12
14
|
|
13
|
-
|
14
|
-
class <<
|
15
|
-
|
15
|
+
included do
|
16
|
+
class << self
|
17
|
+
attr_reader :parameter_parsers
|
16
18
|
end
|
17
19
|
|
18
|
-
|
20
|
+
self.parameter_parsers = DEFAULT_PARSERS
|
19
21
|
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def parameter_parsers=(parsers) # :nodoc:
|
25
|
+
@parameter_parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
20
29
|
# Returns both GET and POST \parameters in a single hash.
|
21
30
|
def parameters
|
22
31
|
params = get_header("action_dispatch.request.parameters")
|
@@ -51,7 +60,7 @@ module ActionDispatch
|
|
51
60
|
def parse_formatted_parameters(parsers)
|
52
61
|
return yield if content_length.zero?
|
53
62
|
|
54
|
-
strategy = parsers.fetch(content_mime_type) { return yield }
|
63
|
+
strategy = parsers.fetch(content_mime_type.symbol) { return yield }
|
55
64
|
|
56
65
|
begin
|
57
66
|
strategy.call(raw_post)
|
@@ -403,6 +403,10 @@ module ActionDispatch
|
|
403
403
|
def commit_flash
|
404
404
|
end
|
405
405
|
|
406
|
+
def ssl?
|
407
|
+
super || scheme == 'wss'.freeze
|
408
|
+
end
|
409
|
+
|
406
410
|
private
|
407
411
|
def check_method(name)
|
408
412
|
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
|
@@ -3,7 +3,7 @@ module ActionDispatch
|
|
3
3
|
class Route # :nodoc:
|
4
4
|
attr_reader :app, :path, :defaults, :name, :precedence
|
5
5
|
|
6
|
-
attr_reader :constraints
|
6
|
+
attr_reader :constraints, :internal
|
7
7
|
alias :conditions :constraints
|
8
8
|
|
9
9
|
module VerbMatchers
|
@@ -55,7 +55,7 @@ module ActionDispatch
|
|
55
55
|
##
|
56
56
|
# +path+ is a path constraint.
|
57
57
|
# +constraints+ is a hash of constraints to be applied to this route.
|
58
|
-
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence)
|
58
|
+
def initialize(name, app, path, constraints, required_defaults, defaults, request_method_match, precedence, internal = false)
|
59
59
|
@name = name
|
60
60
|
@app = app
|
61
61
|
@path = path
|
@@ -70,6 +70,7 @@ module ActionDispatch
|
|
70
70
|
@decorated_ast = nil
|
71
71
|
@precedence = precedence
|
72
72
|
@path_formatter = @path.build_formatter
|
73
|
+
@internal = internal
|
73
74
|
end
|
74
75
|
|
75
76
|
def ast
|
@@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/keys'
|
|
2
2
|
require 'active_support/key_generator'
|
3
3
|
require 'active_support/message_verifier'
|
4
4
|
require 'active_support/json'
|
5
|
+
require 'rack/utils'
|
5
6
|
|
6
7
|
module ActionDispatch
|
7
8
|
class Request
|
@@ -337,7 +338,7 @@ module ActionDispatch
|
|
337
338
|
end
|
338
339
|
|
339
340
|
def to_header
|
340
|
-
@cookies.map { |k,v| "#{k}=#{v}" }.join ';'
|
341
|
+
@cookies.map { |k,v| "#{escape(k)}=#{escape(v)}" }.join '; '
|
341
342
|
end
|
342
343
|
|
343
344
|
def handle_options(options) #:nodoc:
|
@@ -419,6 +420,10 @@ module ActionDispatch
|
|
419
420
|
|
420
421
|
private
|
421
422
|
|
423
|
+
def escape(string)
|
424
|
+
::Rack::Utils.escape(string)
|
425
|
+
end
|
426
|
+
|
422
427
|
def make_set_cookie_header(header)
|
423
428
|
header = @set_cookies.inject(header) { |m, (k, v)|
|
424
429
|
if write_cookie?(v)
|
@@ -156,15 +156,20 @@ module ActionDispatch
|
|
156
156
|
trace = wrapper.framework_trace if trace.empty?
|
157
157
|
|
158
158
|
ActiveSupport::Deprecation.silence do
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
logger.fatal
|
159
|
+
logger.fatal " "
|
160
|
+
logger.fatal "#{exception.class} (#{exception.message}):"
|
161
|
+
log_array logger, exception.annoted_source_code if exception.respond_to?(:annoted_source_code)
|
162
|
+
logger.fatal " "
|
163
|
+
log_array logger, trace
|
163
164
|
end
|
164
165
|
end
|
165
166
|
|
167
|
+
def log_array(logger, array)
|
168
|
+
array.map { |line| logger.fatal line }
|
169
|
+
end
|
170
|
+
|
166
171
|
def logger(request)
|
167
|
-
request.logger || stderr_logger
|
172
|
+
request.logger || ActionView::Base.logger || stderr_logger
|
168
173
|
end
|
169
174
|
|
170
175
|
def stderr_logger
|
@@ -37,6 +37,7 @@ module ActionDispatch
|
|
37
37
|
# The +parsers+ argument can take Hash of parsers where key is identifying
|
38
38
|
# content mime type, and value is a lambda that is going to process data.
|
39
39
|
def self.new(app, parsers = {})
|
40
|
+
parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
|
40
41
|
ActionDispatch::Request.parameter_parsers = ActionDispatch::Request::DEFAULT_PARSERS.merge(parsers)
|
41
42
|
app
|
42
43
|
end
|
@@ -23,7 +23,7 @@ module ActionDispatch
|
|
23
23
|
# goes a step further than signed cookies in that encrypted cookies cannot
|
24
24
|
# be altered or read by users. This is the default starting in Rails 4.
|
25
25
|
#
|
26
|
-
# If you have both secret_token and
|
26
|
+
# If you have both secret_token and secret_key_base set, your cookies will
|
27
27
|
# be encrypted, and signed cookies generated by Rails 3 will be
|
28
28
|
# transparently read and encrypted to provide a smooth upgrade path.
|
29
29
|
#
|
@@ -9,7 +9,7 @@ module ActionDispatch
|
|
9
9
|
|
10
10
|
# Singleton object used to determine if an optional param wasn't specified
|
11
11
|
Unspecified = Object.new
|
12
|
-
|
12
|
+
|
13
13
|
# Creates a session hash, merging the properties of the previous session if any
|
14
14
|
def self.create(store, req, default_options)
|
15
15
|
session_was = find req
|
@@ -61,7 +61,7 @@ module ActionDispatch
|
|
61
61
|
def initialize(by, req)
|
62
62
|
@by = by
|
63
63
|
@req = req
|
64
|
-
@delegate = {}
|
64
|
+
@delegate = {}.with_indifferent_access
|
65
65
|
@loaded = false
|
66
66
|
@exists = nil # we haven't checked yet
|
67
67
|
end
|
@@ -88,13 +88,13 @@ module ActionDispatch
|
|
88
88
|
# nil if the given key is not found in the session.
|
89
89
|
def [](key)
|
90
90
|
load_for_read!
|
91
|
-
@delegate[key
|
91
|
+
@delegate[key]
|
92
92
|
end
|
93
93
|
|
94
94
|
# Returns true if the session has the given key or false.
|
95
95
|
def has_key?(key)
|
96
96
|
load_for_read!
|
97
|
-
@delegate.key?(key
|
97
|
+
@delegate.key?(key)
|
98
98
|
end
|
99
99
|
alias :key? :has_key?
|
100
100
|
alias :include? :has_key?
|
@@ -112,7 +112,7 @@ module ActionDispatch
|
|
112
112
|
# Writes given value to given key of the session.
|
113
113
|
def []=(key, value)
|
114
114
|
load_for_write!
|
115
|
-
@delegate[key
|
115
|
+
@delegate[key] = value
|
116
116
|
end
|
117
117
|
|
118
118
|
# Clears the session.
|
@@ -139,13 +139,13 @@ module ActionDispatch
|
|
139
139
|
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
140
140
|
def update(hash)
|
141
141
|
load_for_write!
|
142
|
-
@delegate.update
|
142
|
+
@delegate.update hash
|
143
143
|
end
|
144
144
|
|
145
145
|
# Deletes given key from the session.
|
146
146
|
def delete(key)
|
147
147
|
load_for_write!
|
148
|
-
@delegate.delete key
|
148
|
+
@delegate.delete key
|
149
149
|
end
|
150
150
|
|
151
151
|
# Returns value of the given key from the session, or raises +KeyError+
|
@@ -165,9 +165,9 @@ module ActionDispatch
|
|
165
165
|
def fetch(key, default=Unspecified, &block)
|
166
166
|
load_for_read!
|
167
167
|
if default == Unspecified
|
168
|
-
@delegate.fetch(key
|
168
|
+
@delegate.fetch(key, &block)
|
169
169
|
else
|
170
|
-
@delegate.fetch(key
|
170
|
+
@delegate.fetch(key, default, &block)
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|
@@ -211,15 +211,9 @@ module ActionDispatch
|
|
211
211
|
def load!
|
212
212
|
id, session = @by.load_session @req
|
213
213
|
options[:id] = id
|
214
|
-
@delegate.replace(
|
214
|
+
@delegate.replace(session)
|
215
215
|
@loaded = true
|
216
216
|
end
|
217
|
-
|
218
|
-
def stringify_keys(other)
|
219
|
-
other.each_with_object({}) { |(key, value), hash|
|
220
|
-
hash[key.to_s] = value
|
221
|
-
}
|
222
|
-
end
|
223
217
|
end
|
224
218
|
end
|
225
219
|
end
|
@@ -159,7 +159,7 @@ module ActionDispatch
|
|
159
159
|
#
|
160
160
|
# controller 'geocode' do
|
161
161
|
# get 'geocode/:postalcode' => :show, constraints: {
|
162
|
-
# postalcode: /#
|
162
|
+
# postalcode: /# Postalcode format
|
163
163
|
# \d{5} #Prefix
|
164
164
|
# (-\d{4})? #Suffix
|
165
165
|
# /x
|
@@ -239,8 +239,7 @@ module ActionDispatch
|
|
239
239
|
#
|
240
240
|
# rails routes
|
241
241
|
#
|
242
|
-
# Target specific controllers by prefixing the command with <tt
|
243
|
-
# - or its <tt>-c</tt> shorthand.
|
242
|
+
# Target specific controllers by prefixing the command with <tt>-c</tt> option.
|
244
243
|
#
|
245
244
|
module Routing
|
246
245
|
extend ActiveSupport::Autoload
|
@@ -41,7 +41,7 @@ module ActionDispatch
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def internal?
|
44
|
-
|
44
|
+
internal
|
45
45
|
end
|
46
46
|
|
47
47
|
def engine?
|
@@ -84,14 +84,15 @@ module ActionDispatch
|
|
84
84
|
if filter.is_a?(Hash) && filter[:controller]
|
85
85
|
{ controller: /#{filter[:controller].downcase.sub(/_?controller\z/, '').sub('::', '/')}/ }
|
86
86
|
elsif filter
|
87
|
-
{ controller: /#{filter}/, action: /#{filter}/ }
|
87
|
+
{ controller: /#{filter}/, action: /#{filter}/, verb: /#{filter}/, name: /#{filter}/, path: /#{filter}/ }
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
def filter_routes(filter)
|
92
92
|
if filter
|
93
93
|
@routes.select do |route|
|
94
|
-
|
94
|
+
route_wrapper = RouteWrapper.new(route)
|
95
|
+
filter.any? { |default, value| route_wrapper.send(default) =~ value }
|
95
96
|
end
|
96
97
|
else
|
97
98
|
@routes
|
@@ -107,6 +107,7 @@ module ActionDispatch
|
|
107
107
|
@ast = ast
|
108
108
|
@anchor = anchor
|
109
109
|
@via = via
|
110
|
+
@internal = options[:internal]
|
110
111
|
|
111
112
|
path_params = ast.find_all(&:symbol?).map(&:to_sym)
|
112
113
|
|
@@ -148,7 +149,8 @@ module ActionDispatch
|
|
148
149
|
required_defaults,
|
149
150
|
defaults,
|
150
151
|
request_method,
|
151
|
-
precedence
|
152
|
+
precedence,
|
153
|
+
@internal)
|
152
154
|
|
153
155
|
route
|
154
156
|
end
|
@@ -289,7 +289,7 @@ module ActionDispatch
|
|
289
289
|
if last.permitted?
|
290
290
|
args.pop.to_h
|
291
291
|
else
|
292
|
-
raise ArgumentError, "Generating
|
292
|
+
raise ArgumentError, "Generating a URL from non sanitized request parameters is insecure!"
|
293
293
|
end
|
294
294
|
end
|
295
295
|
helper.call self, args, options
|
@@ -173,7 +173,7 @@ module ActionDispatch
|
|
173
173
|
route_name)
|
174
174
|
when ActionController::Parameters
|
175
175
|
unless options.permitted?
|
176
|
-
raise ArgumentError.new("Generating
|
176
|
+
raise ArgumentError.new("Generating a URL from non sanitized request parameters is insecure!")
|
177
177
|
end
|
178
178
|
route_name = options.delete :use_route
|
179
179
|
_routes.url_for(options.to_h.symbolize_keys.
|
@@ -86,6 +86,7 @@ module ActionDispatch
|
|
86
86
|
end
|
87
87
|
# Load routes.rb if it hasn't been loaded.
|
88
88
|
|
89
|
+
options = options.clone
|
89
90
|
generated_path, query_string_keys = @routes.generate_extras(options, defaults)
|
90
91
|
found_extras = options.reject { |k, _| ! query_string_keys.include? k }
|
91
92
|
|
@@ -71,7 +71,7 @@ module ActionDispatch
|
|
71
71
|
end
|
72
72
|
|
73
73
|
# Performs an XMLHttpRequest request with the given parameters, mirroring
|
74
|
-
#
|
74
|
+
# an AJAX request made from JavaScript.
|
75
75
|
#
|
76
76
|
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
|
77
77
|
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
|
@@ -321,7 +321,9 @@ module ActionDispatch
|
|
321
321
|
end
|
322
322
|
|
323
323
|
# Performs the actual request.
|
324
|
-
def process(method, path, params: nil, headers: nil, env: nil, xhr: false)
|
324
|
+
def process(method, path, params: nil, headers: nil, env: nil, xhr: false, as: nil)
|
325
|
+
request_encoder = RequestEncoder.encoder(as)
|
326
|
+
|
325
327
|
if path =~ %r{://}
|
326
328
|
location = URI.parse(path)
|
327
329
|
https! URI::HTTPS === location if location.scheme
|
@@ -330,14 +332,17 @@ module ActionDispatch
|
|
330
332
|
url_host += ":#{location.port}" if default != location.port
|
331
333
|
host! url_host
|
332
334
|
end
|
333
|
-
path =
|
335
|
+
path = request_encoder.append_format_to location.path
|
336
|
+
path = location.query ? "#{path}?#{location.query}" : path
|
337
|
+
else
|
338
|
+
path = request_encoder.append_format_to path
|
334
339
|
end
|
335
340
|
|
336
341
|
hostname, port = host.split(':')
|
337
342
|
|
338
343
|
request_env = {
|
339
344
|
:method => method,
|
340
|
-
:params => params,
|
345
|
+
:params => request_encoder.encode_params(params),
|
341
346
|
|
342
347
|
"SERVER_NAME" => hostname,
|
343
348
|
"SERVER_PORT" => port || (https? ? "443" : "80"),
|
@@ -347,7 +352,7 @@ module ActionDispatch
|
|
347
352
|
"REQUEST_URI" => path,
|
348
353
|
"HTTP_HOST" => host,
|
349
354
|
"REMOTE_ADDR" => remote_addr,
|
350
|
-
"CONTENT_TYPE" =>
|
355
|
+
"CONTENT_TYPE" => request_encoder.content_type,
|
351
356
|
"HTTP_ACCEPT" => accept
|
352
357
|
}
|
353
358
|
|
@@ -376,6 +381,7 @@ module ActionDispatch
|
|
376
381
|
response = _mock_session.last_response
|
377
382
|
@response = ActionDispatch::TestResponse.from_response(response)
|
378
383
|
@response.request = @request
|
384
|
+
@response.response_parser = RequestEncoder.parser(@response.content_type)
|
379
385
|
@html_document = nil
|
380
386
|
@url_options = nil
|
381
387
|
|
@@ -387,6 +393,56 @@ module ActionDispatch
|
|
387
393
|
def build_full_uri(path, env)
|
388
394
|
"#{env['rack.url_scheme']}://#{env['SERVER_NAME']}:#{env['SERVER_PORT']}#{path}"
|
389
395
|
end
|
396
|
+
|
397
|
+
class RequestEncoder # :nodoc:
|
398
|
+
@encoders = {}
|
399
|
+
|
400
|
+
attr_reader :response_parser
|
401
|
+
|
402
|
+
def initialize(mime_name, param_encoder, response_parser, url_encoded_form = false)
|
403
|
+
@mime = Mime[mime_name]
|
404
|
+
|
405
|
+
unless @mime
|
406
|
+
raise ArgumentError, "Can't register a request encoder for " \
|
407
|
+
"unregistered MIME Type: #{mime_name}. See `Mime::Type.register`."
|
408
|
+
end
|
409
|
+
|
410
|
+
@url_encoded_form = url_encoded_form
|
411
|
+
@path_format = ".#{@mime.symbol}" unless @url_encoded_form
|
412
|
+
@response_parser = response_parser || -> body { body }
|
413
|
+
@param_encoder = param_encoder || :"to_#{@mime.symbol}".to_proc
|
414
|
+
end
|
415
|
+
|
416
|
+
def append_format_to(path)
|
417
|
+
path << @path_format unless @url_encoded_form
|
418
|
+
path
|
419
|
+
end
|
420
|
+
|
421
|
+
def content_type
|
422
|
+
@mime.to_s
|
423
|
+
end
|
424
|
+
|
425
|
+
def encode_params(params)
|
426
|
+
@param_encoder.call(params)
|
427
|
+
end
|
428
|
+
|
429
|
+
def self.parser(content_type)
|
430
|
+
mime = Mime::Type.lookup(content_type)
|
431
|
+
encoder(mime ? mime.ref : nil).response_parser
|
432
|
+
end
|
433
|
+
|
434
|
+
def self.encoder(name)
|
435
|
+
@encoders[name] || WWWFormEncoder
|
436
|
+
end
|
437
|
+
|
438
|
+
def self.register_encoder(mime_name, param_encoder: nil, response_parser: nil)
|
439
|
+
@encoders[mime_name] = new(mime_name, param_encoder, response_parser)
|
440
|
+
end
|
441
|
+
|
442
|
+
register_encoder :json, response_parser: -> body { JSON.parse(body) }
|
443
|
+
|
444
|
+
WWWFormEncoder = new(:url_encoded_form, -> params { params }, nil, true)
|
445
|
+
end
|
390
446
|
end
|
391
447
|
|
392
448
|
module Runner
|
@@ -643,6 +699,39 @@ module ActionDispatch
|
|
643
699
|
# end
|
644
700
|
# end
|
645
701
|
#
|
702
|
+
# You can also test your JSON API easily by setting what the request should
|
703
|
+
# be encoded as:
|
704
|
+
#
|
705
|
+
# require 'test_helper'
|
706
|
+
#
|
707
|
+
# class ApiTest < ActionDispatch::IntegrationTest
|
708
|
+
# test 'creates articles' do
|
709
|
+
# assert_difference -> { Article.count } do
|
710
|
+
# post articles_path, params: { article: { title: 'Ahoy!' } }, as: :json
|
711
|
+
# end
|
712
|
+
#
|
713
|
+
# assert_response :success
|
714
|
+
# assert_equal({ id: Arcticle.last.id, title: 'Ahoy!' }, response.parsed_body)
|
715
|
+
# end
|
716
|
+
# end
|
717
|
+
#
|
718
|
+
# The `as` option sets the format to JSON, sets the content type to
|
719
|
+
# 'application/json' and encodes the parameters as JSON.
|
720
|
+
#
|
721
|
+
# Calling `parsed_body` on the response parses the response body as what
|
722
|
+
# the last request was encoded as. If the request wasn't encoded `as` something,
|
723
|
+
# it's the same as calling `body`.
|
724
|
+
#
|
725
|
+
# For any custom MIME Types you've registered, you can even add your own encoders with:
|
726
|
+
#
|
727
|
+
# ActionDispatch::IntegrationTest.register_encoder :wibble,
|
728
|
+
# param_encoder: -> params { params.to_wibble },
|
729
|
+
# response_parser: -> body { body }
|
730
|
+
#
|
731
|
+
# Where `param_encoder` defines how the params should be encoded and
|
732
|
+
# `response_parser` defines how the response body should be parsed through
|
733
|
+
# `parsed_body`.
|
734
|
+
#
|
646
735
|
# Consult the Rails Testing Guide for more.
|
647
736
|
|
648
737
|
class IntegrationTest < ActiveSupport::TestCase
|
@@ -671,5 +760,9 @@ module ActionDispatch
|
|
671
760
|
def document_root_element
|
672
761
|
html_document.root
|
673
762
|
end
|
763
|
+
|
764
|
+
def self.register_encoder(*args)
|
765
|
+
Integration::Session::RequestEncoder.register_encoder(*args)
|
766
|
+
end
|
674
767
|
end
|
675
768
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actionpack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.0.0.
|
4
|
+
version: 5.0.0.beta3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Heinemeier Hansson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.0.0.
|
19
|
+
version: 5.0.0.beta3
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.0.0.
|
26
|
+
version: 5.0.0.beta3
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,28 +98,28 @@ dependencies:
|
|
98
98
|
requirements:
|
99
99
|
- - '='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 5.0.0.
|
101
|
+
version: 5.0.0.beta3
|
102
102
|
type: :runtime
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
106
|
- - '='
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version: 5.0.0.
|
108
|
+
version: 5.0.0.beta3
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: activemodel
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
112
112
|
requirements:
|
113
113
|
- - '='
|
114
114
|
- !ruby/object:Gem::Version
|
115
|
-
version: 5.0.0.
|
115
|
+
version: 5.0.0.beta3
|
116
116
|
type: :development
|
117
117
|
prerelease: false
|
118
118
|
version_requirements: !ruby/object:Gem::Requirement
|
119
119
|
requirements:
|
120
120
|
- - '='
|
121
121
|
- !ruby/object:Gem::Version
|
122
|
-
version: 5.0.0.
|
122
|
+
version: 5.0.0.beta3
|
123
123
|
description: Web apps on Rails. Simple, battle-tested conventions for building and
|
124
124
|
testing MVC web applications. Works with any Rack-compatible server.
|
125
125
|
email: david@loudthinking.com
|
@@ -133,6 +133,8 @@ files:
|
|
133
133
|
- lib/abstract_controller.rb
|
134
134
|
- lib/abstract_controller/asset_paths.rb
|
135
135
|
- lib/abstract_controller/base.rb
|
136
|
+
- lib/abstract_controller/caching.rb
|
137
|
+
- lib/abstract_controller/caching/fragments.rb
|
136
138
|
- lib/abstract_controller/callbacks.rb
|
137
139
|
- lib/abstract_controller/collector.rb
|
138
140
|
- lib/abstract_controller/helpers.rb
|
@@ -146,7 +148,6 @@ files:
|
|
146
148
|
- lib/action_controller/api/api_rendering.rb
|
147
149
|
- lib/action_controller/base.rb
|
148
150
|
- lib/action_controller/caching.rb
|
149
|
-
- lib/action_controller/caching/fragments.rb
|
150
151
|
- lib/action_controller/form_builder.rb
|
151
152
|
- lib/action_controller/log_subscriber.rb
|
152
153
|
- lib/action_controller/metal.rb
|
@@ -196,7 +197,6 @@ files:
|
|
196
197
|
- lib/action_dispatch/http/upload.rb
|
197
198
|
- lib/action_dispatch/http/url.rb
|
198
199
|
- lib/action_dispatch/journey.rb
|
199
|
-
- lib/action_dispatch/journey/backwards.rb
|
200
200
|
- lib/action_dispatch/journey/formatter.rb
|
201
201
|
- lib/action_dispatch/journey/gtg/builder.rb
|
202
202
|
- lib/action_dispatch/journey/gtg/simulator.rb
|
@@ -301,9 +301,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
301
301
|
requirements:
|
302
302
|
- none
|
303
303
|
rubyforge_project:
|
304
|
-
rubygems_version: 2.5.
|
304
|
+
rubygems_version: 2.5.1
|
305
305
|
signing_key:
|
306
306
|
specification_version: 4
|
307
307
|
summary: Web-flow and rendering framework putting the VC in MVC (part of Rails).
|
308
308
|
test_files: []
|
309
|
-
has_rdoc:
|