actionpack 3.2.19 → 4.2.11.3

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.

Files changed (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +412 -503
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +11 -294
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +52 -18
  7. data/lib/abstract_controller/callbacks.rb +87 -89
  8. data/lib/abstract_controller/collector.rb +17 -3
  9. data/lib/abstract_controller/helpers.rb +41 -14
  10. data/lib/abstract_controller/logger.rb +1 -2
  11. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  12. data/lib/abstract_controller/rendering.rb +65 -118
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +7 -7
  15. data/lib/abstract_controller.rb +2 -10
  16. data/lib/action_controller/base.rb +61 -28
  17. data/lib/action_controller/caching/fragments.rb +30 -54
  18. data/lib/action_controller/caching.rb +38 -35
  19. data/lib/action_controller/log_subscriber.rb +35 -18
  20. data/lib/action_controller/metal/conditional_get.rb +103 -34
  21. data/lib/action_controller/metal/data_streaming.rb +20 -26
  22. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  23. data/lib/action_controller/metal/exceptions.rb +19 -6
  24. data/lib/action_controller/metal/flash.rb +41 -9
  25. data/lib/action_controller/metal/force_ssl.rb +70 -12
  26. data/lib/action_controller/metal/head.rb +30 -7
  27. data/lib/action_controller/metal/helpers.rb +11 -11
  28. data/lib/action_controller/metal/hide_actions.rb +0 -1
  29. data/lib/action_controller/metal/http_authentication.rb +140 -94
  30. data/lib/action_controller/metal/implicit_render.rb +1 -1
  31. data/lib/action_controller/metal/instrumentation.rb +11 -7
  32. data/lib/action_controller/metal/live.rb +328 -0
  33. data/lib/action_controller/metal/mime_responds.rb +161 -152
  34. data/lib/action_controller/metal/params_wrapper.rb +126 -81
  35. data/lib/action_controller/metal/rack_delegation.rb +10 -4
  36. data/lib/action_controller/metal/redirecting.rb +44 -41
  37. data/lib/action_controller/metal/renderers.rb +48 -19
  38. data/lib/action_controller/metal/rendering.rb +46 -11
  39. data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
  40. data/lib/action_controller/metal/streaming.rb +30 -38
  41. data/lib/action_controller/metal/strong_parameters.rb +669 -0
  42. data/lib/action_controller/metal/testing.rb +12 -18
  43. data/lib/action_controller/metal/url_for.rb +31 -29
  44. data/lib/action_controller/metal.rb +31 -40
  45. data/lib/action_controller/model_naming.rb +12 -0
  46. data/lib/action_controller/railtie.rb +38 -18
  47. data/lib/action_controller/railties/helpers.rb +22 -0
  48. data/lib/action_controller/test_case.rb +359 -173
  49. data/lib/action_controller.rb +9 -16
  50. data/lib/action_dispatch/http/cache.rb +64 -11
  51. data/lib/action_dispatch/http/filter_parameters.rb +20 -10
  52. data/lib/action_dispatch/http/filter_redirect.rb +38 -0
  53. data/lib/action_dispatch/http/headers.rb +85 -17
  54. data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
  55. data/lib/action_dispatch/http/mime_type.rb +167 -114
  56. data/lib/action_dispatch/http/mime_types.rb +2 -1
  57. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  58. data/lib/action_dispatch/http/parameters.rb +30 -46
  59. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  60. data/lib/action_dispatch/http/request.rb +108 -45
  61. data/lib/action_dispatch/http/response.rb +247 -48
  62. data/lib/action_dispatch/http/upload.rb +60 -29
  63. data/lib/action_dispatch/http/url.rb +135 -45
  64. data/lib/action_dispatch/journey/backwards.rb +5 -0
  65. data/lib/action_dispatch/journey/formatter.rb +166 -0
  66. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  67. data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
  68. data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -0
  69. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  70. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  71. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  72. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  73. data/lib/action_dispatch/journey/nodes/node.rb +128 -0
  74. data/lib/action_dispatch/journey/parser.rb +198 -0
  75. data/lib/action_dispatch/journey/parser.y +49 -0
  76. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  77. data/lib/action_dispatch/journey/path/pattern.rb +193 -0
  78. data/lib/action_dispatch/journey/route.rb +125 -0
  79. data/lib/action_dispatch/journey/router/strexp.rb +27 -0
  80. data/lib/action_dispatch/journey/router/utils.rb +93 -0
  81. data/lib/action_dispatch/journey/router.rb +144 -0
  82. data/lib/action_dispatch/journey/routes.rb +80 -0
  83. data/lib/action_dispatch/journey/scanner.rb +61 -0
  84. data/lib/action_dispatch/journey/visitors.rb +221 -0
  85. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  86. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  87. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  88. data/lib/action_dispatch/journey.rb +5 -0
  89. data/lib/action_dispatch/middleware/callbacks.rb +16 -11
  90. data/lib/action_dispatch/middleware/cookies.rb +346 -125
  91. data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
  92. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
  93. data/lib/action_dispatch/middleware/flash.rb +85 -72
  94. data/lib/action_dispatch/middleware/params_parser.rb +16 -31
  95. data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
  96. data/lib/action_dispatch/middleware/reloader.rb +16 -7
  97. data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
  98. data/lib/action_dispatch/middleware/request_id.rb +3 -7
  99. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  100. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  101. data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
  102. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  103. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
  104. data/lib/action_dispatch/middleware/ssl.rb +72 -0
  105. data/lib/action_dispatch/middleware/stack.rb +6 -1
  106. data/lib/action_dispatch/middleware/static.rb +80 -23
  107. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
  108. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  111. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  112. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +133 -5
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  125. data/lib/action_dispatch/railtie.rb +19 -6
  126. data/lib/action_dispatch/request/session.rb +193 -0
  127. data/lib/action_dispatch/request/utils.rb +35 -0
  128. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  129. data/lib/action_dispatch/routing/inspector.rb +234 -0
  130. data/lib/action_dispatch/routing/mapper.rb +897 -436
  131. data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
  132. data/lib/action_dispatch/routing/redirection.rb +97 -37
  133. data/lib/action_dispatch/routing/route_set.rb +432 -239
  134. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  135. data/lib/action_dispatch/routing/url_for.rb +63 -34
  136. data/lib/action_dispatch/routing.rb +57 -89
  137. data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
  138. data/lib/action_dispatch/testing/assertions/response.rb +24 -38
  139. data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
  140. data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
  141. data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
  142. data/lib/action_dispatch/testing/assertions.rb +11 -7
  143. data/lib/action_dispatch/testing/integration.rb +88 -72
  144. data/lib/action_dispatch/testing/test_process.rb +9 -6
  145. data/lib/action_dispatch/testing/test_request.rb +13 -9
  146. data/lib/action_dispatch/testing/test_response.rb +1 -5
  147. data/lib/action_dispatch.rb +24 -21
  148. data/lib/action_pack/gem_version.rb +15 -0
  149. data/lib/action_pack/version.rb +5 -7
  150. data/lib/action_pack.rb +1 -1
  151. metadata +181 -292
  152. data/lib/abstract_controller/layouts.rb +0 -423
  153. data/lib/abstract_controller/view_paths.rb +0 -96
  154. data/lib/action_controller/caching/actions.rb +0 -185
  155. data/lib/action_controller/caching/pages.rb +0 -187
  156. data/lib/action_controller/caching/sweeping.rb +0 -97
  157. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  158. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  159. data/lib/action_controller/deprecated.rb +0 -3
  160. data/lib/action_controller/metal/compatibility.rb +0 -65
  161. data/lib/action_controller/metal/responder.rb +0 -286
  162. data/lib/action_controller/metal/session_management.rb +0 -14
  163. data/lib/action_controller/railties/paths.rb +0 -25
  164. data/lib/action_controller/record_identifier.rb +0 -85
  165. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  166. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  167. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  168. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  169. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  170. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  171. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  172. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  173. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  174. data/lib/action_dispatch/middleware/head.rb +0 -18
  175. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  176. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  177. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  178. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  179. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  180. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  181. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  182. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  183. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  184. data/lib/action_view/asset_paths.rb +0 -142
  185. data/lib/action_view/base.rb +0 -220
  186. data/lib/action_view/buffers.rb +0 -43
  187. data/lib/action_view/context.rb +0 -36
  188. data/lib/action_view/flows.rb +0 -79
  189. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  190. data/lib/action_view/helpers/asset_paths.rb +0 -7
  191. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  192. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  193. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  194. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  195. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  196. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  197. data/lib/action_view/helpers/cache_helper.rb +0 -64
  198. data/lib/action_view/helpers/capture_helper.rb +0 -203
  199. data/lib/action_view/helpers/controller_helper.rb +0 -25
  200. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  201. data/lib/action_view/helpers/date_helper.rb +0 -1062
  202. data/lib/action_view/helpers/debug_helper.rb +0 -40
  203. data/lib/action_view/helpers/form_helper.rb +0 -1486
  204. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  205. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  206. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  207. data/lib/action_view/helpers/number_helper.rb +0 -622
  208. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  209. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  210. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  211. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  212. data/lib/action_view/helpers/tag_helper.rb +0 -160
  213. data/lib/action_view/helpers/text_helper.rb +0 -426
  214. data/lib/action_view/helpers/translation_helper.rb +0 -91
  215. data/lib/action_view/helpers/url_helper.rb +0 -693
  216. data/lib/action_view/helpers.rb +0 -60
  217. data/lib/action_view/locale/en.yml +0 -160
  218. data/lib/action_view/log_subscriber.rb +0 -28
  219. data/lib/action_view/lookup_context.rb +0 -254
  220. data/lib/action_view/path_set.rb +0 -89
  221. data/lib/action_view/railtie.rb +0 -55
  222. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  223. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  224. data/lib/action_view/renderer/renderer.rb +0 -54
  225. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  226. data/lib/action_view/renderer/template_renderer.rb +0 -94
  227. data/lib/action_view/template/error.rb +0 -128
  228. data/lib/action_view/template/handlers/builder.rb +0 -26
  229. data/lib/action_view/template/handlers/erb.rb +0 -125
  230. data/lib/action_view/template/handlers.rb +0 -50
  231. data/lib/action_view/template/resolver.rb +0 -272
  232. data/lib/action_view/template/text.rb +0 -30
  233. data/lib/action_view/template.rb +0 -337
  234. data/lib/action_view/test_case.rb +0 -245
  235. data/lib/action_view/testing/resolvers.rb +0 -50
  236. data/lib/action_view.rb +0 -84
  237. data/lib/sprockets/assets.rake +0 -99
  238. data/lib/sprockets/bootstrap.rb +0 -37
  239. data/lib/sprockets/compressors.rb +0 -83
  240. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  241. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  242. data/lib/sprockets/helpers.rb +0 -6
  243. data/lib/sprockets/railtie.rb +0 -62
  244. data/lib/sprockets/static_compiler.rb +0 -56
@@ -2,41 +2,33 @@ require 'fileutils'
2
2
  require 'uri'
3
3
  require 'set'
4
4
 
5
- module ActionController #:nodoc:
5
+ module ActionController
6
6
  # \Caching is a cheap way of speeding up slow applications by keeping the result of
7
7
  # calculations, renderings, and database calls around for subsequent requests.
8
- # Action Controller affords you three approaches in varying levels of granularity:
9
- # Page, Action, Fragment.
10
8
  #
11
- # You can read more about each approach and the sweeping assistance by clicking the
12
- # modules below.
9
+ # You can read more about each approach by clicking the modules below.
13
10
  #
14
- # Note: To turn off all caching and sweeping, set
15
- # config.action_controller.perform_caching = false.
11
+ # Note: To turn off all caching, set
12
+ # config.action_controller.perform_caching = false
16
13
  #
17
14
  # == \Caching stores
18
15
  #
19
16
  # All the caching stores from ActiveSupport::Cache are available to be used as backends
20
- # for Action Controller caching. This setting only affects action and fragment caching
21
- # as page caching is always written to disk.
17
+ # for Action Controller caching.
22
18
  #
23
- # Configuration examples (MemoryStore is the default):
19
+ # Configuration examples (FileStore is the default):
24
20
  #
25
21
  # config.action_controller.cache_store = :memory_store
26
- # config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
27
- # config.action_controller.cache_store = :mem_cache_store, "localhost"
28
- # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
29
- # config.action_controller.cache_store = MyOwnStore.new("parameter")
22
+ # config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
23
+ # config.action_controller.cache_store = :mem_cache_store, 'localhost'
24
+ # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
25
+ # config.action_controller.cache_store = MyOwnStore.new('parameter')
30
26
  module Caching
31
27
  extend ActiveSupport::Concern
32
28
  extend ActiveSupport::Autoload
33
29
 
34
30
  eager_autoload do
35
- autoload :Actions
36
31
  autoload :Fragments
37
- autoload :Pages
38
- autoload :Sweeper, 'action_controller/caching/sweeping'
39
- autoload :Sweeping, 'action_controller/caching/sweeping'
40
32
  end
41
33
 
42
34
  module ConfigMethods
@@ -48,39 +40,50 @@ module ActionController #:nodoc:
48
40
  config.cache_store = ActiveSupport::Cache.lookup_store(store)
49
41
  end
50
42
 
51
- private
52
-
53
- def cache_configured?
54
- perform_caching && cache_store
55
- end
43
+ private
44
+ def cache_configured?
45
+ perform_caching && cache_store
46
+ end
56
47
  end
57
48
 
58
49
  include RackDelegation
59
50
  include AbstractController::Callbacks
60
51
 
61
52
  include ConfigMethods
62
- include Pages, Actions, Fragments
63
- include Sweeping if defined?(ActiveRecord)
53
+ include Fragments
64
54
 
65
55
  included do
66
56
  extend ConfigMethods
67
57
 
58
+ config_accessor :default_static_extension
59
+ self.default_static_extension ||= '.html'
60
+
68
61
  config_accessor :perform_caching
69
62
  self.perform_caching = true if perform_caching.nil?
70
- end
71
63
 
72
- def caching_allowed?
73
- request.get? && response.status == 200
64
+ class_attribute :_view_cache_dependencies
65
+ self._view_cache_dependencies = []
66
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
74
67
  end
75
68
 
76
- protected
77
- # Convenience accessor
78
- def cache(key, options = {}, &block)
79
- if cache_configured?
80
- cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
81
- else
82
- yield
69
+ module ClassMethods
70
+ def view_cache_dependency(&dependency)
71
+ self._view_cache_dependencies += [dependency]
83
72
  end
84
73
  end
74
+
75
+ def view_cache_dependencies
76
+ self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
77
+ end
78
+
79
+ protected
80
+ # Convenience accessor.
81
+ def cache(key, options = {}, &block)
82
+ if cache_configured?
83
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
84
+ else
85
+ yield
86
+ end
87
+ end
85
88
  end
86
89
  end
@@ -1,10 +1,10 @@
1
- require 'active_support/core_ext/object/blank'
2
-
3
1
  module ActionController
4
2
  class LogSubscriber < ActiveSupport::LogSubscriber
5
3
  INTERNAL_PARAMS = %w(controller action format _method only_path)
6
4
 
7
5
  def start_processing(event)
6
+ return unless logger.info?
7
+
8
8
  payload = event.payload
9
9
  params = payload[:params].except(*INTERNAL_PARAMS)
10
10
  format = payload[:format]
@@ -15,44 +15,61 @@ module ActionController
15
15
  end
16
16
 
17
17
  def process_action(event)
18
- payload = event.payload
19
- additions = ActionController::Base.log_process_action(payload)
18
+ info do
19
+ payload = event.payload
20
+ additions = ActionController::Base.log_process_action(payload)
20
21
 
21
- status = payload[:status]
22
- if status.nil? && payload[:exception].present?
23
- exception_class_name = payload[:exception].first
24
- status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
22
+ status = payload[:status]
23
+ if status.nil? && payload[:exception].present?
24
+ exception_class_name = payload[:exception].first
25
+ status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
26
+ end
27
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
28
+ message << " (#{additions.join(" | ")})" unless additions.blank?
29
+ message
25
30
  end
26
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{format_duration(event.duration)}"
27
- message << " (#{additions.join(" | ")})" unless additions.blank?
28
-
29
- info(message)
30
31
  end
31
32
 
32
33
  def halted_callback(event)
33
- info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
34
+ info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
34
35
  end
35
36
 
36
37
  def send_file(event)
37
- info("Sent file #{event.payload[:path]} (#{format_duration(event.duration)})")
38
+ info { "Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)" }
38
39
  end
39
40
 
40
41
  def redirect_to(event)
41
- info "Redirected to #{event.payload[:location]}"
42
+ info { "Redirected to #{event.payload[:location]}" }
42
43
  end
43
44
 
44
45
  def send_data(event)
45
- info("Sent data #{event.payload[:filename]} (#{format_duration(event.duration)})")
46
+ info { "Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)" }
47
+ end
48
+
49
+ def unpermitted_parameters(event)
50
+ debug do
51
+ unpermitted_keys = event.payload[:keys]
52
+ "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
53
+ end
54
+ end
55
+
56
+ def deep_munge(event)
57
+ debug do
58
+ "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
59
+ "to nil, because it was one of [], [null] or [null, null, ...]. "\
60
+ "Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
61
+ "for more information."\
62
+ end
46
63
  end
47
64
 
48
65
  %w(write_fragment read_fragment exist_fragment?
49
66
  expire_fragment expire_page write_page).each do |method|
50
67
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
51
68
  def #{method}(event)
69
+ return unless logger.info?
52
70
  key_or_path = event.payload[:key] || event.payload[:path]
53
71
  human_name = #{method.to_s.humanize.inspect}
54
- duration = format_duration(event.duration)
55
- info("\#{human_name} \#{key_or_path} \#{duration}")
72
+ info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
56
73
  end
57
74
  METHOD
58
75
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
1
3
  module ActionController
2
4
  module ConditionalGet
3
5
  extend ActiveSupport::Concern
@@ -5,25 +7,58 @@ module ActionController
5
7
  include RackDelegation
6
8
  include Head
7
9
 
8
- # Sets the etag, last_modified, or both on the response and renders a
10
+ included do
11
+ class_attribute :etaggers
12
+ self.etaggers = []
13
+ end
14
+
15
+ module ClassMethods
16
+ # Allows you to consider additional controller-wide information when generating an ETag.
17
+ # For example, if you serve pages tailored depending on who's logged in at the moment, you
18
+ # may want to add the current user id to be part of the ETag to prevent authorized displaying
19
+ # of cached pages.
20
+ #
21
+ # class InvoicesController < ApplicationController
22
+ # etag { current_user.try :id }
23
+ #
24
+ # def show
25
+ # # Etag will differ even for the same invoice when it's viewed by a different current_user
26
+ # @invoice = Invoice.find(params[:id])
27
+ # fresh_when(@invoice)
28
+ # end
29
+ # end
30
+ def etag(&etagger)
31
+ self.etaggers += [etagger]
32
+ end
33
+ end
34
+
35
+ # Sets the +etag+, +last_modified+, or both on the response and renders a
9
36
  # <tt>304 Not Modified</tt> response if the request is already fresh.
10
37
  #
11
- # Parameters:
12
- # * <tt>:etag</tt>
13
- # * <tt>:last_modified</tt>
14
- # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
38
+ # === Parameters:
39
+ #
40
+ # * <tt>:etag</tt>.
41
+ # * <tt>:last_modified</tt>.
42
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to
43
+ # +true+ if you want your application to be cachable by other devices (proxy caches).
44
+ # * <tt>:template</tt> By default, the template digest for the current
45
+ # controller/action is included in ETags. If the action renders a
46
+ # different template, you can include its digest instead. If the action
47
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
48
+ # to skip any attempt to check for a template digest.
15
49
  #
16
- # Example:
50
+ # === Example:
17
51
  #
18
52
  # def show
19
53
  # @article = Article.find(params[:id])
20
- # fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
54
+ # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
21
55
  # end
22
56
  #
23
- # This will render the show template if the request isn't sending a matching etag or
57
+ # This will render the show template if the request isn't sending a matching ETag or
24
58
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
25
59
  #
26
- # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
60
+ # You can also just pass a record where +last_modified+ will be set by calling
61
+ # +updated_at+ and the +etag+ by passing the object itself.
27
62
  #
28
63
  # def show
29
64
  # @article = Article.find(params[:id])
@@ -34,40 +69,53 @@ module ActionController
34
69
  #
35
70
  # def show
36
71
  # @article = Article.find(params[:id])
37
- # fresh_when(@article, :public => true)
72
+ # fresh_when(@article, public: true)
38
73
  # end
74
+ #
75
+ # When rendering a different template than the default controller/action
76
+ # style, you can indicate which digest to include in the ETag:
77
+ #
78
+ # before_action { fresh_when @article, template: 'widgets/show' }
79
+ #
39
80
  def fresh_when(record_or_options, additional_options = {})
40
81
  if record_or_options.is_a? Hash
41
82
  options = record_or_options
42
- options.assert_valid_keys(:etag, :last_modified, :public)
83
+ options.assert_valid_keys(:etag, :last_modified, :public, :template)
43
84
  else
44
85
  record = record_or_options
45
- options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
86
+ options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
46
87
  end
47
88
 
48
- response.etag = options[:etag] if options[:etag]
49
- response.last_modified = options[:last_modified] if options[:last_modified]
50
- response.cache_control[:public] = true if options[:public]
89
+ response.etag = combine_etags(options) if options[:etag] || options[:template]
90
+ response.last_modified = options[:last_modified] if options[:last_modified]
91
+ response.cache_control[:public] = true if options[:public]
51
92
 
52
93
  head :not_modified if request.fresh?(response)
53
94
  end
54
95
 
55
- # Sets the etag and/or last_modified on the response and checks it against
96
+ # Sets the +etag+ and/or +last_modified+ on the response and checks it against
56
97
  # the client request. If the request doesn't match the options provided, the
57
98
  # request is considered stale and should be generated from scratch. Otherwise,
58
99
  # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
59
100
  #
60
- # Parameters:
61
- # * <tt>:etag</tt>
62
- # * <tt>:last_modified</tt>
63
- # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
101
+ # === Parameters:
64
102
  #
65
- # Example:
103
+ # * <tt>:etag</tt>.
104
+ # * <tt>:last_modified</tt>.
105
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to
106
+ # +true+ if you want your application to be cachable by other devices (proxy caches).
107
+ # * <tt>:template</tt> By default, the template digest for the current
108
+ # controller/action is included in ETags. If the action renders a
109
+ # different template, you can include its digest instead. If the action
110
+ # doesn't render a template at all, you can pass <tt>template: false</tt>
111
+ # to skip any attempt to check for a template digest.
112
+ #
113
+ # === Example:
66
114
  #
67
115
  # def show
68
116
  # @article = Article.find(params[:id])
69
117
  #
70
- # if stale?(:etag => @article, :last_modified => @article.created_at)
118
+ # if stale?(etag: @article, last_modified: @article.created_at)
71
119
  # @statistics = @article.really_expensive_call
72
120
  # respond_to do |format|
73
121
  # # all the supported formats
@@ -75,7 +123,8 @@ module ActionController
75
123
  # end
76
124
  # end
77
125
  #
78
- # You can also just pass a record where last_modified will be set by calling updated_at and the etag by passing the object itself. Example:
126
+ # You can also just pass a record where +last_modified+ will be set by calling
127
+ # +updated_at+ and the +etag+ by passing the object itself.
79
128
  #
80
129
  # def show
81
130
  # @article = Article.find(params[:id])
@@ -93,39 +142,59 @@ module ActionController
93
142
  # def show
94
143
  # @article = Article.find(params[:id])
95
144
  #
96
- # if stale?(@article, :public => true)
145
+ # if stale?(@article, public: true)
97
146
  # @statistics = @article.really_expensive_call
98
147
  # respond_to do |format|
99
148
  # # all the supported formats
100
149
  # end
101
150
  # end
102
151
  # end
152
+ #
153
+ # When rendering a different template than the default controller/action
154
+ # style, you can indicate which digest to include in the ETag:
155
+ #
156
+ # def show
157
+ # super if stale? @article, template: 'widgets/show'
158
+ # end
159
+ #
103
160
  def stale?(record_or_options, additional_options = {})
104
161
  fresh_when(record_or_options, additional_options)
105
162
  !request.fresh?(response)
106
163
  end
107
164
 
108
- # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
109
- # intermediate caches must not cache the response.
165
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
166
+ # instruction, so that intermediate caches must not cache the response.
110
167
  #
111
- # Examples:
112
168
  # expires_in 20.minutes
113
- # expires_in 3.hours, :public => true
114
- # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
169
+ # expires_in 3.hours, public: true
170
+ # expires_in 3.hours, public: true, must_revalidate: true
115
171
  #
116
172
  # This method will overwrite an existing Cache-Control header.
117
173
  # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
118
- def expires_in(seconds, options = {}) #:doc:
119
- response.cache_control.merge!(:max_age => seconds, :public => options.delete(:public))
174
+ #
175
+ # The method will also ensure a HTTP Date header for client compatibility.
176
+ def expires_in(seconds, options = {})
177
+ response.cache_control.merge!(
178
+ :max_age => seconds,
179
+ :public => options.delete(:public),
180
+ :must_revalidate => options.delete(:must_revalidate)
181
+ )
120
182
  options.delete(:private)
121
183
 
122
184
  response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
185
+ response.date = Time.now unless response.date?
123
186
  end
124
187
 
125
- # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
126
- # intermediate caches (like caching proxy servers).
127
- def expires_now #:doc:
188
+ # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
189
+ # occur by the browser or intermediate caches (like caching proxy servers).
190
+ def expires_now
128
191
  response.cache_control.replace(:no_cache => true)
129
192
  end
193
+
194
+ private
195
+ def combine_etags(options)
196
+ etags = etaggers.map { |etagger| instance_exec(options, &etagger) }.compact
197
+ etags.unshift options[:etag]
198
+ end
130
199
  end
131
200
  end
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/file/path'
2
1
  require 'action_controller/metal/exceptions'
3
2
 
4
3
  module ActionController #:nodoc:
@@ -9,15 +8,13 @@ module ActionController #:nodoc:
9
8
 
10
9
  include ActionController::Rendering
11
10
 
12
- DEFAULT_SEND_FILE_OPTIONS = {
13
- :type => 'application/octet-stream'.freeze,
14
- :disposition => 'attachment'.freeze,
15
- }.freeze
11
+ DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
12
+ DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
16
13
 
17
14
  protected
18
15
  # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
19
16
  # via the Rack::Sendfile middleware. The header to use is set via
20
- # config.action_dispatch.x_sendfile_header.
17
+ # +config.action_dispatch.x_sendfile_header+.
21
18
  # Your server can also configure this for you by setting the X-Sendfile-Type header.
22
19
  #
23
20
  # Be careful to sanitize the path parameter if it is coming from a web
@@ -50,11 +47,11 @@ module ActionController #:nodoc:
50
47
  #
51
48
  # Show a JPEG in the browser:
52
49
  #
53
- # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
50
+ # send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
54
51
  #
55
52
  # Show a 404 page in the browser:
56
53
  #
57
- # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
54
+ # send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
58
55
  #
59
56
  # Read about the other Content-* HTTP headers if you'd like to
60
57
  # provide the user with more information (such as Content-Description) in
@@ -99,7 +96,7 @@ module ActionController #:nodoc:
99
96
  end
100
97
 
101
98
  # Sends the given binary data to the browser. This method is similar to
102
- # <tt>render :text => data</tt>, but also allows you to specify whether
99
+ # <tt>render plain: data</tt>, but also allows you to specify whether
103
100
  # the browser should display the response as a file attachment (i.e. in a
104
101
  # download dialog) or as inline data. You may also set the content type,
105
102
  # the apparent file name, and other things.
@@ -120,15 +117,15 @@ module ActionController #:nodoc:
120
117
  #
121
118
  # Download a dynamically-generated tarball:
122
119
  #
123
- # send_data generate_tgz('dir'), :filename => 'dir.tgz'
120
+ # send_data generate_tgz('dir'), filename: 'dir.tgz'
124
121
  #
125
122
  # Display an image Active Record in the browser:
126
123
  #
127
- # send_data image.data, :type => image.content_type, :disposition => 'inline'
124
+ # send_data image.data, type: image.content_type, disposition: 'inline'
128
125
  #
129
126
  # See +send_file+ for more information on HTTP Content-* headers and caching.
130
127
  def send_data(data, options = {}) #:doc:
131
- send_file_headers! options.dup
128
+ send_file_headers! options
132
129
  render options.slice(:status, :content_type).merge(:text => data)
133
130
  end
134
131
 
@@ -136,15 +133,8 @@ module ActionController #:nodoc:
136
133
  def send_file_headers!(options)
137
134
  type_provided = options.has_key?(:type)
138
135
 
139
- options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
140
- [:type, :disposition].each do |arg|
141
- raise ArgumentError, ":#{arg} option required" if options[arg].nil?
142
- end
143
-
144
- disposition = options[:disposition].to_s
145
- disposition += %(; filename="#{options[:filename]}") if options[:filename]
146
-
147
- content_type = options[:type]
136
+ content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
137
+ raise ArgumentError, ":type option required" if content_type.nil?
148
138
 
149
139
  if content_type.is_a?(Symbol)
150
140
  extension = Mime[content_type]
@@ -153,15 +143,19 @@ module ActionController #:nodoc:
153
143
  else
154
144
  if !type_provided && options[:filename]
155
145
  # If type wasn't provided, try guessing from file extension.
156
- content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.tr('.','')) || content_type
146
+ content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type
157
147
  end
