omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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