actionpack 3.2.19 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +850 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +39 -37
  7. data/lib/abstract_controller/callbacks.rb +101 -82
  8. data/lib/abstract_controller/collector.rb +7 -3
  9. data/lib/abstract_controller/helpers.rb +25 -13
  10. data/lib/abstract_controller/layouts.rb +74 -74
  11. data/lib/abstract_controller/logger.rb +1 -2
  12. data/lib/abstract_controller/rendering.rb +30 -13
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +6 -6
  15. data/lib/abstract_controller/view_paths.rb +1 -1
  16. data/lib/abstract_controller.rb +1 -8
  17. data/lib/action_controller/base.rb +46 -22
  18. data/lib/action_controller/caching/fragments.rb +23 -53
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/log_subscriber.rb +16 -8
  23. data/lib/action_controller/metal/conditional_get.rb +76 -32
  24. data/lib/action_controller/metal/data_streaming.rb +20 -26
  25. data/lib/action_controller/metal/exceptions.rb +19 -6
  26. data/lib/action_controller/metal/flash.rb +24 -9
  27. data/lib/action_controller/metal/force_ssl.rb +70 -12
  28. data/lib/action_controller/metal/head.rb +25 -4
  29. data/lib/action_controller/metal/helpers.rb +5 -9
  30. data/lib/action_controller/metal/hide_actions.rb +0 -1
  31. data/lib/action_controller/metal/http_authentication.rb +107 -83
  32. data/lib/action_controller/metal/implicit_render.rb +1 -1
  33. data/lib/action_controller/metal/instrumentation.rb +2 -1
  34. data/lib/action_controller/metal/live.rb +175 -0
  35. data/lib/action_controller/metal/mime_responds.rb +161 -47
  36. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  37. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  38. data/lib/action_controller/metal/redirecting.rb +15 -20
  39. data/lib/action_controller/metal/renderers.rb +11 -9
  40. data/lib/action_controller/metal/rendering.rb +9 -1
  41. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  42. data/lib/action_controller/metal/responder.rb +20 -19
  43. data/lib/action_controller/metal/streaming.rb +12 -18
  44. data/lib/action_controller/metal/strong_parameters.rb +520 -0
  45. data/lib/action_controller/metal/testing.rb +13 -18
  46. data/lib/action_controller/metal/url_for.rb +28 -25
  47. data/lib/action_controller/metal.rb +17 -32
  48. data/lib/action_controller/model_naming.rb +12 -0
  49. data/lib/action_controller/railtie.rb +33 -17
  50. data/lib/action_controller/railties/helpers.rb +22 -0
  51. data/lib/action_controller/record_identifier.rb +18 -72
  52. data/lib/action_controller/test_case.rb +251 -131
  53. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  54. data/lib/action_controller.rb +15 -6
  55. data/lib/action_dispatch/http/cache.rb +63 -11
  56. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  58. data/lib/action_dispatch/http/headers.rb +49 -17
  59. data/lib/action_dispatch/http/mime_negotiation.rb +24 -1
  60. data/lib/action_dispatch/http/mime_type.rb +154 -100
  61. data/lib/action_dispatch/http/mime_types.rb +1 -1
  62. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  63. data/lib/action_dispatch/http/parameters.rb +28 -28
  64. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  65. data/lib/action_dispatch/http/request.rb +64 -18
  66. data/lib/action_dispatch/http/response.rb +130 -35
  67. data/lib/action_dispatch/http/upload.rb +63 -20
  68. data/lib/action_dispatch/http/url.rb +98 -35
  69. data/lib/action_dispatch/journey/backwards.rb +5 -0
  70. data/lib/action_dispatch/journey/formatter.rb +146 -0
  71. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  72. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  73. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  74. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  75. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  76. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  77. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  78. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  79. data/lib/action_dispatch/journey/parser.rb +206 -0
  80. data/lib/action_dispatch/journey/parser.y +47 -0
  81. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  82. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  83. data/lib/action_dispatch/journey/route.rb +124 -0
  84. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  85. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  86. data/lib/action_dispatch/journey/router.rb +166 -0
  87. data/lib/action_dispatch/journey/routes.rb +75 -0
  88. data/lib/action_dispatch/journey/scanner.rb +61 -0
  89. data/lib/action_dispatch/journey/visitors.rb +197 -0
  90. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  91. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  92. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  93. data/lib/action_dispatch/journey.rb +5 -0
  94. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  95. data/lib/action_dispatch/middleware/cookies.rb +259 -114
  96. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  97. data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -3
  98. data/lib/action_dispatch/middleware/flash.rb +58 -58
  99. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  100. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -14
  101. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  102. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  103. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  105. data/lib/action_dispatch/middleware/session/cookie_store.rb +82 -28
  106. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  107. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  108. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  109. data/lib/action_dispatch/middleware/stack.rb +6 -1
  110. data/lib/action_dispatch/middleware/static.rb +2 -1
  111. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  112. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +7 -9
  114. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +127 -5
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  118. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  119. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  120. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  122. data/lib/action_dispatch/railtie.rb +16 -6
  123. data/lib/action_dispatch/request/session.rb +181 -0
  124. data/lib/action_dispatch/routing/inspector.rb +240 -0
  125. data/lib/action_dispatch/routing/mapper.rb +540 -291
  126. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  127. data/lib/action_dispatch/routing/redirection.rb +46 -29
  128. data/lib/action_dispatch/routing/route_set.rb +207 -164
  129. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  130. data/lib/action_dispatch/routing/url_for.rb +48 -33
  131. data/lib/action_dispatch/routing.rb +48 -83
  132. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  133. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  134. data/lib/action_dispatch/testing/assertions/routing.rb +42 -41
  135. data/lib/action_dispatch/testing/assertions/selector.rb +17 -22
  136. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  137. data/lib/action_dispatch/testing/integration.rb +65 -51
  138. data/lib/action_dispatch/testing/test_process.rb +9 -6
  139. data/lib/action_dispatch/testing/test_request.rb +7 -3
  140. data/lib/action_dispatch.rb +21 -15
  141. data/lib/action_pack/version.rb +7 -6
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_view/base.rb +15 -34
  144. data/lib/action_view/buffers.rb +7 -1
  145. data/lib/action_view/context.rb +4 -4
  146. data/lib/action_view/dependency_tracker.rb +93 -0
  147. data/lib/action_view/digestor.rb +85 -0
  148. data/lib/action_view/flows.rb +1 -4
  149. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  150. data/lib/action_view/helpers/asset_tag_helper.rb +215 -352
  151. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  152. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  153. data/lib/action_view/helpers/cache_helper.rb +150 -18
  154. data/lib/action_view/helpers/capture_helper.rb +44 -31
  155. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  156. data/lib/action_view/helpers/date_helper.rb +269 -248
  157. data/lib/action_view/helpers/debug_helper.rb +10 -11
  158. data/lib/action_view/helpers/form_helper.rb +931 -537
  159. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  160. data/lib/action_view/helpers/form_tag_helper.rb +190 -90
  161. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  162. data/lib/action_view/helpers/number_helper.rb +148 -329
  163. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  164. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  165. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  166. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  167. data/lib/action_view/helpers/tag_helper.rb +46 -33
  168. data/lib/action_view/helpers/tags/base.rb +147 -0
  169. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  170. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  171. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  172. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  173. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  174. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  175. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  176. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  177. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  178. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  179. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  180. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  181. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  182. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  183. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  184. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  185. data/lib/action_view/helpers/tags/label.rb +65 -0
  186. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  187. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  188. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  189. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  190. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  191. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  192. data/lib/action_view/helpers/tags/select.rb +40 -0
  193. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  194. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  195. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  196. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  197. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  198. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  199. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  200. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  201. data/lib/action_view/helpers/tags.rb +39 -0
  202. data/lib/action_view/helpers/text_helper.rb +130 -114
  203. data/lib/action_view/helpers/translation_helper.rb +32 -16
  204. data/lib/action_view/helpers/url_helper.rb +211 -270
  205. data/lib/action_view/helpers.rb +2 -4
  206. data/lib/action_view/locale/en.yml +1 -105
  207. data/lib/action_view/log_subscriber.rb +6 -4
  208. data/lib/action_view/lookup_context.rb +15 -28
  209. data/lib/action_view/model_naming.rb +12 -0
  210. data/lib/action_view/path_set.rb +8 -20
  211. data/lib/action_view/railtie.rb +6 -22
  212. data/lib/action_view/record_identifier.rb +84 -0
  213. data/lib/action_view/renderer/abstract_renderer.rb +25 -19
  214. data/lib/action_view/renderer/partial_renderer.rb +158 -81
  215. data/lib/action_view/renderer/renderer.rb +8 -12
  216. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  217. data/lib/action_view/renderer/template_renderer.rb +12 -10
  218. data/lib/action_view/routing_url_for.rb +107 -0
  219. data/lib/action_view/template/error.rb +22 -12
  220. data/lib/action_view/template/handlers/builder.rb +1 -1
  221. data/lib/action_view/template/handlers/erb.rb +40 -19
  222. data/lib/action_view/template/handlers/raw.rb +11 -0
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/resolver.rb +107 -53
  225. data/lib/action_view/template/text.rb +12 -8
  226. data/lib/action_view/template/types.rb +57 -0
  227. data/lib/action_view/template.rb +25 -23
  228. data/lib/action_view/test_case.rb +67 -42
  229. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  230. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  231. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +13 -2
  232. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +9 -9
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  235. data/lib/action_view/vendor/html-scanner.rb +20 -0
  236. data/lib/action_view.rb +17 -8
  237. metadata +184 -214
  238. data/lib/action_controller/caching/actions.rb +0 -185
  239. data/lib/action_controller/caching/pages.rb +0 -187
  240. data/lib/action_controller/caching/sweeping.rb +0 -97
  241. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  242. data/lib/action_controller/metal/compatibility.rb +0 -65
  243. data/lib/action_controller/metal/session_management.rb +0 -14
  244. data/lib/action_controller/railties/paths.rb +0 -25
  245. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  246. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  247. data/lib/action_dispatch/middleware/head.rb +0 -18
  248. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  249. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  250. data/lib/action_view/asset_paths.rb +0 -142
  251. data/lib/action_view/helpers/asset_paths.rb +0 -7
  252. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  253. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  254. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  255. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  256. data/lib/sprockets/assets.rake +0 -99
  257. data/lib/sprockets/bootstrap.rb +0 -37
  258. data/lib/sprockets/compressors.rb +0 -83
  259. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  260. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/railtie.rb +0 -62
  263. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,59 +1,29 @@