158
148
  self.content_type = content_type
159
149
  end
160
150
 
161
- headers.merge!(
162
- 'Content-Disposition' => disposition,
163
- 'Content-Transfer-Encoding' => 'binary'
164
- )
151
+ disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
152
+ unless disposition.nil?
153
+ disposition = disposition.to_s
154
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
155
+ headers['Content-Disposition'] = disposition
156
+ end
157
+
158
+ headers['Content-Transfer-Encoding'] = 'binary'
165
159
 
166
160
  response.sending_file = true
167
161
 
@@ -0,0 +1,50 @@
1
+ module ActionController
2
+ # When our views change, they should bubble up into HTTP cache freshness
3
+ # and bust browser caches. So the template digest for the current action
4
+ # is automatically included in the ETag.
5
+ #
6
+ # Enabled by default for apps that use Action View. Disable by setting
7
+ #
8
+ # config.action_controller.etag_with_template_digest = false
9
+ #
10
+ # Override the template to digest by passing +:template+ to +fresh_when+
11
+ # and +stale?+ calls. For example:
12
+ #
13
+ # # We're going to render widgets/show, not posts/show
14
+ # fresh_when @post, template: 'widgets/show'
15
+ #
16
+ # # We're not going to render a template, so omit it from the ETag.
17
+ # fresh_when @post, template: false
18
+ #
19
+ module EtagWithTemplateDigest
20
+ extend ActiveSupport::Concern
21
+
22
+ include ActionController::ConditionalGet
23
+
24
+ included do
25
+ class_attribute :etag_with_template_digest
26
+ self.etag_with_template_digest = true
27
+
28
+ ActiveSupport.on_load :action_view, yield: true do |action_view_base|
29
+ etag do |options|
30
+ determine_template_etag(options) if etag_with_template_digest
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+ def determine_template_etag(options)
37
+ if template = pick_template_for_etag(options)
38
+ lookup_and_digest_template(template)
39
+ end
40
+ end
41
+
42
+ def pick_template_for_etag(options)
43
+ options.fetch(:template) { "#{controller_name}/#{action_name}" }
44
+ end
45
+
46
+ def lookup_and_digest_template(template)
47
+ ActionView::Digestor.digest name: template, finder: lookup_context
48
+ end
49
+ end
50
+ end
@@ -2,6 +2,18 @@ module ActionController
2
2
  class ActionControllerError < StandardError #:nodoc:
