omg-actionpack 8.0.0.alpha1

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 (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +691 -0
  65. data/lib/action_controller.rb +80 -0
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/syntax_error_proxy"
7
+ require "active_support/core_ext/thread/backtrace/location"
8
+ require "rack/utils"
9
+
10
+ module ActionDispatch
11
+ class ExceptionWrapper
12
+ cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
13
+ "ActionController::RoutingError" => :not_found,
14
+ "AbstractController::ActionNotFound" => :not_found,
15
+ "ActionController::MethodNotAllowed" => :method_not_allowed,
16
+ "ActionController::UnknownHttpMethod" => :method_not_allowed,
17
+ "ActionController::NotImplemented" => :not_implemented,
18
+ "ActionController::UnknownFormat" => :not_acceptable,
19
+ "ActionDispatch::Http::MimeNegotiation::InvalidType" => :not_acceptable,
20
+ "ActionController::MissingExactTemplate" => :not_acceptable,
21
+ "ActionController::InvalidAuthenticityToken" => :unprocessable_entity,
22
+ "ActionController::InvalidCrossOriginRequest" => :unprocessable_entity,
23
+ "ActionDispatch::Http::Parameters::ParseError" => :bad_request,
24
+ "ActionController::BadRequest" => :bad_request,
25
+ "ActionController::ParameterMissing" => :bad_request,
26
+ "Rack::QueryParser::ParameterTypeError" => :bad_request,
27
+ "Rack::QueryParser::InvalidParameterError" => :bad_request
28
+ )
29
+
30
+ cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
31
+ "ActionView::MissingTemplate" => "missing_template",
32
+ "ActionController::RoutingError" => "routing_error",
33
+ "AbstractController::ActionNotFound" => "unknown_action",
34
+ "ActiveRecord::StatementInvalid" => "invalid_statement",
35
+ "ActionView::Template::Error" => "template_error",
36
+ "ActionController::MissingExactTemplate" => "missing_exact_template",
37
+ )
38
+
39
+ cattr_accessor :wrapper_exceptions, default: [
40
+ "ActionView::Template::Error"
41
+ ]
42
+
43
+ cattr_accessor :silent_exceptions, default: [
44
+ "ActionController::RoutingError",
45
+ "ActionDispatch::Http::MimeNegotiation::InvalidType"
46
+ ]
47
+
48
+ attr_reader :backtrace_cleaner, :wrapped_causes, :exception_class_name, :exception
49
+
50
+ def initialize(backtrace_cleaner, exception)
51
+ @backtrace_cleaner = backtrace_cleaner
52
+ @exception_class_name = exception.class.name
53
+ @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
54
+ @exception = exception
55
+ if exception.is_a?(SyntaxError)
56
+ @exception = ActiveSupport::SyntaxErrorProxy.new(exception)
57
+ end
58
+ @backtrace = build_backtrace
59
+ end
60
+
61
+ def routing_error?
62
+ @exception.is_a?(ActionController::RoutingError)
63
+ end
64
+
65
+ def template_error?
66
+ @exception.is_a?(ActionView::Template::Error)
67
+ end
68
+
69
+ def sub_template_message
70
+ @exception.sub_template_message
71
+ end
72
+
73
+ def has_cause?
74
+ @exception.cause
75
+ end
76
+
77
+ def failures
78
+ @exception.failures
79
+ end
80
+
81
+ def has_corrections?
82
+ @exception.respond_to?(:original_message) && @exception.respond_to?(:corrections)
83
+ end
84
+
85
+ def original_message
86
+ @exception.original_message
87
+ end
88
+
89
+ def corrections
90
+ @exception.corrections
91
+ end
92
+
93
+ def file_name
94
+ @exception.file_name
95
+ end
96
+
97
+ def line_number
98
+ @exception.line_number
99
+ end
100
+
101
+ def actions
102
+ ActiveSupport::ActionableError.actions(@exception)
103
+ end
104
+
105
+ def unwrapped_exception
106
+ if wrapper_exceptions.include?(@exception_class_name)
107
+ @exception.cause
108
+ else
109
+ @exception
110
+ end
111
+ end
112
+
113
+ def annotated_source_code
114
+ if exception.respond_to?(:annotated_source_code)
115
+ exception.annotated_source_code
116
+ else
117
+ []
118
+ end
119
+ end
120
+
121
+ def rescue_template
122
+ @@rescue_templates[@exception_class_name]
123
+ end
124
+
125
+ def status_code
126
+ self.class.status_code_for_exception(unwrapped_exception.class.name)
127
+ end
128
+
129
+ def exception_trace
130
+ trace = application_trace
131
+ trace = framework_trace if trace.empty? && !silent_exceptions.include?(@exception_class_name)
132
+ trace
133
+ end
134
+
135
+ def application_trace
136
+ clean_backtrace(:silent)
137
+ end
138
+
139
+ def framework_trace
140
+ clean_backtrace(:noise)
141
+ end
142
+
143
+ def full_trace
144
+ clean_backtrace(:all)
145
+ end
146
+
147
+ def traces
148
+ application_trace_with_ids = []
149
+ framework_trace_with_ids = []
150
+ full_trace_with_ids = []
151
+
152
+ full_trace.each_with_index do |trace, idx|
153
+ trace_with_id = {
154
+ exception_object_id: @exception.object_id,
155
+ id: idx,
156
+ trace: trace
157
+ }
158
+
159
+ if application_trace.include?(trace)
160
+ application_trace_with_ids << trace_with_id
161
+ else
162
+ framework_trace_with_ids << trace_with_id
163
+ end
164
+
165
+ full_trace_with_ids << trace_with_id
166
+ end
167
+
168
+ {
169
+ "Application Trace" => application_trace_with_ids,
170
+ "Framework Trace" => framework_trace_with_ids,
171
+ "Full Trace" => full_trace_with_ids
172
+ }
173
+ end
174
+
175
+ def self.status_code_for_exception(class_name)
176
+ Rack::Utils.status_code(@@rescue_responses[class_name])
177
+ end
178
+
179
+ def show?(request)
180
+ # We're treating `nil` as "unset", and we want the default setting to be `:all`.
181
+ # This logic should be extracted to `env_config` and calculated once.
182
+ config = request.get_header("action_dispatch.show_exceptions")
183
+
184
+ case config
185
+ when :none
186
+ false
187
+ when :rescuable
188
+ rescue_response?
189
+ else
190
+ true
191
+ end
192
+ end
193
+
194
+ def rescue_response?
195
+ @@rescue_responses.key?(exception.class.name)
196
+ end
197
+
198
+ def source_extracts
199
+ backtrace.map do |trace|
200
+ extract_source(trace)
201
+ end
202
+ end
203
+
204
+ def error_highlight_available?
205
+ # ErrorHighlight.spot with backtrace_location keyword is available since
206
+ # error_highlight 0.4.0
207
+ defined?(ErrorHighlight) && Gem::Version.new(ErrorHighlight::VERSION) >= Gem::Version.new("0.4.0")
208
+ end
209
+
210
+ def trace_to_show
211
+ if traces["Application Trace"].empty? && rescue_template != "routing_error"
212
+ "Full Trace"
213
+ else
214
+ "Application Trace"
215
+ end
216
+ end
217
+
218
+ def source_to_show_id
219
+ (traces[trace_to_show].first || {})[:id]
220
+ end
221
+
222
+ def exception_name
223
+ exception.cause.class.to_s
224
+ end
225
+
226
+ def message
227
+ exception.message
228
+ end
229
+
230
+ def exception_inspect
231
+ exception.inspect
232
+ end
233
+
234
+ def exception_id
235
+ exception.object_id
236
+ end
237
+
238
+ private
239
+ class SourceMapLocation < DelegateClass(Thread::Backtrace::Location) # :nodoc:
240
+ def initialize(location, template)
241
+ super(location)
242
+ @template = template
243
+ end
244
+
245
+ def spot(exc)
246
+ if RubyVM::AbstractSyntaxTree.respond_to?(:node_id_for_backtrace_location) && __getobj__.is_a?(Thread::Backtrace::Location)
247
+ location = @template.spot(__getobj__)
248
+ else
249
+ location = super
250
+ end
251
+
252
+ if location
253
+ @template.translate_location(__getobj__, location)
254
+ end
255
+ end
256
+ end
257
+
258
+ attr_reader :backtrace
259
+
260
+ def build_backtrace
261
+ built_methods = {}
262
+
263
+ ActionView::PathRegistry.all_resolvers.each do |resolver|
264
+ resolver.built_templates.each do |template|
265
+ built_methods[template.method_name] = template
266
+ end
267
+ end
268
+
269
+ (@exception.backtrace_locations || []).map do |loc|
270
+ if built_methods.key?(loc.label.to_s)
271
+ thread_backtrace_location = if loc.respond_to?(:__getobj__)
272
+ loc.__getobj__
273
+ else
274
+ loc
275
+ end
276
+ SourceMapLocation.new(thread_backtrace_location, built_methods[loc.label.to_s])
277
+ else
278
+ loc
279
+ end
280
+ end
281
+ end
282
+
283
+ def causes_for(exception)
284
+ return enum_for(__method__, exception) unless block_given?
285
+
286
+ yield exception while exception = exception.cause
287
+ end
288
+
289
+ def wrapped_causes_for(exception, backtrace_cleaner)
290
+ causes_for(exception).map { |cause| self.class.new(backtrace_cleaner, cause) }
291
+ end
292
+
293
+ def clean_backtrace(*args)
294
+ if backtrace_cleaner
295
+ backtrace_cleaner.clean(backtrace, *args)
296
+ else
297
+ backtrace
298
+ end
299
+ end
300
+
301
+ def extract_source(trace)
302
+ spot = trace.spot(@exception)
303
+
304
+ if spot
305
+ line = spot[:first_lineno]
306
+ code = extract_source_fragment_lines(spot[:script_lines], line)
307
+
308
+ if line == spot[:last_lineno]
309
+ code[line] = [
310
+ code[line][0, spot[:first_column]],
311
+ code[line][spot[:first_column]...spot[:last_column]],
312
+ code[line][spot[:last_column]..-1],
313
+ ]
314
+ end
315
+
316
+ return {
317
+ code: code,
318
+ line_number: line
319
+ }
320
+ end
321
+
322
+ file, line_number = extract_file_and_line_number(trace)
323
+
324
+ {
325
+ code: source_fragment(file, line_number),
326
+ line_number: line_number
327
+ }
328
+ end
329
+
330
+ def extract_source_fragment_lines(source_lines, line)
331
+ start = [line - 3, 0].max
332
+ lines = source_lines.drop(start).take(6)
333
+ Hash[*(start + 1..(lines.count + start)).zip(lines).flatten]
334
+ end
335
+
336
+ def source_fragment(path, line)
337
+ return unless Rails.respond_to?(:root) && Rails.root
338
+ full_path = Rails.root.join(path)
339
+ if File.exist?(full_path)
340
+ File.open(full_path, "r") do |file|
341
+ extract_source_fragment_lines(file.each_line, line)
342
+ end
343
+ end
344
+ end
345
+
346
+ def extract_file_and_line_number(trace)
347
+ [trace.path, trace.lineno]
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "rack/body_proxy"
6
+
7
+ module ActionDispatch
8
+ class Executor
9
+ def initialize(app, executor)
10
+ @app, @executor = app, executor
11
+ end
12
+
13
+ def call(env)
14
+ state = @executor.run!(reset: true)
15
+ begin
16
+ response = @app.call(env)
17
+
18
+ if env["action_dispatch.report_exception"]
19
+ error = env["action_dispatch.exception"]
20
+ @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
21
+ end
22
+
23
+ returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
24
+ rescue => error
25
+ @executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
26
+ raise
27
+ ensure
28
+ state.complete! unless returned
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,318 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/hash/keys"
6
+
7
+ module ActionDispatch
8
+ # # Action Dispatch Flash
9
+ #
10
+ # The flash provides a way to pass temporary primitive-types (String, Array,
11
+ # Hash) between actions. Anything you place in the flash will be exposed to the
12
+ # very next action and then cleared out. This is a great way of doing notices
13
+ # and alerts, such as a create action that sets `flash[:notice] = "Post
14
+ # successfully created"` before redirecting to a display action that can then
15
+ # expose the flash to its template. Actually, that exposure is automatically
16
+ # done.
17
+ #
18
+ # class PostsController < ActionController::Base
19
+ # def create
20
+ # # save post
21
+ # flash[:notice] = "Post successfully created"
22
+ # redirect_to @post
23
+ # end
24
+ #
25
+ # def show
26
+ # # doesn't need to assign the flash notice to the template, that's done automatically
27
+ # end
28
+ # end
29
+ #
30
+ # Then in `show.html.erb`:
31
+ #
32
+ # <% if flash[:notice] %>
33
+ # <div class="notice"><%= flash[:notice] %></div>
34
+ # <% end %>
35
+ #
36
+ # Since the `notice` and `alert` keys are a common idiom, convenience accessors
37
+ # are available:
38
+ #
39
+ # flash.alert = "You must be logged in"
40
+ # flash.notice = "Post successfully created"
41
+ #
42
+ # This example places a string in the flash. And of course, you can put as many
43
+ # as you like at a time too. If you want to pass non-primitive types, you will
44
+ # have to handle that in your application. Example: To show messages with links,
45
+ # you will have to use sanitize helper.
46
+ #
47
+ # Just remember: They'll be gone by the time the next action has been performed.
48
+ #
49
+ # See docs on the FlashHash class for more details about the flash.
50
+ class Flash
51
+ KEY = "action_dispatch.request.flash_hash"
52
+
53
+ module RequestMethods
54
+ # Access the contents of the flash. Returns a ActionDispatch::Flash::FlashHash.
55
+ #
56
+ # See ActionDispatch::Flash for example usage.
57
+ def flash
58
+ flash = flash_hash
59
+ return flash if flash
60
+ self.flash = Flash::FlashHash.from_session_value(session["flash"])
61
+ end
62
+
63
+ def flash=(flash)
64
+ set_header Flash::KEY, flash
65
+ end
66
+
67
+ def flash_hash # :nodoc:
68
+ get_header Flash::KEY
69
+ end
70
+
71
+ def commit_flash # :nodoc:
72
+ return unless session.enabled?
73
+
74
+ if flash_hash && (flash_hash.present? || session.key?("flash"))
75
+ session["flash"] = flash_hash.to_session_value
76
+ self.flash = flash_hash.dup
77
+ end
78
+
79
+ if session.loaded? && session.key?("flash") && session["flash"].nil?
80
+ session.delete("flash")
81
+ end
82
+ end
83
+
84
+ def reset_session # :nodoc:
85
+ super
86
+ self.flash = nil
87
+ end
88
+ end
89
+
90
+ class FlashNow # :nodoc:
91
+ attr_accessor :flash
92
+
93
+ def initialize(flash)
94
+ @flash = flash
95
+ end
96
+
97
+ def []=(k, v)
98
+ k = k.to_s
99
+ @flash[k] = v
100
+ @flash.discard(k)
101
+ v
102
+ end
103
+
104
+ def [](k)
105
+ @flash[k.to_s]
106
+ end
107
+
108
+ # Convenience accessor for `flash.now[:alert]=`.
109
+ def alert=(message)
110
+ self[:alert] = message
111
+ end
112
+
113
+ # Convenience accessor for `flash.now[:notice]=`.
114
+ def notice=(message)
115
+ self[:notice] = message
116
+ end
117
+ end
118
+
119
+ class FlashHash
120
+ include Enumerable
121
+
122
+ def self.from_session_value(value) # :nodoc:
123
+ case value
124
+ when FlashHash # Rails 3.1, 3.2
125
+ flashes = value.instance_variable_get(:@flashes)
126
+ if discard = value.instance_variable_get(:@used)
127
+ flashes.except!(*discard)
128
+ end
129
+ new(flashes, flashes.keys)
130
+ when Hash # Rails 4.0
131
+ flashes = value["flashes"]
132
+ if discard = value["discard"]
133
+ flashes.except!(*discard)
134
+ end
135
+ new(flashes, flashes.keys)
136
+ else
137
+ new
138
+ end
139
+ end
140
+
141
+ # Builds a hash containing the flashes to keep for the next request. If there
142
+ # are none to keep, returns `nil`.
143
+ def to_session_value # :nodoc:
144
+ flashes_to_keep = @flashes.except(*@discard)
145
+ return nil if flashes_to_keep.empty?
146
+ { "discard" => [], "flashes" => flashes_to_keep }
147
+ end
148
+
149
+ def initialize(flashes = {}, discard = []) # :nodoc:
150
+ @discard = Set.new(stringify_array(discard))
151
+ @flashes = flashes.stringify_keys
152
+ @now = nil
153
+ end
154
+
155
+ def initialize_copy(other)
156
+ if other.now_is_loaded?
157
+ @now = other.now.dup
158
+ @now.flash = self
159
+ end
160
+ super
161
+ end
162
+
163
+ def []=(k, v)
164
+ k = k.to_s
165
+ @discard.delete k
166
+ @flashes[k] = v
167
+ end
168
+
169
+ def [](k)
170
+ @flashes[k.to_s]
171
+ end
172
+
173
+ def update(h) # :nodoc:
174
+ @discard.subtract stringify_array(h.keys)
175
+ @flashes.update h.stringify_keys
176
+ self
177
+ end
178
+
179
+ def keys
180
+ @flashes.keys
181
+ end
182
+
183
+ def key?(name)
184
+ @flashes.key? name.to_s
185
+ end
186
+
187
+ # Immediately deletes the single flash entry. Use this method when you want
188
+ # remove the message within the current action. See also #discard.
189
+ def delete(key)
190
+ key = key.to_s
191
+ @discard.delete key
192
+ @flashes.delete key
193
+ self
194
+ end
195
+
196
+ def to_hash
197
+ @flashes.dup
198
+ end
199
+
200
+ def empty?
201
+ @flashes.empty?
202
+ end
203
+
204
+ def clear
205
+ @discard.clear
206
+ @flashes.clear
207
+ end
208
+
209
+ def each(&block)
210
+ @flashes.each(&block)
211
+ end
212
+
213
+ alias :merge! :update
214
+
215
+ def replace(h) # :nodoc:
216
+ @discard.clear
217
+ @flashes.replace h.stringify_keys
218
+ self
219
+ end
220
+
221
+ # Sets a flash that will not be available to the next action, only to the
222
+ # current.
223
+ #
224
+ # flash.now[:message] = "Hello current action"
225
+ #
226
+ # This method enables you to use the flash as a central messaging system in your
227
+ # app. When you need to pass an object to the next action, you use the standard
228
+ # flash assign (`[]=`). When you need to pass an object to the current action,
229
+ # you use `now`, and your object will vanish when the current action is done.
230
+ #
231
+ # Entries set via `now` are accessed the same way as standard entries:
232
+ # `flash['my-key']`.
233
+ #
234
+ # Also, brings two convenience accessors:
235
+ #
236
+ # flash.now.alert = "Beware now!"
237
+ # # Equivalent to flash.now[:alert] = "Beware now!"
238
+ #
239
+ # flash.now.notice = "Good luck now!"
240
+ # # Equivalent to flash.now[:notice] = "Good luck now!"
241
+ def now
242
+ @now ||= FlashNow.new(self)
243
+ end
244
+
245
+ # Keeps either the entire current flash or a specific flash entry available for
246
+ # the next action:
247
+ #
248
+ # flash.keep # keeps the entire flash
249
+ # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
250
+ def keep(k = nil)
251
+ k = k.to_s if k
252
+ @discard.subtract Array(k || keys)
253
+ k ? self[k] : self
254
+ end
255
+
256
+ # Marks the entire flash or a single flash entry to be discarded by the end of
257
+ # the current action:
258
+ #
259
+ # flash.discard # discard the entire flash at the end of the current action
260
+ # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
261
+ #
262
+ # Use this method when you want to display the message in the current action but
263
+ # not in the next one. See also #delete.
264
+ def discard(k = nil)
265
+ k = k.to_s if k
266
+ @discard.merge Array(k || keys)
267
+ k ? self[k] : self
268
+ end
269
+
270
+ # Mark for removal entries that were kept, and delete unkept ones.
271
+ #
272
+ # This method is called automatically by filters, so you generally don't need to
273
+ # care about it.
274
+ def sweep # :nodoc:
275
+ @discard.each { |k| @flashes.delete k }
276
+ @discard.replace @flashes.keys
277
+ end
278
+
279
+ # Convenience accessor for `flash[:alert]`.
280
+ def alert
281
+ self[:alert]
282
+ end
283
+
284
+ # Convenience accessor for `flash[:alert]=`.
285
+ def alert=(message)
286
+ self[:alert] = message
287
+ end
288
+
289
+ # Convenience accessor for `flash[:notice]`.
290
+ def notice
291
+ self[:notice]
292
+ end
293
+
294
+ # Convenience accessor for `flash[:notice]=`.
295
+ def notice=(message)
296
+ self[:notice] = message
297
+ end
298
+
299
+ protected
300
+ def now_is_loaded?
301
+ @now
302
+ end
303
+
304
+ private
305
+ def stringify_array(array) # :doc:
306
+ array.map do |item|
307
+ item.kind_of?(Symbol) ? item.to_s : item
308
+ end
309
+ end
310
+ end
311
+
312
+ def self.new(app) app; end
313
+ end
314
+
315
+ class Request
316
+ prepend Flash::RequestMethods
317
+ end
318
+ end