1
- module ActionController #:nodoc:
1
+ module ActionController
2
2
  module Caching
3
- # Fragment caching is used for caching various blocks within
3
+ # Fragment caching is used for caching various blocks within
4
4
  # views without caching the entire action as a whole. This is
5
- # useful when certain elements of an action change frequently or
6
- # depend on complicated state while other parts rarely change or
5
+ # useful when certain elements of an action change frequently or
6
+ # depend on complicated state while other parts rarely change or
7
7
  # can be shared amongst multiple parties. The caching is done using
8
- # the <tt>cache</tt> helper available in the Action View. A
9
- # template with fragment caching might look like:
8
+ # the +cache+ helper available in the Action View. See
9
+ # ActionView::Helpers::CacheHelper for more information.
10
10
  #
11
- # <b>Hello <%= @name %></b>
11
+ # While it's strongly recommended that you use key-based cache
12
+ # expiration (see links in CacheHelper for more information),
13
+ # it is also possible to manually expire caches. For example:
12
14
  #
13
- # <% cache do %>
14
- # All the topics in the system:
15
- # <%= render :partial => "topic", :collection => Topic.all %>
16
- # <% end %>
17
- #
18
- # This cache will bind the name of the action that called it, so if
19
- # this code was part of the view for the topics/list action, you
20
- # would be able to invalidate it using:
21
- #
22
- # expire_fragment(:controller => "topics", :action => "list")
23
- #
24
- # This default behavior is limited if you need to cache multiple
25
- # fragments per action or if the action itself is cached using
26
- # <tt>caches_action</tt>. To remedy this, there is an option to
27
- # qualify the name of the cached fragment by using the
28
- # <tt>:action_suffix</tt> option:
29
- #
30
- # <% cache(:action => "list", :action_suffix => "all_topics") do %>
31
- #
32
- # That would result in a name such as
33
- # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
34
- # action cache and with any fragments that use a different suffix.
35
- # Note that the URL doesn't have to really exist or be callable
36
- # - the url_for system is just used to generate unique cache names
37
- # that we can refer to when we need to expire the cache.
38
- #
39
- # The expiration call for this example is:
40
- #
41
- # expire_fragment(:controller => "topics",
42
- # :action => "list",
43
- # :action_suffix => "all_topics")
15
+ # expire_fragment('name_of_cache')
44
16
  module Fragments
