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
@@ -1,30 +1,30 @@
1
1
  require 'action_dispatch/http/request'
2
2
  require 'action_dispatch/middleware/exception_wrapper'
3
+ require 'action_dispatch/routing/inspector'
3
4
 
4
5
  module ActionDispatch
5
6
  # This middleware is responsible for logging exceptions and
6
7
  # showing a debugging page in case the request is local.
7
8
  class DebugExceptions
8
- RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
9
+ RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
9
10
 
10
- def initialize(app)
11
- @app = app
11
+ def initialize(app, routes_app = nil)
12
+ @app = app
13
+ @routes_app = routes_app
12
14
  end
13
15
 
14
16
  def call(env)
15
- begin
16
- response = @app.call(env)
17
+ _, headers, body = response = @app.call(env)
17
18
 
18
- if response[1]['X-Cascade'] == 'pass'
19
- body = response[2]
20
- body.close if body.respond_to?(:close)
21
- raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
22
- end
23
- rescue Exception => exception
24
- raise exception if env['action_dispatch.show_exceptions'] == false
19
+ if headers['X-Cascade'] == 'pass'
20
+ body.close if body.respond_to?(:close)
21
+ raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
25
22
  end
26
23
 
27
- exception ? render_exception(env, exception) : response
24
+ response
25
+ rescue Exception => exception
26
+ raise exception if env['action_dispatch.show_exceptions'] == false
27
+ render_exception(env, exception)
28
28
  end
29
29
 
30
30
  private
@@ -34,24 +34,46 @@ module ActionDispatch
34
34
  log_error(env, wrapper)
35
35
 
36
36
  if env['action_dispatch.show_detailed_exceptions']
37
+ request = Request.new(env)
38
+ traces = wrapper.traces
39
+
40
+ trace_to_show = 'Application Trace'
41
+ if traces[trace_to_show].empty? && wrapper.rescue_template != 'routing_error'
42
+ trace_to_show = 'Full Trace'
43
+ end
44
+
45
+ if source_to_show = traces[trace_to_show].first
46
+ source_to_show_id = source_to_show[:id]
47
+ end
48
+
37
49
  template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
38
- :request => Request.new(env),
39
- :exception => wrapper.exception,
40
- :application_trace => wrapper.application_trace,
41
- :framework_trace => wrapper.framework_trace,
42
- :full_trace => wrapper.full_trace
50
+ request: request,
51
+ exception: wrapper.exception,
52
+ traces: traces,
53
+ show_source_idx: source_to_show_id,
54
+ trace_to_show: trace_to_show,
55
+ routes_inspector: routes_inspector(exception),
56
+ source_extracts: wrapper.source_extracts,
57
+ line_number: wrapper.line_number,
58
+ file: wrapper.file
43
59
  )
44
-
45
60
  file = "rescues/#{wrapper.rescue_template}"
46
- body = template.render(:template => file, :layout => 'rescues/layout')
47
- render(wrapper.status_code, body)
61
+
62
+ if request.xhr?
63
+ body = template.render(template: file, layout: false, formats: [:text])
64
+ format = "text/plain"
65
+ else
66
+ body = template.render(template: file, layout: 'rescues/layout')
67
+ format = "text/html"
68
+ end
69
+ render(wrapper.status_code, body, format)
48
70
  else
49
71
  raise exception
50
72
  end
51
73
  end
52
74
 
