actionpack 3.1.12 → 3.2.0.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.
- data/CHANGELOG.md +5503 -108
- data/README.rdoc +3 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +102 -18
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/layouts.rb +116 -50
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +1 -6
- data/lib/abstract_controller/view_paths.rb +6 -5
- data/lib/action_controller.rb +0 -15
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/caching/actions.rb +5 -6
- data/lib/action_controller/caching/fragments.rb +18 -18
- data/lib/action_controller/caching/pages.rb +7 -6
- data/lib/action_controller/caching/sweeping.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +8 -4
- data/lib/action_controller/metal.rb +7 -1
- data/lib/action_controller/metal/conditional_get.rb +49 -4
- data/lib/action_controller/metal/data_streaming.rb +17 -5
- data/lib/action_controller/metal/force_ssl.rb +8 -5
- data/lib/action_controller/metal/helpers.rb +7 -4
- data/lib/action_controller/metal/http_authentication.rb +9 -12
- data/lib/action_controller/metal/instrumentation.rb +9 -4
- data/lib/action_controller/metal/mime_responds.rb +4 -4
- data/lib/action_controller/metal/params_wrapper.rb +12 -8
- data/lib/action_controller/metal/redirecting.rb +7 -6
- data/lib/action_controller/metal/renderers.rb +9 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
- data/lib/action_controller/metal/rescue.rb +13 -0
- data/lib/action_controller/metal/responder.rb +11 -23
- data/lib/action_controller/metal/streaming.rb +0 -25
- data/lib/action_controller/railtie.rb +1 -0
- data/lib/action_controller/railties/paths.rb +4 -3
- data/lib/action_controller/record_identifier.rb +4 -4
- data/lib/action_controller/test_case.rb +60 -56
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
- data/lib/action_dispatch.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +27 -15
- data/lib/action_dispatch/http/filter_parameters.rb +3 -1
- data/lib/action_dispatch/http/headers.rb +3 -5
- data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
- data/lib/action_dispatch/http/mime_type.rb +7 -3
- data/lib/action_dispatch/http/mime_types.rb +12 -0
- data/lib/action_dispatch/http/parameter_filter.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +0 -4
- data/lib/action_dispatch/http/request.rb +18 -68
- data/lib/action_dispatch/http/response.rb +11 -32
- data/lib/action_dispatch/http/upload.rb +3 -14
- data/lib/action_dispatch/http/url.rb +1 -1
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +20 -16
- data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
- data/lib/action_dispatch/middleware/flash.rb +6 -9
- data/lib/action_dispatch/middleware/params_parser.rb +6 -11
- data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
- data/lib/action_dispatch/middleware/reloader.rb +38 -14
- data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
- data/lib/action_dispatch/middleware/request_id.rb +39 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
- data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
- data/lib/action_dispatch/middleware/static.rb +2 -10
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
- data/lib/action_dispatch/railtie.rb +15 -1
- data/lib/action_dispatch/routing.rb +1 -2
- data/lib/action_dispatch/routing/mapper.rb +108 -107
- data/lib/action_dispatch/routing/redirection.rb +63 -69
- data/lib/action_dispatch/routing/route_set.rb +75 -43
- data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
- data/lib/action_dispatch/routing/url_for.rb +3 -3
- data/lib/action_dispatch/testing/assertions/response.rb +5 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
- data/lib/action_dispatch/testing/integration.rb +8 -25
- data/lib/action_dispatch/testing/test_process.rb +3 -2
- data/lib/action_dispatch/testing/test_request.rb +4 -23
- data/lib/action_pack/version.rb +3 -3
- data/lib/action_view.rb +1 -5
- data/lib/action_view/asset_paths.rb +7 -8
- data/lib/action_view/base.rb +7 -5
- data/lib/action_view/helpers/asset_paths.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
- data/lib/action_view/helpers/capture_helper.rb +3 -3
- data/lib/action_view/helpers/controller_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +26 -18
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +71 -13
- data/lib/action_view/helpers/form_options_helper.rb +65 -34
- data/lib/action_view/helpers/form_tag_helper.rb +24 -18
- data/lib/action_view/helpers/javascript_helper.rb +12 -3
- data/lib/action_view/helpers/number_helper.rb +3 -2
- data/lib/action_view/helpers/record_tag_helper.rb +51 -5
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +6 -7
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +5 -4
- data/lib/action_view/helpers/url_helper.rb +19 -11
- data/lib/action_view/locale/en.yml +6 -0
- data/lib/action_view/log_subscriber.rb +1 -1
- data/lib/action_view/lookup_context.rb +123 -125
- data/lib/action_view/path_set.rb +60 -13
- data/lib/action_view/renderer/abstract_renderer.rb +16 -11
- data/lib/action_view/renderer/partial_renderer.rb +59 -40
- data/lib/action_view/renderer/template_renderer.rb +29 -17
- data/lib/action_view/template.rb +0 -1
- data/lib/action_view/template/error.rb +6 -5
- data/lib/action_view/template/handlers.rb +0 -6
- data/lib/action_view/template/handlers/builder.rb +10 -1
- data/lib/action_view/template/handlers/erb.rb +2 -2
- data/lib/action_view/template/resolver.rb +20 -31
- data/lib/action_view/test_case.rb +7 -10
- data/lib/sprockets/assets.rake +1 -1
- data/lib/sprockets/bootstrap.rb +3 -31
- data/lib/sprockets/compressors.rb +69 -7
- data/lib/sprockets/helpers/rails_helper.rb +6 -11
- data/lib/sprockets/railtie.rb +1 -0
- data/lib/sprockets/static_compiler.rb +0 -3
- metadata +57 -86
- checksums.yaml +0 -7
- data/lib/action_dispatch/middleware/closed_error.rb +0 -7
- data/lib/action_dispatch/routing/route.rb +0 -67
- data/lib/action_view/template/handler.rb +0 -49
@@ -6,13 +6,14 @@ module ActionController
|
|
6
6
|
define_method(:inherited) do |klass|
|
7
7
|
super(klass)
|
8
8
|
|
9
|
-
if namespace = klass.parents.detect {|m| m.respond_to?(:
|
10
|
-
paths = namespace.
|
9
|
+
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
|
10
|
+
paths = namespace.railtie_helpers_paths
|
11
11
|
else
|
12
|
-
paths = app.
|
12
|
+
paths = app.helpers_paths
|
13
13
|
end
|
14
14
|
|
15
15
|
klass.helpers_path = paths
|
16
|
+
|
16
17
|
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
|
17
18
|
klass.helper :all
|
18
19
|
end
|
@@ -14,9 +14,9 @@ module ActionController
|
|
14
14
|
# <% end %> </div>
|
15
15
|
#
|
16
16
|
# # controller
|
17
|
-
# def
|
17
|
+
# def update
|
18
18
|
# post = Post.find(params[:id])
|
19
|
-
# post.
|
19
|
+
# post.update_attributes(params[:post])
|
20
20
|
#
|
21
21
|
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
|
22
22
|
# end
|
@@ -40,7 +40,7 @@ module ActionController
|
|
40
40
|
# dom_class(post, :edit) # => "edit_post"
|
41
41
|
# dom_class(Person, :edit) # => "edit_person"
|
42
42
|
def dom_class(record_or_class, prefix = nil)
|
43
|
-
singular = ActiveModel::Naming.
|
43
|
+
singular = ActiveModel::Naming.param_key(record_or_class)
|
44
44
|
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
|
45
45
|
end
|
46
46
|
|
@@ -67,7 +67,7 @@ module ActionController
|
|
67
67
|
# This can be overwritten to customize the default generated string representation if desired.
|
68
68
|
# If you need to read back a key from a dom_id in order to query for the underlying database record,
|
69
69
|
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
|
70
|
-
# on the default implementation (which just joins all key attributes with '
|
70
|
+
# on the default implementation (which just joins all key attributes with '_') or on your own
|
71
71
|
# overwritten version of the method. By default, this implementation passes the key string through a
|
72
72
|
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
|
73
73
|
# make sure yourself that your dom ids are valid, in case you overwrite this method.
|
@@ -79,13 +79,28 @@ module ActionController
|
|
79
79
|
"expecting <?> but rendering with <?>",
|
80
80
|
options, rendered.keys.join(', '))
|
81
81
|
assert_block(msg) do
|
82
|
-
if options
|
83
|
-
@templates.blank?
|
84
|
-
else
|
82
|
+
if options
|
85
83
|
rendered.any? { |t,num| t.match(options) }
|
84
|
+
else
|
85
|
+
@templates.blank?
|
86
86
|
end
|
87
87
|
end
|
88
88
|
when Hash
|
89
|
+
if expected_layout = options[:layout]
|
90
|
+
msg = build_message(message,
|
91
|
+
"expecting layout <?> but action rendered <?>",
|
92
|
+
expected_layout, @layouts.keys)
|
93
|
+
|
94
|
+
case expected_layout
|
95
|
+
when String
|
96
|
+
assert(@layouts.keys.include?(expected_layout), msg)
|
97
|
+
when Regexp
|
98
|
+
assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
|
99
|
+
when nil
|
100
|
+
assert(@layouts.empty?, msg)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
89
104
|
if expected_partial = options[:partial]
|
90
105
|
if expected_locals = options[:locals]
|
91
106
|
actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
|
@@ -98,19 +113,6 @@ module ActionController
|
|
98
113
|
"expecting ? to be rendered ? time(s) but rendered ? time(s)",
|
99
114
|
expected_partial, expected_count, actual_count)
|
100
115
|
assert(actual_count == expected_count.to_i, msg)
|
101
|
-
elsif options.key?(:layout)
|
102
|
-
msg = build_message(message,
|
103
|
-
"expecting layout <?> but action rendered <?>",
|
104
|
-
expected_layout, @layouts.keys)
|
105
|
-
|
106
|
-
case layout = options[:layout]
|
107
|
-
when String
|
108
|
-
assert(@layouts.include?(expected_layout), msg)
|
109
|
-
when Regexp
|
110
|
-
assert(@layouts.any? {|l| l =~ layout }, msg)
|
111
|
-
when nil
|
112
|
-
assert(@layouts.empty?, msg)
|
113
|
-
end
|
114
116
|
else
|
115
117
|
msg = build_message(message,
|
116
118
|
"expecting partial <?> but action rendered <?>",
|
@@ -145,23 +147,17 @@ module ActionController
|
|
145
147
|
extra_keys = routes.extra_keys(parameters)
|
146
148
|
non_path_parameters = get? ? query_parameters : request_parameters
|
147
149
|
parameters.each do |key, value|
|
148
|
-
if value.is_a?
|
149
|
-
value = value.
|
150
|
-
elsif value.is_a?
|
151
|
-
value =
|
152
|
-
elsif value.
|
150
|
+
if value.is_a? Fixnum
|
151
|
+
value = value.to_s
|
152
|
+
elsif value.is_a? Array
|
153
|
+
value = Result.new(value.map { |v| v.is_a?(String) ? v.dup : v })
|
154
|
+
elsif value.is_a? String
|
153
155
|
value = value.dup
|
154
156
|
end
|
155
157
|
|
156
158
|
if extra_keys.include?(key.to_sym)
|
157
159
|
non_path_parameters[key] = value
|
158
160
|
else
|
159
|
-
if value.is_a?(Array)
|
160
|
-
value = Result.new(value.map(&:to_param))
|
161
|
-
else
|
162
|
-
value = value.to_param
|
163
|
-
end
|
164
|
-
|
165
161
|
path_parameters[key.to_s] = value
|
166
162
|
end
|
167
163
|
end
|
@@ -181,10 +177,6 @@ module ActionController
|
|
181
177
|
end
|
182
178
|
|
183
179
|
def recycle!
|
184
|
-
write_cookies!
|
185
|
-
@env.delete('HTTP_COOKIE') if @cookies.blank?
|
186
|
-
@env.delete('action_dispatch.cookies')
|
187
|
-
@cookies = nil
|
188
180
|
@formats = nil
|
189
181
|
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
190
182
|
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
@@ -192,6 +184,14 @@ module ActionController
|
|
192
184
|
@method = @request_method = nil
|
193
185
|
@fullpath = @ip = @remote_ip = @protocol = nil
|
194
186
|
@env['action_dispatch.request.query_parameters'] = {}
|
187
|
+
@set_cookies ||= {}
|
188
|
+
@set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
|
189
|
+
deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
|
190
|
+
@set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
|
191
|
+
cookie_jar.update(rack_cookies)
|
192
|
+
cookie_jar.update(cookies)
|
193
|
+
cookie_jar.update(@set_cookies)
|
194
|
+
cookie_jar.recycle!
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
@@ -212,7 +212,7 @@ module ActionController
|
|
212
212
|
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
|
213
213
|
|
214
214
|
def initialize(session = {})
|
215
|
-
|
215
|
+
super(nil, nil)
|
216
216
|
replace(session.stringify_keys)
|
217
217
|
@loaded = true
|
218
218
|
end
|
@@ -298,27 +298,26 @@ module ActionController
|
|
298
298
|
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
|
299
299
|
# assert flash.empty? # makes sure that there's nothing in the flash
|
300
300
|
#
|
301
|
-
# For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To
|
301
|
+
# For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
|
302
302
|
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
|
303
|
-
# So assigns(:person) will work just like assigns["person"]
|
303
|
+
# So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
|
304
304
|
#
|
305
|
-
# On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url
|
305
|
+
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
|
306
306
|
#
|
307
307
|
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
|
308
308
|
# action call which can then be asserted against.
|
309
309
|
#
|
310
|
-
# == Manipulating
|
310
|
+
# == Manipulating session and cookie variables
|
311
311
|
#
|
312
|
-
#
|
313
|
-
#
|
314
|
-
# and cookies, though. For sessions, you just do:
|
312
|
+
# Sometimes you need to set up the session and cookie variables for a test.
|
313
|
+
# To do this just assign a value to the session or cookie collection:
|
315
314
|
#
|
316
|
-
#
|
317
|
-
#
|
315
|
+
# session[:key] = "value"
|
316
|
+
# cookies[:key] = "value"
|
318
317
|
#
|
319
|
-
# To clear the cookies for a test just clear the
|
318
|
+
# To clear the cookies for a test just clear the cookie collection:
|
320
319
|
#
|
321
|
-
#
|
320
|
+
# cookies.clear
|
322
321
|
#
|
323
322
|
# == \Testing named routes
|
324
323
|
#
|
@@ -336,9 +335,21 @@ module ActionController
|
|
336
335
|
module ClassMethods
|
337
336
|
|
338
337
|
# Sets the controller class name. Useful if the name can't be inferred from test class.
|
339
|
-
#
|
338
|
+
# Normalizes +controller_class+ before using. Examples:
|
339
|
+
#
|
340
|
+
# tests WidgetController
|
341
|
+
# tests :widget
|
342
|
+
# tests 'widget'
|
343
|
+
#
|
340
344
|
def tests(controller_class)
|
341
|
-
|
345
|
+
case controller_class
|
346
|
+
when String, Symbol
|
347
|
+
self.controller_class = "#{controller_class.to_s.underscore}_controller".camelize.constantize
|
348
|
+
when Class
|
349
|
+
self.controller_class = controller_class
|
350
|
+
else
|
351
|
+
raise ArgumentError, "controller class must be a String, Symbol, or Class"
|
352
|
+
end
|
342
353
|
end
|
343
354
|
|
344
355
|
def controller_class=(new_class)
|
@@ -355,9 +366,7 @@ module ActionController
|
|
355
366
|
end
|
356
367
|
|
357
368
|
def determine_default_controller_class(name)
|
358
|
-
name.sub(/Test$/, '').
|
359
|
-
rescue NameError
|
360
|
-
nil
|
369
|
+
name.sub(/Test$/, '').safe_constantize
|
361
370
|
end
|
362
371
|
|
363
372
|
def prepare_controller_class(new_class)
|
@@ -417,7 +426,7 @@ module ActionController
|
|
417
426
|
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
|
418
427
|
# Ensure that numbers and symbols passed as params are converted to
|
419
428
|
# proper params, as is the case when engaging rack.
|
420
|
-
parameters = paramify_values(parameters)
|
429
|
+
parameters = paramify_values(parameters)
|
421
430
|
|
422
431
|
# Sanity check for required instance variables so we can give an
|
423
432
|
# understandable error message.
|
@@ -445,15 +454,16 @@ module ActionController
|
|
445
454
|
|
446
455
|
@request.session = ActionController::TestSession.new(session) if session
|
447
456
|
@request.session["flash"] = @request.flash.update(flash || {})
|
457
|
+
@request.session["flash"].sweep
|
448
458
|
|
449
459
|
@controller.request = @request
|
460
|
+
@controller.params.merge!(parameters)
|
450
461
|
build_request_uri(action, parameters)
|
451
462
|
@controller.class.class_eval { include Testing }
|
452
463
|
@controller.recycle!
|
453
464
|
@controller.process_with_new_base_test(@request, @response)
|
454
465
|
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
|
455
466
|
@request.session.delete('flash') if @request.session['flash'].blank?
|
456
|
-
@request.cookies.merge!(@response.cookies)
|
457
467
|
@response
|
458
468
|
end
|
459
469
|
|
@@ -503,12 +513,6 @@ module ActionController
|
|
503
513
|
@request.env["QUERY_STRING"] = query_string || ""
|
504
514
|
end
|
505
515
|
end
|
506
|
-
|
507
|
-
def html_format?(parameters)
|
508
|
-
return true unless parameters.is_a?(Hash)
|
509
|
-
format = Mime[parameters[:format]]
|
510
|
-
format.nil? || format.html?
|
511
|
-
end
|
512
516
|
end
|
513
517
|
|
514
518
|
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
|
@@ -66,7 +66,7 @@ module HTML
|
|
66
66
|
|
67
67
|
# A regular expression of the valid characters used to separate protocols like
|
68
68
|
# the ':' in 'http://foo.com'
|
69
|
-
self.protocol_separator = /:|(�*58)|(p)|(
|
69
|
+
self.protocol_separator = /:|(�*58)|(p)|(%|%)3A/
|
70
70
|
|
71
71
|
# Specifies a Set of HTML attributes that can have URIs.
|
72
72
|
self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
|
@@ -104,14 +104,14 @@ module HTML
|
|
104
104
|
# Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
|
105
105
|
self.shorthand_css_properties = Set.new(%w(background border margin padding))
|
106
106
|
|
107
|
-
# Sanitizes a block of css code.
|
107
|
+
# Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
|
108
108
|
def sanitize_css(style)
|
109
109
|
# disallow urls
|
110
110
|
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
|
111
111
|
|
112
112
|
# gauntlet
|
113
|
-
if style !~
|
114
|
-
style !~
|
113
|
+
if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
|
114
|
+
style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
|
115
115
|
return ''
|
116
116
|
end
|
117
117
|
|
@@ -122,7 +122,7 @@ module HTML
|
|
122
122
|
elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
|
123
123
|
unless val.split().any? do |keyword|
|
124
124
|
!allowed_css_keywords.include?(keyword) &&
|
125
|
-
keyword !~
|
125
|
+
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
|
126
126
|
end
|
127
127
|
clean << prop + ': ' + val + ';'
|
128
128
|
end
|
@@ -171,7 +171,7 @@ module HTML
|
|
171
171
|
|
172
172
|
def contains_bad_protocols?(attr_name, value)
|
173
173
|
uri_attributes.include?(attr_name) &&
|
174
|
-
(value =~ /(^[^\/:]*):|(�*58)|(p)|(
|
174
|
+
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
data/lib/action_dispatch.rb
CHANGED
@@ -47,12 +47,16 @@ module ActionDispatch
|
|
47
47
|
end
|
48
48
|
|
49
49
|
autoload_under 'middleware' do
|
50
|
+
autoload :RequestId
|
50
51
|
autoload :BestStandardsSupport
|
51
52
|
autoload :Callbacks
|
52
53
|
autoload :Cookies
|
54
|
+
autoload :DebugExceptions
|
55
|
+
autoload :ExceptionWrapper
|
53
56
|
autoload :Flash
|
54
57
|
autoload :Head
|
55
58
|
autoload :ParamsParser
|
59
|
+
autoload :PublicExceptions
|
56
60
|
autoload :Reloader
|
57
61
|
autoload :RemoteIp
|
58
62
|
autoload :Rescue
|
@@ -60,7 +64,6 @@ module ActionDispatch
|
|
60
64
|
autoload :Static
|
61
65
|
end
|
62
66
|
|
63
|
-
autoload :ClosedError, 'action_dispatch/middleware/closed_error'
|
64
67
|
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
|
65
68
|
autoload :Routing
|
66
69
|
|
@@ -82,6 +85,7 @@ module ActionDispatch
|
|
82
85
|
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
|
83
86
|
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
|
84
87
|
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
|
88
|
+
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
|
85
89
|
end
|
86
90
|
|
87
91
|
autoload_under 'testing' do
|
@@ -4,14 +4,18 @@ module ActionDispatch
|
|
4
4
|
module Http
|
5
5
|
module Cache
|
6
6
|
module Request
|
7
|
+
|
8
|
+
HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
|
9
|
+
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
|
10
|
+
|
7
11
|
def if_modified_since
|
8
|
-
if since = env[
|
12
|
+
if since = env[HTTP_IF_MODIFIED_SINCE]
|
9
13
|
Time.rfc2822(since) rescue nil
|
10
14
|
end
|
11
15
|
end
|
12
16
|
|
13
17
|
def if_none_match
|
14
|
-
env[
|
18
|
+
env[HTTP_IF_NONE_MATCH]
|
15
19
|
end
|
16
20
|
|
17
21
|
def not_modified?(modified_at)
|
@@ -43,31 +47,35 @@ module ActionDispatch
|
|
43
47
|
alias :etag? :etag
|
44
48
|
|
45
49
|
def last_modified
|
46
|
-
if last = headers[
|
50
|
+
if last = headers[LAST_MODIFIED]
|
47
51
|
Time.httpdate(last)
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
51
55
|
def last_modified?
|
52
|
-
headers.include?(
|
56
|
+
headers.include?(LAST_MODIFIED)
|
53
57
|
end
|
54
58
|
|
55
59
|
def last_modified=(utc_time)
|
56
|
-
headers[
|
60
|
+
headers[LAST_MODIFIED] = utc_time.httpdate
|
57
61
|
end
|
58
62
|
|
59
63
|
def etag=(etag)
|
60
64
|
key = ActiveSupport::Cache.expand_cache_key(etag)
|
61
|
-
@etag = self[
|
65
|
+
@etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
|
62
66
|
end
|
63
67
|
|
64
68
|
private
|
65
69
|
|
70
|
+
LAST_MODIFIED = "Last-Modified".freeze
|
71
|
+
ETAG = "ETag".freeze
|
72
|
+
CACHE_CONTROL = "Cache-Control".freeze
|
73
|
+
|
66
74
|
def prepare_cache_control!
|
67
75
|
@cache_control = {}
|
68
|
-
@etag = self[
|
76
|
+
@etag = self[ETAG]
|
69
77
|
|
70
|
-
if cache_control = self[
|
78
|
+
if cache_control = self[CACHE_CONTROL]
|
71
79
|
cache_control.split(/,\s*/).each do |segment|
|
72
80
|
first, last = segment.split("=")
|
73
81
|
@cache_control[first.to_sym] = last || true
|
@@ -81,28 +89,32 @@ module ActionDispatch
|
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
84
|
-
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
|
92
|
+
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
|
93
|
+
NO_CACHE = "no-cache".freeze
|
94
|
+
PUBLIC = "public".freeze
|
95
|
+
PRIVATE = "private".freeze
|
96
|
+
MUST_REVALIDATE = "must-revalidate".freeze
|
85
97
|
|
86
98
|
def set_conditional_cache_control!
|
87
|
-
return if self[
|
99
|
+
return if self[CACHE_CONTROL].present?
|
88
100
|
|
89
101
|
control = @cache_control
|
90
102
|
|
91
103
|
if control.empty?
|
92
|
-
headers[
|
104
|
+
headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
|
93
105
|
elsif control[:no_cache]
|
94
|
-
headers[
|
106
|
+
headers[CACHE_CONTROL] = NO_CACHE
|
95
107
|
else
|
96
108
|
extras = control[:extras]
|
97
109
|
max_age = control[:max_age]
|
98
110
|
|
99
111
|
options = []
|
100
112
|
options << "max-age=#{max_age.to_i}" if max_age
|
101
|
-
options << (control[:public] ?
|
102
|
-
options <<
|
113
|
+
options << (control[:public] ? PUBLIC : PRIVATE)
|
114
|
+
options << MUST_REVALIDATE if control[:must_revalidate]
|
103
115
|
options.concat(extras) if extras
|
104
116
|
|
105
|
-
headers[
|
117
|
+
headers[CACHE_CONTROL] = options.join(", ")
|
106
118
|
end
|
107
119
|
end
|
108
120
|
end
|