45
- # Given a key (as described in <tt>expire_fragment</tt>), returns
46
- # a key suitable for use in reading, writing, or expiring a
47
- # cached fragment. If the key is a hash, the generated key is the
48
- # return value of url_for on that hash (without the protocol).
49
- # All keys are prefixed with <tt>views/</tt> and uses
17
+ # Given a key (as described in +expire_fragment+), returns
18
+ # a key suitable for use in reading, writing, or expiring a
19
+ # cached fragment. All keys are prefixed with <tt>views/</tt> and uses
50
20
  # ActiveSupport::Cache.expand_cache_key for the expansion.
51
21
  def fragment_cache_key(key)
52
22
  ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
53
23
  end
54
24
 
55
- # Writes <tt>content</tt> to the location signified by
56
- # <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
25
+ # Writes +content+ to the location signified by
26
+ # +key+ (see +expire_fragment+ for acceptable formats).
57
27
  def write_fragment(key, content, options = nil)
58
28
  return content unless cache_configured?
59
29
 
@@ -65,8 +35,8 @@ module ActionController #:nodoc:
65
35
  content
66
36
  end
67
37
 
68
- # Reads a cached fragment from the location signified by <tt>key</tt>
69
- # (see <tt>expire_fragment</tt> for acceptable formats).
38
+ # Reads a cached fragment from the location signified by +key+
39
+ # (see +expire_fragment+ for acceptable formats).
70
40
  def read_fragment(key, options = nil)
