actionpack 4.2.0.beta1 → 4.2.0.beta2
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 +54 -4
- data/lib/action_controller/metal/renderers.rb +12 -7
- data/lib/action_dispatch/http/mime_type.rb +2 -2
- data/lib/action_dispatch/http/request.rb +8 -2
- data/lib/action_dispatch/http/response.rb +17 -2
- data/lib/action_dispatch/journey/router.rb +23 -17
- data/lib/action_dispatch/middleware/remote_ip.rb +38 -52
- data/lib/action_dispatch/middleware/static.rb +42 -22
- data/lib/action_dispatch/routing/mapper.rb +0 -2
- data/lib/action_dispatch/routing/polymorphic_routes.rb +3 -4
- data/lib/action_dispatch/testing/assertions/routing.rb +8 -8
- data/lib/action_dispatch/testing/assertions/selector.rb +1 -1
- data/lib/action_dispatch/testing/assertions/tag.rb +1 -1
- data/lib/action_pack/gem_version.rb +2 -2
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9093a36f426a1fe8cef497530b8ae253e1de975
|
4
|
+
data.tar.gz: c8704d3bc342e3eebab15189b9d5f996472c999a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d3f4fb1fb46549a3bf300d861f998239e812fa38aa906e8675a1362f51f51ffdd9e264888e1f122dc9fc955b43c0fdb944dc6b696a2bd781c18906c4909cefd
|
7
|
+
data.tar.gz: ba2666cc3a9f82160f9a2e092dfe9f952b3ac132f1ecdb68f57a70f692b169245e7085b120ba474954e1300ca693b072cc88058999780d17087b40add6e8afa7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,53 @@
|
|
1
|
+
* Deprecate implicit Array conversion for Response objects. It was added
|
2
|
+
(using `#to_ary`) so we could conveniently use implicit splatting:
|
3
|
+
|
4
|
+
status, headers, body = response
|
5
|
+
|
6
|
+
But it also means `response + response` works and `[response].flatten`
|
7
|
+
cascades down to the Rack body. Nonsense behavior. Instead, rely on
|
8
|
+
explicit conversion and splatting with `#to_a`:
|
9
|
+
|
10
|
+
status, header, body = *response
|
11
|
+
|
12
|
+
*Jeremy Kemper*
|
13
|
+
|
14
|
+
* Don't rescue `IPAddr::InvalidAddressError`.
|
15
|
+
|
16
|
+
`IPAddr::InvalidAddressError` does not exist in Ruby 1.9.3
|
17
|
+
and fails for JRuby in 1.9 mode.
|
18
|
+
|
19
|
+
*Peter Suschlik*
|
20
|
+
|
21
|
+
* Fix bug where the router would ignore any constraints added to redirect
|
22
|
+
routes.
|
23
|
+
|
24
|
+
Fixes #16605.
|
25
|
+
|
26
|
+
*Agis Anastasopoulos*
|
27
|
+
|
28
|
+
* Allow `config.action_dispatch.trusted_proxies` to accept an IPAddr object.
|
29
|
+
|
30
|
+
Example:
|
31
|
+
|
32
|
+
# config/environments/production.rb
|
33
|
+
config.action_dispatch.trusted_proxies = IPAddr.new('4.8.15.0/16')
|
34
|
+
|
35
|
+
*Sam Aarons*
|
36
|
+
|
37
|
+
* Avoid duplicating routes for HEAD requests.
|
38
|
+
|
39
|
+
Instead of duplicating the routes, we will first match the HEAD request to
|
40
|
+
HEAD routes. If no match is found, we will then map the HEAD request to
|
41
|
+
GET routes.
|
42
|
+
|
43
|
+
*Guo Xiang Tan*, *Andrew White*
|
44
|
+
|
45
|
+
* Requests that hit `ActionDispatch::Static` can now take advantage
|
46
|
+
of gzipped assets on disk. By default a gzip asset will be served if
|
47
|
+
the client supports gzip and a compressed file is on disk.
|
48
|
+
|
49
|
+
*Richard Schneeman*
|
50
|
+
|
1
51
|
* `ActionController::Parameters` will stop inheriting from `Hash` and
|
2
52
|
`HashWithIndifferentAccess` in the next major release. If you use any method
|
3
53
|
that is not available on `ActionController::Parameters` you should consider
|
@@ -33,7 +83,7 @@
|
|
33
83
|
|
34
84
|
*Prem Sichanugrist*
|
35
85
|
|
36
|
-
* Deprecated TagAssertions
|
86
|
+
* Deprecated `TagAssertions`.
|
37
87
|
|
38
88
|
*Kasper Timm Hansen*
|
39
89
|
|
@@ -65,11 +115,11 @@
|
|
65
115
|
If you render a different template, you can now pass the `:template`
|
66
116
|
option to include its digest instead:
|
67
117
|
|
68
|
-
|
118
|
+
fresh_when @post, template: 'widgets/show'
|
69
119
|
|
70
120
|
Pass `template: false` to skip the lookup. To turn this off entirely, set:
|
71
121
|
|
72
|
-
|
122
|
+
config.action_controller.etag_with_template_digest = false
|
73
123
|
|
74
124
|
*Jeremy Kemper*
|
75
125
|
|
@@ -123,7 +173,7 @@
|
|
123
173
|
*Godfrey Chan*
|
124
174
|
|
125
175
|
* Prepend a JS comment to JSONP callbacks. Addresses CVE-2014-4671
|
126
|
-
("Rosetta Flash")
|
176
|
+
("Rosetta Flash").
|
127
177
|
|
128
178
|
*Greg Campbell*
|
129
179
|
|
@@ -34,14 +34,15 @@ module ActionController
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def render_to_body(options)
|
37
|
-
|
37
|
+
_render_to_body_with_renderer(options) || super
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
40
|
+
def _render_to_body_with_renderer(options)
|
41
41
|
_renderers.each do |name|
|
42
42
|
if options.key?(name)
|
43
43
|
_process_options(options)
|
44
|
-
|
44
|
+
method_name = Renderers._render_with_renderer_method_name(name)
|
45
|
+
return send(method_name, options.delete(name), options)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
nil
|
@@ -51,6 +52,10 @@ module ActionController
|
|
51
52
|
# Default values are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
|
52
53
|
RENDERERS = Set.new
|
53
54
|
|
55
|
+
def self._render_with_renderer_method_name(key)
|
56
|
+
"_render_with_renderer_#{key}"
|
57
|
+
end
|
58
|
+
|
54
59
|
# Adds a new renderer to call within controller actions.
|
55
60
|
# A renderer is invoked by passing its name as an option to
|
56
61
|
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
|
@@ -84,7 +89,7 @@ module ActionController
|
|
84
89
|
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
|
85
90
|
# <tt>ActionController::MimeResponds#respond_with</tt>
|
86
91
|
def self.add(key, &block)
|
87
|
-
define_method(
|
92
|
+
define_method(_render_with_renderer_method_name(key), &block)
|
88
93
|
RENDERERS << key.to_sym
|
89
94
|
end
|
90
95
|
|
@@ -95,8 +100,8 @@ module ActionController
|
|
95
100
|
# ActionController::Renderers.remove(:csv)
|
96
101
|
def self.remove(key)
|
97
102
|
RENDERERS.delete(key.to_sym)
|
98
|
-
|
99
|
-
remove_method(
|
103
|
+
method_name = _render_with_renderer_method_name(key)
|
104
|
+
remove_method(method_name) if method_defined?(method_name)
|
100
105
|
end
|
101
106
|
|
102
107
|
module All
|
@@ -112,7 +117,7 @@ module ActionController
|
|
112
117
|
json = json.to_json(options) unless json.kind_of?(String)
|
113
118
|
|
114
119
|
if options[:callback].present?
|
115
|
-
if
|
120
|
+
if content_type.nil? || content_type == Mime::JSON
|
116
121
|
self.content_type = Mime::JS
|
117
122
|
end
|
118
123
|
|
@@ -45,8 +45,8 @@ module Mime
|
|
45
45
|
#
|
46
46
|
# respond_to do |format|
|
47
47
|
# format.html
|
48
|
-
# format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
|
49
|
-
# format.xml { render xml: @
|
48
|
+
# format.ics { render text: @post.to_ics, mime_type: Mime::Type["text/calendar"] }
|
49
|
+
# format.xml { render xml: @post }
|
50
50
|
# end
|
51
51
|
# end
|
52
52
|
# end
|
@@ -105,6 +105,12 @@ module ActionDispatch
|
|
105
105
|
@request_method ||= check_method(env["REQUEST_METHOD"])
|
106
106
|
end
|
107
107
|
|
108
|
+
def request_method=(request_method) #:nodoc:
|
109
|
+
if check_method(request_method)
|
110
|
+
@request_method = env["REQUEST_METHOD"] = request_method
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
108
114
|
# Returns a symbol form of the #request_method
|
109
115
|
def request_method_symbol
|
110
116
|
HTTP_METHOD_LOOKUP[request_method]
|
@@ -292,7 +298,7 @@ module ActionDispatch
|
|
292
298
|
# Override Rack's GET method to support indifferent access
|
293
299
|
def GET
|
294
300
|
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
295
|
-
rescue
|
301
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
296
302
|
raise ActionController::BadRequest.new(:query, e)
|
297
303
|
end
|
298
304
|
alias :query_parameters :GET
|
@@ -300,7 +306,7 @@ module ActionDispatch
|
|
300
306
|
# Override Rack's POST method to support indifferent access
|
301
307
|
def POST
|
302
308
|
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
303
|
-
rescue
|
309
|
+
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
304
310
|
raise ActionController::BadRequest.new(:request, e)
|
305
311
|
end
|
306
312
|
alias :request_parameters :POST
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'active_support/deprecation'
|
2
3
|
require 'action_dispatch/http/filter_redirect'
|
3
4
|
require 'monitor'
|
4
5
|
|
@@ -274,12 +275,22 @@ module ActionDispatch # :nodoc:
|
|
274
275
|
end
|
275
276
|
|
276
277
|
# Turns the Response into a Rack-compatible array of the status, headers,
|
277
|
-
# and body.
|
278
|
+
# and body. Allows explict splatting:
|
279
|
+
#
|
280
|
+
# status, headers, body = *response
|
278
281
|
def to_a
|
279
282
|
rack_response @status, @header.to_hash
|
280
283
|
end
|
281
284
|
alias prepare! to_a
|
282
|
-
|
285
|
+
|
286
|
+
# Be super clear that a response object is not an Array. Defining this
|
287
|
+
# would make implicit splatting work, but it also makes adding responses
|
288
|
+
# as arrays work, and "flattening" responses, cascading to the rack body!
|
289
|
+
# Not sensible behavior.
|
290
|
+
def to_ary
|
291
|
+
ActiveSupport::Deprecation.warn 'ActionDispatch::Response#to_ary no longer performs implicit conversion to an Array. Please use response.to_a instead, or a splat like `status, headers, body = *response`'
|
292
|
+
to_a
|
293
|
+
end
|
283
294
|
|
284
295
|
# Returns the response cookies, converted to a Hash of (name => value) pairs
|
285
296
|
#
|
@@ -369,6 +380,10 @@ module ActionDispatch # :nodoc:
|
|
369
380
|
def to_path
|
370
381
|
@response.stream.to_path
|
371
382
|
end
|
383
|
+
|
384
|
+
def to_ary
|
385
|
+
nil
|
386
|
+
end
|
372
387
|
end
|
373
388
|
|
374
389
|
def rack_response(status, header)
|
@@ -101,11 +101,13 @@ module ActionDispatch
|
|
101
101
|
r.path.match(req.path_info)
|
102
102
|
}
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
routes =
|
105
|
+
if req.request_method == "HEAD"
|
106
|
+
match_head_routes(routes, req)
|
107
|
+
else
|
108
|
+
match_routes(routes, req)
|
109
|
+
end
|
107
110
|
|
108
|
-
routes.select! { |r| r.matches?(req) }
|
109
111
|
routes.sort_by!(&:precedence)
|
110
112
|
|
111
113
|
routes.map! { |r|
|
@@ -118,19 +120,23 @@ module ActionDispatch
|
|
118
120
|
}
|
119
121
|
end
|
120
122
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
123
|
+
def match_head_routes(routes, req)
|
124
|
+
head_routes = match_routes(routes, req)
|
125
|
+
|
126
|
+
if head_routes.empty?
|
127
|
+
begin
|
128
|
+
req.request_method = "GET"
|
129
|
+
match_routes(routes, req)
|
130
|
+
ensure
|
131
|
+
req.request_method = "HEAD"
|
132
|
+
end
|
133
|
+
else
|
134
|
+
head_routes
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def match_routes(routes, req)
|
139
|
+
routes.select { |r| r.matches?(req) }
|
134
140
|
end
|
135
141
|
end
|
136
142
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
# This middleware calculates the IP address of the remote client that is
|
3
5
|
# making the request. It does this by checking various headers that could
|
@@ -28,14 +30,14 @@ module ActionDispatch
|
|
28
30
|
# guaranteed by the IP specification to be private addresses. Those will
|
29
31
|
# not be the ultimate client IP in production, and so are discarded. See
|
30
32
|
# http://en.wikipedia.org/wiki/Private_network for details.
|
31
|
-
TRUSTED_PROXIES =
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
33
|
+
TRUSTED_PROXIES = [
|
34
|
+
"127.0.0.1", # localhost IPv4
|
35
|
+
"::1", # localhost IPv6
|
36
|
+
"fc00::/7", # private IPv6 range fc00::/7
|
37
|
+
"10.0.0.0/8", # private IPv4 range 10.x.x.x
|
38
|
+
"172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
39
|
+
"192.168.0.0/16", # private IPv4 range 192.168.x.x
|
40
|
+
].map { |proxy| IPAddr.new(proxy) }
|
39
41
|
|
40
42
|
attr_reader :check_ip, :proxies
|
41
43
|
|
@@ -47,24 +49,24 @@ module ActionDispatch
|
|
47
49
|
# clients (like WAP devices), or behind proxies that set headers in an
|
48
50
|
# incorrect or confusing way (like AWS ELB).
|
49
51
|
#
|
50
|
-
# The +custom_proxies+ argument can take
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
52
|
+
# The +custom_proxies+ argument can take an Array of string, IPAddr, or
|
53
|
+
# Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a
|
54
|
+
# single string, IPAddr, or Regexp object is provided, it will be used in
|
55
|
+
# addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you
|
56
|
+
# want in the middle (or at the beginning) of the X-Forwarded-For list,
|
57
|
+
# with your proxy servers after it. If your proxies aren't removed, pass
|
58
|
+
# them in via the +custom_proxies+ parameter. That way, the middleware will
|
59
|
+
# ignore those IP addresses, and return the one that you want.
|
57
60
|
def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
|
58
61
|
@app = app
|
59
62
|
@check_ip = check_ip_spoofing
|
60
|
-
@proxies =
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
end
|
63
|
+
@proxies = if custom_proxies.blank?
|
64
|
+
TRUSTED_PROXIES
|
65
|
+
elsif custom_proxies.respond_to?(:any?)
|
66
|
+
custom_proxies
|
67
|
+
else
|
68
|
+
Array(custom_proxies) + TRUSTED_PROXIES
|
69
|
+
end
|
68
70
|
end
|
69
71
|
|
70
72
|
# Since the IP address may not be needed, we store the object here
|
@@ -80,32 +82,6 @@ module ActionDispatch
|
|
80
82
|
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
|
81
83
|
# is called, this class will calculate the value and then memoize it.
|
82
84
|
class GetIp
|
83
|
-
|
84
|
-
# This constant contains a regular expression that validates every known
|
85
|
-
# form of IP v4 and v6 address, with or without abbreviations, adapted
|
86
|
-
# from {this gist}[https://gist.github.com/gazay/1289635].
|
87
|
-
VALID_IP = %r{
|
88
|
-
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
|
89
|
-
(^(
|
90
|
-
(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
|
91
|
-
(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
|
92
|
-
(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
|
93
|
-
(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
|
94
|
-
(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
|
95
|
-
(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
|
96
|
-
(([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
97
|
-
(([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
98
|
-
(([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
99
|
-
(([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
100
|
-
(([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
101
|
-
(([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
102
|
-
(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
|
103
|
-
([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
|
104
|
-
(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning
|
105
|
-
(([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
|
106
|
-
)$)
|
107
|
-
}x
|
108
|
-
|
109
85
|
def initialize(env, middleware)
|
110
86
|
@env = env
|
111
87
|
@check_ip = middleware.check_ip
|
@@ -173,12 +149,22 @@ module ActionDispatch
|
|
173
149
|
def ips_from(header)
|
174
150
|
# Split the comma-separated list into an array of strings
|
175
151
|
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
176
|
-
|
177
|
-
|
152
|
+
ips.select do |ip|
|
153
|
+
begin
|
154
|
+
# Only return IPs that are valid according to the IPAddr#new method
|
155
|
+
range = IPAddr.new(ip).to_range
|
156
|
+
# we want to make sure nobody is sneaking a netmask in
|
157
|
+
range.begin == range.end
|
158
|
+
rescue ArgumentError
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
end
|
178
162
|
end
|
179
163
|
|
180
164
|
def filter_proxies(ips)
|
181
|
-
ips.reject
|
165
|
+
ips.reject do |ip|
|
166
|
+
@proxies.any? { |proxy| proxy === ip }
|
167
|
+
end
|
182
168
|
end
|
183
169
|
|
184
170
|
end
|
@@ -16,43 +16,63 @@ module ActionDispatch
|
|
16
16
|
def initialize(root, cache_control)
|
17
17
|
@root = root.chomp('/')
|
18
18
|
@compiled_root = /^#{Regexp.escape(root)}/
|
19
|
-
headers
|
20
|
-
@file_server
|
19
|
+
headers = cache_control && { 'Cache-Control' => cache_control }
|
20
|
+
@file_server = ::Rack::File.new(@root, headers)
|
21
21
|
end
|
22
22
|
|
23
23
|
def match?(path)
|
24
|
-
path =
|
24
|
+
path = URI.parser.unescape(path)
|
25
25
|
return false unless path.valid_encoding?
|
26
26
|
|
27
|
-
|
28
|
-
paths = "#{full_path}#{ext}"
|
27
|
+
paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"]
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
if match
|
33
|
-
match.sub!(@compiled_root, '')
|
34
|
-
::Rack::Utils.escape(match)
|
29
|
+
if match = paths.detect {|p| File.file?(File.join(@root, p)) }
|
30
|
+
return ::Rack::Utils.escape(match)
|
35
31
|
end
|
36
32
|
end
|
37
33
|
|
38
34
|
def call(env)
|
39
|
-
|
40
|
-
|
35
|
+
path = env['PATH_INFO']
|
36
|
+
gzip_path = gzip_file_path(path)
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
if gzip_path && gzip_encoding_accepted?(env)
|
39
|
+
env['PATH_INFO'] = gzip_path
|
40
|
+
status, headers, body = @file_server.call(env)
|
41
|
+
headers['Content-Encoding'] = 'gzip'
|
42
|
+
headers['Content-Type'] = content_type(path)
|
43
|
+
else
|
44
|
+
status, headers, body = @file_server.call(env)
|
46
45
|
end
|
47
|
-
end
|
48
46
|
|
49
|
-
|
50
|
-
URI.parser.unescape(path)
|
51
|
-
end
|
47
|
+
headers['Vary'] = 'Accept-Encoding' if gzip_path
|
52
48
|
|
53
|
-
|
54
|
-
|
49
|
+
return [status, headers, body]
|
50
|
+
ensure
|
51
|
+
env['PATH_INFO'] = path
|
55
52
|
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def ext
|
56
|
+
::ActionController::Base.default_static_extension
|
57
|
+
end
|
58
|
+
|
59
|
+
def content_type(path)
|
60
|
+
::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
|
61
|
+
end
|
62
|
+
|
63
|
+
def gzip_encoding_accepted?(env)
|
64
|
+
env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/i
|
65
|
+
end
|
66
|
+
|
67
|
+
def gzip_file_path(path)
|
68
|
+
can_gzip_mime = content_type(path) =~ /\A(?:text\/|application\/javascript)/
|
69
|
+
gzip_path = "#{path}.gz"
|
70
|
+
if can_gzip_mime && File.exist?(File.join(@root, ::Rack::Utils.unescape(gzip_path)))
|
71
|
+
gzip_path
|
72
|
+
else
|
73
|
+
false
|
74
|
+
end
|
75
|
+
end
|
56
76
|
end
|
57
77
|
|
58
78
|
# This middleware will attempt to return the contents of a file's body from
|
@@ -116,7 +116,6 @@ module ActionDispatch
|
|
116
116
|
action,
|
117
117
|
type,
|
118
118
|
opts
|
119
|
-
|
120
119
|
end
|
121
120
|
|
122
121
|
# Returns the path component of a URL for the given record. It uses
|
@@ -159,8 +158,7 @@ module ActionDispatch
|
|
159
158
|
end
|
160
159
|
|
161
160
|
def polymorphic_path_for_action(action, record_or_hash, options)
|
162
|
-
|
163
|
-
polymorphic_path(record_or_hash, options)
|
161
|
+
polymorphic_path(record_or_hash, options.merge(:action => action))
|
164
162
|
end
|
165
163
|
|
166
164
|
class HelperMethodBuilder # :nodoc:
|
@@ -197,7 +195,8 @@ module ActionDispatch
|
|
197
195
|
|
198
196
|
case record_or_hash_or_array
|
199
197
|
when Array
|
200
|
-
|
198
|
+
record_or_hash_or_array = record_or_hash_or_array.compact
|
199
|
+
if record_or_hash_or_array.empty?
|
201
200
|
raise ArgumentError, "Nil location provided. Can't build URI."
|
202
201
|
end
|
203
202
|
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
|
@@ -38,7 +38,7 @@ module ActionDispatch
|
|
38
38
|
# # Test a custom route
|
39
39
|
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
|
40
40
|
def assert_recognizes(expected_options, path, extras={}, msg=nil)
|
41
|
-
request = recognized_request_for(path, extras)
|
41
|
+
request = recognized_request_for(path, extras, msg)
|
42
42
|
|
43
43
|
expected_options = expected_options.clone
|
44
44
|
|
@@ -69,9 +69,9 @@ module ActionDispatch
|
|
69
69
|
#
|
70
70
|
# # Asserts that the generated route gives us our custom route
|
71
71
|
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
|
72
|
-
def assert_generates(expected_path, options, defaults={}, extras
|
72
|
+
def assert_generates(expected_path, options, defaults={}, extras={}, message=nil)
|
73
73
|
if expected_path =~ %r{://}
|
74
|
-
fail_on(URI::InvalidURIError) do
|
74
|
+
fail_on(URI::InvalidURIError, message) do
|
75
75
|
uri = URI.parse(expected_path)
|
76
76
|
expected_path = uri.path.to_s.empty? ? "/" : uri.path
|
77
77
|
end
|
@@ -174,7 +174,7 @@ module ActionDispatch
|
|
174
174
|
|
175
175
|
private
|
176
176
|
# Recognizes the route for a given path.
|
177
|
-
def recognized_request_for(path, extras = {})
|
177
|
+
def recognized_request_for(path, extras = {}, msg)
|
178
178
|
if path.is_a?(Hash)
|
179
179
|
method = path[:method]
|
180
180
|
path = path[:path]
|
@@ -186,7 +186,7 @@ module ActionDispatch
|
|
186
186
|
request = ActionController::TestRequest.new
|
187
187
|
|
188
188
|
if path =~ %r{://}
|
189
|
-
fail_on(URI::InvalidURIError) do
|
189
|
+
fail_on(URI::InvalidURIError, msg) do
|
190
190
|
uri = URI.parse(path)
|
191
191
|
request.env["rack.url_scheme"] = uri.scheme || "http"
|
192
192
|
request.host = uri.host if uri.host
|
@@ -200,7 +200,7 @@ module ActionDispatch
|
|
200
200
|
|
201
201
|
request.request_method = method if method
|
202
202
|
|
203
|
-
params = fail_on(ActionController::RoutingError) do
|
203
|
+
params = fail_on(ActionController::RoutingError, msg) do
|
204
204
|
@routes.recognize_path(path, { :method => method, :extras => extras })
|
205
205
|
end
|
206
206
|
request.path_parameters = params.with_indifferent_access
|
@@ -208,10 +208,10 @@ module ActionDispatch
|
|
208
208
|
request
|
209
209
|
end
|
210
210
|
|
211
|
-
def fail_on(exception_class)
|
211
|
+
def fail_on(exception_class, message)
|
212
212
|
yield
|
213
213
|
rescue exception_class => e
|
214
|
-
raise Minitest::Assertion, e.message
|
214
|
+
raise Minitest::Assertion, message || e.message
|
215
215
|
end
|
216
216
|
end
|
217
217
|
end
|
@@ -1,3 +1,3 @@
|
|
1
1
|
require 'active_support/deprecation'
|
2
2
|
|
3
|
-
ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::SelectorAssertions has been
|
3
|
+
ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::SelectorAssertions has been extracted to the rails-dom-testing gem.")
|
@@ -1,3 +1,3 @@
|
|
1
1
|
require 'active_support/deprecation'
|
2
2
|
|
3
|
-
ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::TagAssertions has been
|
3
|
+
ActiveSupport::Deprecation.warn("ActionDispatch::Assertions::TagAssertions has been extracted to the rails-dom-testing gem.")
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActionPack
|
2
|
-
# Returns the version of the currently loaded
|
2
|
+
# Returns the version of the currently loaded Action Pack as a <tt>Gem::Version</tt>
|
3
3
|
def self.gem_version
|
4
4
|
Gem::Version.new VERSION::STRING
|
5
5
|
end
|
@@ -8,7 +8,7 @@ module ActionPack
|
|
8
8
|
MAJOR = 4
|
9
9
|
MINOR = 2
|
10
10
|
TINY = 0
|
11
|
-
PRE = "
|
11
|
+
PRE = "beta2"
|
12
12
|
|
13
13
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
14
14
|
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: 4.2.0.
|
4
|
+
version: 4.2.0.beta2
|
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: 2014-
|
11
|
+
date: 2014-09-26 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: 4.2.0.
|
19
|
+
version: 4.2.0.beta2
|
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: 4.2.0.
|
26
|
+
version: 4.2.0.beta2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 0.6.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: rails-
|
56
|
+
name: rails-html-sanitizer
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
@@ -61,7 +61,7 @@ dependencies:
|
|
61
61
|
version: '1.0'
|
62
62
|
- - ">="
|
63
63
|
- !ruby/object:Gem::Version
|
64
|
-
version: 1.0.
|
64
|
+
version: 1.0.1
|
65
65
|
type: :runtime
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -71,7 +71,7 @@ dependencies:
|
|
71
71
|
version: '1.0'
|
72
72
|
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: 1.0.
|
74
|
+
version: 1.0.1
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: rails-dom-testing
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
version: '1.0'
|
82
82
|
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version: 1.0.
|
84
|
+
version: 1.0.3
|
85
85
|
type: :runtime
|
86
86
|
prerelease: false
|
87
87
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -91,35 +91,35 @@ dependencies:
|
|
91
91
|
version: '1.0'
|
92
92
|
- - ">="
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
version: 1.0.
|
94
|
+
version: 1.0.3
|
95
95
|
- !ruby/object:Gem::Dependency
|
96
96
|
name: actionview
|
97
97
|
requirement: !ruby/object:Gem::Requirement
|
98
98
|
requirements:
|
99
99
|
- - '='
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 4.2.0.
|
101
|
+
version: 4.2.0.beta2
|
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: 4.2.0.
|
108
|
+
version: 4.2.0.beta2
|
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: 4.2.0.
|
115
|
+
version: 4.2.0.beta2
|
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: 4.2.0.
|
122
|
+
version: 4.2.0.beta2
|
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
|