3
3
  end
4
4
 
5
+ class BadRequest < ActionControllerError #:nodoc:
6
+ attr_reader :original_exception
7
+
8
+ def initialize(type = nil, e = nil)
9
+ return super() unless type && e
10
+
11
+ super("Invalid #{type} parameters: #{e.message}")
12
+ @original_exception = e
13
+ set_backtrace e.backtrace
14
+ end
15
+ end
16
+
5
17
  class RenderError < ActionControllerError #:nodoc:
6
18
  end
7
19
 
@@ -13,9 +25,10 @@ module ActionController
13
25
  end
14
26
  end
15
27
 
16
- class MethodNotAllowed < ActionControllerError #:nodoc:
17
- attr_reader :allowed_methods
28
+ class ActionController::UrlGenerationError < ActionControllerError #:nodoc:
29
+ end
18
30
 
31
+ class MethodNotAllowed < ActionControllerError #:nodoc:
19
32
  def initialize(*allowed_methods)
20
33
  super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
21
34
  end
@@ -30,9 +43,6 @@ module ActionController
30
43
  class MissingFile < ActionControllerError #:nodoc:
31
44
  end
32
45
 
33
- class RenderError < ActionControllerError #:nodoc:
34
- end
35
-
36
46
  class SessionOverflowError < ActionControllerError #:nodoc:
37
47
  DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
38
48
 
@@ -43,4 +53,7 @@ module ActionController
43
53
 
44
54
  class UnknownHttpMethod < ActionControllerError #:nodoc:
45
55
  end
46
- end
56
+
57
+ class UnknownFormat < ActionControllerError #:nodoc:
58
+ end
59
+ end