71
41
  return unless cache_configured?
72
42
 
@@ -77,8 +47,8 @@ module ActionController #:nodoc:
77
47
  end
78
48
  end
79
49
 
80
- # Check if a cached fragment from the location signified by
81
- # <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
50
+ # Check if a cached fragment from the location signified by
51
+ # +key+ exists (see +expire_fragment+ for acceptable formats).
82
52
  def fragment_exist?(key, options = nil)
83
53
  return unless cache_configured?
84
54
  key = fragment_cache_key(key)
@@ -95,7 +65,7 @@ module ActionController #:nodoc:
95
65
  # * String - This would normally take the form of a path, like
96
66
  # <tt>pages/45/notes</tt>.
97
67
  # * Hash - Treated as an implicit call to +url_for+, like
98
- # <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
68
+ # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
99
69
  # * Regexp - Will remove any fragment that matches, so
100
70
  # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
101
71
  # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
@@ -104,8 +74,8 @@ module ActionController #:nodoc:
104
74
  # only supported on caches that can iterate over all keys (unlike
105
75
  # memcached).
106
76
  #
107
- # +options+ is passed through to the cache store's <tt>delete</tt>
108
- # method (or <tt>delete_matched</tt>, for Regexp keys.)
77
+ # +options+ is passed through to the cache store's +delete+
78
+ # method (or <tt>delete_matched</tt>, for Regexp keys).
109
79
  def expire_fragment(key, options = nil)
110
80
  return unless cache_configured?
111
81
  key = fragment_cache_key(key) unless key.is_a?(Regexp)
@@ -119,7 +89,7 @@ module ActionController #:nodoc:
119
89
  end
120
90
  end
121
91
 
122
- def instrument_fragment_cache(name, key)
92
+ def instrument_fragment_cache(name, key) # :nodoc:
123
93
  ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
124
94
  end
125
95
  end
@@ -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
11
+ # Note: To turn off all caching, set
15
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
19
  # Configuration examples (MemoryStore 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,60 @@ 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
+
61
+ def self.page_cache_extension=(extension)
62
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
63
+ self.default_static_extension = extension
64
+ end
65
+
66
+ def self.page_cache_extension
67
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
68
+ default_static_extension
69
+ end
70
+
68
71
  config_accessor :perform_caching
69
72
  self.perform_caching = true if perform_caching.nil?
70
- end
71
73
 
72
- def caching_allowed?
73
- request.get? && response.status == 200
74
+ class_attribute :_view_cache_dependencies
75
+ self._view_cache_dependencies = []
76
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
74
77
  end
75
78
 
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
79
+ module ClassMethods
80
+ def view_cache_dependency(&dependency)
81
+ self._view_cache_dependencies += [dependency]
83
82
  end
84
83
  end