53
- def render(status, body)
54
- [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
75
+ def render(status, body, format)
76
+ [status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
55
77
  end
56
78
 
57
79
  def log_error(env, wrapper)
@@ -76,7 +98,13 @@ module ActionDispatch
76
98
  end
77
99
 
78
100
  def stderr_logger
79
- @stderr_logger ||= Logger.new($stderr)
101
+ @stderr_logger ||= ActiveSupport::Logger.new($stderr)
102
+ end
103
+
104
+ def routes_inspector(exception)
105
+ if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
106
+ ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
107
+ end
80
108
  end
81
109
  end
82
110
  end
@@ -1,16 +1,22 @@
1
1
  require 'action_controller/metal/exceptions'
2
- require 'active_support/core_ext/exception'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
3
 
4
4
  module ActionDispatch
5
5
  class ExceptionWrapper
6
6
  cattr_accessor :rescue_responses
7
7
  @@rescue_responses = Hash.new(:internal_server_error)
8
8
  @@rescue_responses.merge!(
9
- 'ActionController::RoutingError' => :not_found,
10
- 'AbstractController::ActionNotFound' => :not_found,
11
- 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
- 'ActionController::NotImplemented' => :not_implemented,
13
- 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
9
+ 'ActionController::RoutingError' => :not_found,
10
+ 'AbstractController::ActionNotFound' => :not_found,
11
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
12
+ 'ActionController::UnknownHttpMethod' => :method_not_allowed,
13
+ 'ActionController::NotImplemented' => :not_implemented,
14
+ 'ActionController::UnknownFormat' => :not_acceptable,
15
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
16
+ 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity,
17
+ 'ActionDispatch::ParamsParser::ParseError' => :bad_request,
18
+ 'ActionController::BadRequest' => :bad_request,
19
+ 'ActionController::ParameterMissing' => :bad_request
14
20
  )
15
21
 
16
22
  cattr_accessor :rescue_templates
@@ -22,11 +28,13 @@ module ActionDispatch
22
28
  'ActionView::Template::Error' => 'template_error'
23
29
  )
24
30
 
25
- attr_reader :env, :exception
31
+ attr_reader :env, :exception, :line_number, :file
26
32
 
27
33
  def initialize(env, exception)
28
34
  @env = env
29
35
  @exception = original_exception(exception)
36
+
37
+ expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
30
38
  end
31
39
 
32
40
  def rescue_template
@@ -49,12 +57,52 @@ module ActionDispatch
49
57
  clean_backtrace(:all)
50
58
  end
51
59
 
60
+ def traces
61
+ appplication_trace_with_ids = []
62
+ framework_trace_with_ids = []
63
+ full_trace_with_ids = []
64
+
65
+ full_trace.each_with_index do |trace, idx|
66
+ trace_with_id = { id: idx, trace: trace }
67
+
68
+ if application_trace.include?(trace)
69
+ appplication_trace_with_ids << trace_with_id
70
+ else
71
+ framework_trace_with_ids << trace_with_id
72
+ end
73
+
74
+ full_trace_with_ids << trace_with_id
75
+ end
76
+
77
+ {
78
+ "Application Trace" => appplication_trace_with_ids,
79
+ "Framework Trace" => framework_trace_with_ids,
80
+ "Full Trace" => full_trace_with_ids
81
+ }
82
+ end
83
+
52
84
  def self.status_code_for_exception(class_name)
53
85
  Rack::Utils.status_code(@@rescue_responses[class_name])
54
86
  end
55
87
 
88
+ def source_extracts
89
+ backtrace.map do |trace|
90
+ file, line = trace.split(":")
91
+ line_number = line.to_i
92
+
93
+ {
94
+ code: source_fragment(file, line_number),
95
+ line_number: line_number
96
+ }
97
+ end
98
+ end
99
+
56
100
  private
57
101
 
102
+ def backtrace
103
+ Array(@exception.backtrace)
104
+ end
105
+
58
106
  def original_exception(exception)
59
107
  if registered_original_exception?(exception)
60
108
  exception.original_exception
@@ -69,14 +117,32 @@ module ActionDispatch
69
117
 
70
118
  def clean_backtrace(*args)
71
119
  if backtrace_cleaner
72
- backtrace_cleaner.clean(@exception.backtrace, *args)
120
+ backtrace_cleaner.clean(backtrace, *args)
73
121
  else
74
- @exception.backtrace
122
+ backtrace
75
123
  end
76
124
  end
77
125
 
78
126
  def backtrace_cleaner
79
127
  @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
80
128
  end
129
+
130
+ def source_fragment(path, line)
131
+ return unless Rails.respond_to?(:root) && Rails.root
132
+ full_path = Rails.root.join(path)
133
+ if File.exist?(full_path)
134
+ File.open(full_path, "r") do |file|
135
+ start = [line - 3, 0].max
136
+ lines = file.each_line.drop(start).take(6)
137
+ Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
138
+ end
139
+ end
140
+ end
141
+
142
+ def expand_backtrace
143
+ @exception.backtrace.unshift(
144
+ @exception.to_s.split("\n")
145
+ ).flatten!
146
+ end
81
147
  end
82
148
  end
@@ -1,23 +1,25 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+
1
3
  module ActionDispatch
2
- class Request
4
+ class Request < Rack::Request
3
5
  # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
4
6
  # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
5
7
  # to put a new one.
6
8
  def flash
7
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
9
+ @env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
8
10
  end
9
11
  end
10
12
 
11
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
13
+ # The flash provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed
12
14
  # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
13
15
  # action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
14
- # then expose the flash to its template. Actually, that exposure is automatically done. Example:
16
+ # then expose the flash to its template. Actually, that exposure is automatically done.
15
17
  #
16
18
  # class PostsController < ActionController::Base
17
19
  # def create
18
20
  # # save post
19
21
  # flash[:notice] = "Post successfully created"
20
- # redirect_to posts_path(@post)
22
+ # redirect_to @post
21
23
  # end
22
24
  #
23
25
  # def show
@@ -35,8 +37,11 @@ module ActionDispatch
35
37
  # flash.alert = "You must be logged in"
36
38
  # flash.notice = "Post successfully created"
37
39
  #
38
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
39
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
40
+ # This example places a string in the flash. And of course, you can put as many as you like at a time too. If you want to pass
41
+ # non-primitive types, you will have to handle that in your application. Example: To show messages with links, you will have to
42
+ # use sanitize helper.
43
+ #
44
+ # Just remember: They'll be gone by the time the next action has been performed.
40
45
  #
41
46
  # See docs on the FlashHash class for more details about the flash.
42
47
  class Flash
@@ -50,37 +55,54 @@ module ActionDispatch
50
55
  end
51
56
 
52
57
  def []=(k, v)
58
+ k = k.to_s
53
59
  @flash[k] = v
54
60
  @flash.discard(k)
55
61
  v
56
62
  end
57
63
 
58
64
  def [](k)
59
- @flash[k]
65
+ @flash[k.to_s]
60
66
  end
61
67
 
62
- # Convenience accessor for flash.now[:alert]=
68
+ # Convenience accessor for <tt>flash.now[:alert]=</tt>.
63
69
  def alert=(message)
64
70
  self[:alert] = message
65
71
  end
66
72
 
67
- # Convenience accessor for flash.now[:notice]=
73
+ # Convenience accessor for <tt>flash.now[:notice]=</tt>.
68
74
  def notice=(message)
69
75
  self[:notice] = message
70
76
  end
71
77
  end
72
78
 
73
- # Implementation detail: please do not change the signature of the
74
- # FlashHash class. Doing that will likely affect all Rails apps in
75
- # production as the FlashHash currently stored in their sessions will
76
- # become invalid.
77
79
  class FlashHash
78
80
  include Enumerable
79
81
 
80
- def initialize #:nodoc:
81
- @used = Set.new
82
- @closed = false
83
- @flashes = {}
82
+ def self.from_session_value(value) #:nodoc:
83
+ flash = case value
84
+ when FlashHash # Rails 3.1, 3.2
85
+ new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
86
+ when Hash # Rails 4.0
87
+ new(value['flashes'], value['discard'])
88
+ else
89
+ new
90
+ end
91
+
92
+ flash.tap(&:sweep)
93
+ end
94
+
95
+ # Builds a hash containing the discarded values and the hashes
96
+ # representing the flashes.
97
+ # If there are no values in @flashes, returns nil.
98
+ def to_session_value #:nodoc:
99
+ return nil if empty?
100
+ {'discard' => @discard.to_a, 'flashes' => @flashes}
101
+ end
102
+
103
+ def initialize(flashes = {}, discard = []) #:nodoc:
104
+ @discard = Set.new(stringify_array(discard))
105
+ @flashes = flashes.stringify_keys
84
106
  @now = nil
85
107
  end
86
108
 
@@ -92,18 +114,19 @@ module ActionDispatch
92
114
  super
93
115
  end
94
116
 
95
- def []=(k, v) #:nodoc:
96
- keep(k)
117
+ def []=(k, v)
118
+ k = k.to_s
119
+ @discard.delete k
97
120
  @flashes[k] = v
98
121
  end
99
122
 
100
123
  def [](k)
101
- @flashes[k]
124
+ @flashes[k.to_s]
102
125
  end
103
126
 
104
127
  def update(h) #:nodoc:
105
- h.keys.each { |k| keep(k) }
106
- @flashes.update h
128
+ @discard.subtract stringify_array(h.keys)
129
+ @flashes.update h.stringify_keys
107
130
  self
108
131
  end
109
132
 
@@ -112,10 +135,12 @@ module ActionDispatch
112
135
  end
113
136
 
114
137
  def key?(name)
115
- @flashes.key? name
138
+ @flashes.key? name.to_s
116
139
  end
117
140
 
118
141
  def delete(key)
142
+ key = key.to_s
143
+ @discard.delete key
119
144
  @flashes.delete key
120
145
  self
121
146
  end
@@ -129,6 +154,7 @@ module ActionDispatch
129
154
  end
130
155
 
131
156
  def clear
157
+ @discard.clear
132
158
  @flashes.clear
133
159
  end
134
160
 
@@ -139,8 +165,8 @@ module ActionDispatch
139
165
  alias :merge! :update
140
166
 
141
167
  def replace(h) #:nodoc:
142
- @used = Set.new
143
- @flashes.replace h
168
+ @discard.clear
169
+ @flashes.replace h.stringify_keys
144
170
  self
145
171
  end
146
172
 
@@ -154,6 +180,14 @@ module ActionDispatch
154
180
  # vanish when the current action is done.
155
181
  #
156
182
  # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
183
+ #
184
+ # Also, brings two convenience accessors:
185
+ #
186
+ # flash.now.alert = "Beware now!"
187
+ # # Equivalent to flash.now[:alert] = "Beware now!"
188
+ #
189
+ # flash.now.notice = "Good luck now!"
190
+ # # Equivalent to flash.now[:notice] = "Good luck now!"
157
191
  def now
158
192
  @now ||= FlashNow.new(self)
159
193
  end
@@ -163,7 +197,9 @@ module ActionDispatch
163
197
  # flash.keep # keeps the entire flash
164
198
  # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
165
199
  def keep(k = nil)
166
- use(k, false)
200
+ k = k.to_s if k
201
+ @discard.subtract Array(k || keys)
202
+ k ? self[k] : self
167
203
  end
168
204
 
169
205
  # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
@@ -171,63 +207,49 @@ module ActionDispatch
171
207
  # flash.discard # discard the entire flash at the end of the current action
172
208
  # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
173
209
  def discard(k = nil)
174
- use(k)
210
+ k = k.to_s if k
211
+ @discard.merge Array(k || keys)
212
+ k ? self[k] : self
175
213
  end
176
214
 
177
215
  # Mark for removal entries that were kept, and delete unkept ones.
178
216
  #
179
217
  # This method is called automatically by filters, so you generally don't need to care about it.
180
218
  def sweep #:nodoc:
181
- keys.each do |k|
182
- unless @used.include?(k)
183
- @used << k
184
- else
185
- delete(k)
186
- @used.delete(k)
187
- end
188
- end
189
-
190
- # clean up after keys that could have been left over by calling reject! or shift on the flash
191
- (@used - keys).each{ |k| @used.delete(k) }
219
+ @discard.each { |k| @flashes.delete k }
220
+ @discard.replace @flashes.keys
192
221
  end
193
222
 
194
- # Convenience accessor for flash[:alert]
223
+ # Convenience accessor for <tt>flash[:alert]</tt>.
195
224
  def alert
196
225
  self[:alert]
197
226
  end
198
227
 
199
- # Convenience accessor for flash[:alert]=
228
+ # Convenience accessor for <tt>flash[:alert]=</tt>.
200
229
  def alert=(message)
201
230
  self[:alert] = message
202
231
  end
203
232
 
204
- # Convenience accessor for flash[:notice]
233
+ # Convenience accessor for <tt>flash[:notice]</tt>.
205
234
  def notice
206
235
  self[:notice]
207
236
  end
208
237
 
209
- # Convenience accessor for flash[:notice]=
238
+ # Convenience accessor for <tt>flash[:notice]=</tt>.
210
239
  def notice=(message)
211
240
  self[:notice] = message
212
241
  end
213
242
 
214
243
  protected
244
+ def now_is_loaded?
245
+ @now
246
+ end
215
247
 
216
- def now_is_loaded?
217
- !!@now
218
- end
219
-
220
- # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
221
- # use() # marks the entire flash as used
222
- # use('msg') # marks the "msg" entry as used
223
- # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
224
- # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
225
- # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
226
- # if no key is passed.
227
- def use(key = nil, used = true)
228
- Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
229
- return key ? self[key] : self
248
+ def stringify_array(array)
249
+ array.map do |item|
250
+ item.kind_of?(Symbol) ? item.to_s : item
230
251
  end
252
+ end
231
253
  end
232
254
 
233
255
  def initialize(app)
@@ -235,27 +257,18 @@ module ActionDispatch
235
257
  end
236
258
 
237
259
  def call(env)
238
- if (session = env['rack.session']) && (flash = session['flash'])
239
- flash.sweep
240
- end
241
-
242
260
  @app.call(env)
243
261
  ensure
244
- session = env['rack.session'] || {}
262
+ session = Request::Session.find(env) || {}
245
263
  flash_hash = env[KEY]
246
264
 
247
- if flash_hash
248
- if !flash_hash.empty? || session.key?('flash')
249
- session["flash"] = flash_hash
250
- new_hash = flash_hash.dup
251
- else
252
- new_hash = flash_hash
253
- end
254
-
255
- env[KEY] = new_hash
265
+ if flash_hash && (flash_hash.present? || session.key?('flash'))
266
+ session["flash"] = flash_hash.to_session_value
267
+ env[KEY] = flash_hash.dup
256
268
  end
257
269
 
258
- if session.key?('flash') && session['flash'].empty?
270
+ if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
271
+ session.key?('flash') && session['flash'].nil?
259
272
  session.delete('flash')
260
273
  end
261
274
  end
@@ -4,10 +4,16 @@ require 'active_support/core_ext/hash/indifferent_access'
4
4
 
5
5
  module ActionDispatch
6
6
  class ParamsParser
7
- DEFAULT_PARSERS = {
8
- Mime::XML => :xml_simple,
9
- Mime::JSON => :json
10
- }
7
+ class ParseError < StandardError
8
+ attr_reader :original_exception
9
+
10
+ def initialize(message, original_exception)
11
+ super(message)
12
+ @original_exception = original_exception
13
+ end
14
+ end
15
+
16
+ DEFAULT_PARSERS = { Mime::JSON => :json }
11
17
 
12
18
  def initialize(app, parsers = {})
13
19
  @app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
@@ -27,49 +33,28 @@ module ActionDispatch
27
33
 
28
34
  return false if request.content_length.zero?
29
35
 
30
- mime_type = content_type_from_legacy_post_data_format_header(env) ||
31
- request.content_mime_type
32
-
33
- strategy = @parsers[mime_type]
36
+ strategy = @parsers[request.content_mime_type]
34
37
 
35
38
  return false unless strategy
36
39
 
37
40
  case strategy
38
41
  when Proc
39
42
  strategy.call(request.raw_post)
40
- when :xml_simple, :xml_node
41
- data = request.deep_munge(Hash.from_xml(request.body.read) || {})
42
- request.body.rewind if request.body.respond_to?(:rewind)
43
- data.with_indifferent_access
44
- when :yaml
45
- YAML.load(request.raw_post)
46
43
  when :json
47
- data = ActiveSupport::JSON.decode(request.body)
48
- request.body.rewind if request.body.respond_to?(:rewind)
44
+ data = ActiveSupport::JSON.decode(request.raw_post)
49
45
  data = {:_json => data} unless data.is_a?(Hash)
50
- request.deep_munge(data).with_indifferent_access
46
+ Request::Utils.deep_munge(data).with_indifferent_access
51
47
  else
52
48
  false
53
49
  end
54
- rescue Exception => e # YAML, XML or Ruby code block errors
50
+ rescue => e # JSON or Ruby code block errors
55
51
  logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
56
52
 
57
- raise e
58
- end
59
-
60
- def content_type_from_legacy_post_data_format_header(env)
61
- if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
62
- case x_post_format.to_s.downcase
63
- when 'yaml' then return Mime::YAML
64
- when 'xml' then return Mime::XML
65
- end
66
- end
67
-
68
- nil
53
+ raise ParseError.new(e.message, e)
69
54
  end
70
55
 
71
56
  def logger(env)
72
- env['action_dispatch.logger'] || Logger.new($stderr)
57
+ env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
73
58
  end
74
59
  end
75
60
  end