84
+
85
+ def view_cache_dependencies
86
+ self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
87
+ end
88
+
89
+ protected
90
+ # Convenience accessor.
91
+ def cache(key, options = {}, &block)
92
+ if cache_configured?
93
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
94
+ else
95
+ yield
96
+ end
97
+ end
85
98
  end
86
99
  end
@@ -1,2 +1,5 @@
1
1
  ActionController::Integration = ActionDispatch::Integration
2
2
  ActionController::IntegrationTest = ActionDispatch::IntegrationTest
3
+
4
+ ActiveSupport::Deprecation.warn 'ActionController::Integration is deprecated and will be removed, use ActionDispatch::Integration instead.'
5
+ ActiveSupport::Deprecation.warn 'ActionController::IntegrationTest is deprecated and will be removed, use ActionDispatch::IntegrationTest instead.'
@@ -1,3 +1,7 @@
1
1
  ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
2
2
  ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
3
- ActionController::Routing = ActionDispatch::Routing
3
+ ActionController::Routing = ActionDispatch::Routing
4
+
5
+ ActiveSupport::Deprecation.warn 'ActionController::AbstractRequest and ActionController::Request are deprecated and will be removed, use ActionDispatch::Request instead.'
6
+ ActiveSupport::Deprecation.warn 'ActionController::AbstractResponse and ActionController::Response are deprecated and will be removed, use ActionDispatch::Response instead.'
7
+ ActiveSupport::Deprecation.warn 'ActionController::Routing is deprecated and will be removed, use ActionDispatch::Routing instead.'
@@ -1,10 +1,11 @@
1
- require 'active_support/core_ext/object/blank'
2
1
 
3
2
  module ActionController
4
3
  class LogSubscriber < ActiveSupport::LogSubscriber
5
4
  INTERNAL_PARAMS = %w(controller action format _method only_path)
6
5
 
7
6
  def start_processing(event)
7
+ return unless logger.info?
8
+
8
9
  payload = event.payload
9
10
  params = payload[:params].except(*INTERNAL_PARAMS)
10
11
  format = payload[:format]
@@ -15,6 +16,8 @@ module ActionController
15
16
  end
16
17
 
17
18
  def process_action(event)
19
+ return unless logger.info?
20
+
18
21
  payload = event.payload
19
22
  additions = ActionController::Base.log_process_action(payload)
20
23
 
@@ -23,36 +26,41 @@ module ActionController
23
26
  exception_class_name = payload[:exception].first
24
27
  status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
25
28
  end
26
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{format_duration(event.duration)}"
29
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
27
30
  message << " (#{additions.join(" | ")})" unless additions.blank?
28
31
 
29
32
  info(message)
30
33
  end
31
34
 
32
35
  def halted_callback(event)
33
- info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
36
+ info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
34
37
  end
35
38
 
36
39
  def send_file(event)
37
- info("Sent file #{event.payload[:path]} (#{format_duration(event.duration)})")
40
+ info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
38
41
  end
39
42
 
40
43
  def redirect_to(event)
41
- info "Redirected to #{event.payload[:location]}"
44
+ info("Redirected to #{event.payload[:location]}")
42
45
  end
43
46
 
44
47
  def send_data(event)
45
- info("Sent data #{event.payload[:filename]} (#{format_duration(event.duration)})")
48
+ info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
49
+ end
50
+
51
+ def unpermitted_parameters(event)
52
+ unpermitted_keys = event.payload[:keys]
53
+ debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
46
54
  end
47
55
 
48
56
  %w(write_fragment read_fragment exist_fragment?
49
57
  expire_fragment expire_page write_page).each do |method|
50
58
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
51
59
  def #{method}(event)
60
+ return unless logger.info?
52
61
  key_or_path = event.payload[:key] || event.payload[:path]
53
62
  human_name = #{method.to_s.humanize.inspect}
54
- duration = format_duration(event.duration)
55
- info("\#{human_name} \#{key_or_path} \#{duration}")
63
+ info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
56
64
  end
57
65
  METHOD
58
66
  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,53 @@ 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:
15
39
  #
16
- # Example:
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
+ #
45
+ # === Example:
17
46
  #
18
47
  # def show
19
48
  # @article = Article.find(params[:id])
20
- # fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
49
+ # fresh_when(etag: @article, last_modified: @article.created_at, public: true)
21
50
  # end
22
51
  #
23
52
  # This will render the show template if the request isn't sending a matching etag or
24
53
  # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
25
54
  #
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:
55
+ # You can also just pass a record where +last_modified+ will be set by calling
56
+ # +updated_at+ and the etag by passing the object itself.
27
57
  #
28
58
  # def show
29
59
  # @article = Article.find(params[:id])
@@ -34,7 +64,7 @@ module ActionController
34
64
  #
35
65
  # def show
36
66
  # @article = Article.find(params[:id])
37
- # fresh_when(@article, :public => true)
67
+ # fresh_when(@article, public: true)
38
68
  # end
39
69
  def fresh_when(record_or_options, additional_options = {})
40
70
  if record_or_options.is_a? Hash
@@ -42,32 +72,34 @@ module ActionController
42
72
  options.assert_valid_keys(:etag, :last_modified, :public)
43
73
  else
44
74
  record = record_or_options
45
- options = { :etag => record, :last_modified => record.try(:updated_at) }.merge(additional_options)
75
+ options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
46
76
  end
47
77
 
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]
78
+ response.etag = combine_etags(options[:etag]) if options[:etag]
79
+ response.last_modified = options[:last_modified] if options[:last_modified]
80
+ response.cache_control[:public] = true if options[:public]
51
81
 
52
82
  head :not_modified if request.fresh?(response)
53
83
  end
54
84
 
55
- # Sets the etag and/or last_modified on the response and checks it against
85
+ # Sets the +etag+ and/or +last_modified+ on the response and checks it against
56
86
  # the client request. If the request doesn't match the options provided, the
57
87
  # request is considered stale and should be generated from scratch. Otherwise,
58
88
  # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
59
89
  #
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).
90
+ # === Parameters:
91
+ #
92
+ # * <tt>:etag</tt>.
93
+ # * <tt>:last_modified</tt>.
94
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to
95
+ # +true+ if you want your application to be cachable by other devices (proxy caches).
64
96
  #
65
- # Example:
97
+ # === Example:
66
98
  #
67
99
  # def show
68
100
  # @article = Article.find(params[:id])
69
101
  #
70
- # if stale?(:etag => @article, :last_modified => @article.created_at)
102
+ # if stale?(etag: @article, last_modified: @article.created_at)
71
103
  # @statistics = @article.really_expensive_call
72
104
  # respond_to do |format|
73
105
  # # all the supported formats
@@ -75,7 +107,8 @@ module ActionController
75
107
  # end
76
108
  # end
77
109
  #
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:
110
+ # You can also just pass a record where +last_modified+ will be set by calling
111
+ # updated_at and the etag by passing the object itself.
79
112
  #
80
113
  # def show
81
114
  # @article = Article.find(params[:id])
@@ -93,7 +126,7 @@ module ActionController
93
126
  # def show
94
127
  # @article = Article.find(params[:id])
95
128
  #
96
- # if stale?(@article, :public => true)
129
+ # if stale?(@article, public: true)
97
130
  # @statistics = @article.really_expensive_call
98
131
  # respond_to do |format|
99
132
  # # all the supported formats
@@ -105,27 +138,38 @@ module ActionController
105
138
  !request.fresh?(response)
106
139
  end
107
140
 
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.
141
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
142
+ # instruction, so that intermediate caches must not cache the response.
110
143
  #
111
- # Examples:
112
144
  # expires_in 20.minutes
113
- # expires_in 3.hours, :public => true
114
- # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
145
+ # expires_in 3.hours, public: true
146
+ # expires_in 3.hours, public: true, must_revalidate: true
115
147
  #
116
148
  # This method will overwrite an existing Cache-Control header.
117
149
  # 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))
150
+ #
151
+ # The method will also ensure a HTTP Date header for client compatibility.
152
+ def expires_in(seconds, options = {})
153
+ response.cache_control.merge!(
154
+ :max_age => seconds,
155
+ :public => options.delete(:public),
156
+ :must_revalidate => options.delete(:must_revalidate)
157
+ )
120
158
  options.delete(:private)
121
159
 
122
160
  response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
161
+ response.date = Time.now unless response.date?
123
162
  end
124
163
 
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:
164
+ # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
165
+ # occur by the browser or intermediate caches (like caching proxy servers).
166
+ def expires_now
128
167
  response.cache_control.replace(:no_cache => true)
129
168
  end
169
+
170
+ private
171
+ def combine_etags(etag)
172
+ [ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
173
+ end
130
174
  end
131
175
  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 text: 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