actionpack 3.1.12 → 3.2.0.rc1

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 (128) hide show
  1. data/CHANGELOG.md +5503 -108
  2. data/README.rdoc +3 -3
  3. data/lib/abstract_controller/asset_paths.rb +1 -1
  4. data/lib/abstract_controller/base.rb +1 -1
  5. data/lib/abstract_controller/callbacks.rb +102 -18
  6. data/lib/abstract_controller/helpers.rb +1 -1
  7. data/lib/abstract_controller/layouts.rb +116 -50
  8. data/lib/abstract_controller/logger.rb +1 -1
  9. data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
  10. data/lib/abstract_controller/rendering.rb +1 -6
  11. data/lib/abstract_controller/view_paths.rb +6 -5
  12. data/lib/action_controller.rb +0 -15
  13. data/lib/action_controller/caching.rb +0 -1
  14. data/lib/action_controller/caching/actions.rb +5 -6
  15. data/lib/action_controller/caching/fragments.rb +18 -18
  16. data/lib/action_controller/caching/pages.rb +7 -6
  17. data/lib/action_controller/caching/sweeping.rb +1 -1
  18. data/lib/action_controller/log_subscriber.rb +8 -4
  19. data/lib/action_controller/metal.rb +7 -1
  20. data/lib/action_controller/metal/conditional_get.rb +49 -4
  21. data/lib/action_controller/metal/data_streaming.rb +17 -5
  22. data/lib/action_controller/metal/force_ssl.rb +8 -5
  23. data/lib/action_controller/metal/helpers.rb +7 -4
  24. data/lib/action_controller/metal/http_authentication.rb +9 -12
  25. data/lib/action_controller/metal/instrumentation.rb +9 -4
  26. data/lib/action_controller/metal/mime_responds.rb +4 -4
  27. data/lib/action_controller/metal/params_wrapper.rb +12 -8
  28. data/lib/action_controller/metal/redirecting.rb +7 -6
  29. data/lib/action_controller/metal/renderers.rb +9 -11
  30. data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
  31. data/lib/action_controller/metal/rescue.rb +13 -0
  32. data/lib/action_controller/metal/responder.rb +11 -23
  33. data/lib/action_controller/metal/streaming.rb +0 -25
  34. data/lib/action_controller/railtie.rb +1 -0
  35. data/lib/action_controller/railties/paths.rb +4 -3
  36. data/lib/action_controller/record_identifier.rb +4 -4
  37. data/lib/action_controller/test_case.rb +60 -56
  38. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
  39. data/lib/action_dispatch.rb +5 -1
  40. data/lib/action_dispatch/http/cache.rb +27 -15
  41. data/lib/action_dispatch/http/filter_parameters.rb +3 -1
  42. data/lib/action_dispatch/http/headers.rb +3 -5
  43. data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
  44. data/lib/action_dispatch/http/mime_type.rb +7 -3
  45. data/lib/action_dispatch/http/mime_types.rb +12 -0
  46. data/lib/action_dispatch/http/parameter_filter.rb +3 -1
  47. data/lib/action_dispatch/http/parameters.rb +0 -4
  48. data/lib/action_dispatch/http/request.rb +18 -68
  49. data/lib/action_dispatch/http/response.rb +11 -32
  50. data/lib/action_dispatch/http/upload.rb +3 -14
  51. data/lib/action_dispatch/http/url.rb +1 -1
  52. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  53. data/lib/action_dispatch/middleware/cookies.rb +20 -16
  54. data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
  55. data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
  56. data/lib/action_dispatch/middleware/flash.rb +6 -9
  57. data/lib/action_dispatch/middleware/params_parser.rb +6 -11
  58. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
  59. data/lib/action_dispatch/middleware/reloader.rb +38 -14
  60. data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
  61. data/lib/action_dispatch/middleware/request_id.rb +39 -0
  62. data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
  63. data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
  64. data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
  65. data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
  66. data/lib/action_dispatch/middleware/static.rb +2 -10
  67. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  68. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
  69. data/lib/action_dispatch/railtie.rb +15 -1
  70. data/lib/action_dispatch/routing.rb +1 -2
  71. data/lib/action_dispatch/routing/mapper.rb +108 -107
  72. data/lib/action_dispatch/routing/redirection.rb +63 -69
  73. data/lib/action_dispatch/routing/route_set.rb +75 -43
  74. data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
  75. data/lib/action_dispatch/routing/url_for.rb +3 -3
  76. data/lib/action_dispatch/testing/assertions/response.rb +5 -7
  77. data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
  78. data/lib/action_dispatch/testing/integration.rb +8 -25
  79. data/lib/action_dispatch/testing/test_process.rb +3 -2
  80. data/lib/action_dispatch/testing/test_request.rb +4 -23
  81. data/lib/action_pack/version.rb +3 -3
  82. data/lib/action_view.rb +1 -5
  83. data/lib/action_view/asset_paths.rb +7 -8
  84. data/lib/action_view/base.rb +7 -5
  85. data/lib/action_view/helpers/asset_paths.rb +1 -1
  86. data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
  87. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
  88. data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
  89. data/lib/action_view/helpers/capture_helper.rb +3 -3
  90. data/lib/action_view/helpers/controller_helper.rb +1 -1
  91. data/lib/action_view/helpers/date_helper.rb +26 -18
  92. data/lib/action_view/helpers/debug_helper.rb +1 -1
  93. data/lib/action_view/helpers/form_helper.rb +71 -13
  94. data/lib/action_view/helpers/form_options_helper.rb +65 -34
  95. data/lib/action_view/helpers/form_tag_helper.rb +24 -18
  96. data/lib/action_view/helpers/javascript_helper.rb +12 -3
  97. data/lib/action_view/helpers/number_helper.rb +3 -2
  98. data/lib/action_view/helpers/record_tag_helper.rb +51 -5
  99. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  100. data/lib/action_view/helpers/sanitize_helper.rb +6 -7
  101. data/lib/action_view/helpers/tag_helper.rb +1 -1
  102. data/lib/action_view/helpers/text_helper.rb +5 -4
  103. data/lib/action_view/helpers/url_helper.rb +19 -11
  104. data/lib/action_view/locale/en.yml +6 -0
  105. data/lib/action_view/log_subscriber.rb +1 -1
  106. data/lib/action_view/lookup_context.rb +123 -125
  107. data/lib/action_view/path_set.rb +60 -13
  108. data/lib/action_view/renderer/abstract_renderer.rb +16 -11
  109. data/lib/action_view/renderer/partial_renderer.rb +59 -40
  110. data/lib/action_view/renderer/template_renderer.rb +29 -17
  111. data/lib/action_view/template.rb +0 -1
  112. data/lib/action_view/template/error.rb +6 -5
  113. data/lib/action_view/template/handlers.rb +0 -6
  114. data/lib/action_view/template/handlers/builder.rb +10 -1
  115. data/lib/action_view/template/handlers/erb.rb +2 -2
  116. data/lib/action_view/template/resolver.rb +20 -31
  117. data/lib/action_view/test_case.rb +7 -10
  118. data/lib/sprockets/assets.rake +1 -1
  119. data/lib/sprockets/bootstrap.rb +3 -31
  120. data/lib/sprockets/compressors.rb +69 -7
  121. data/lib/sprockets/helpers/rails_helper.rb +6 -11
  122. data/lib/sprockets/railtie.rb +1 -0
  123. data/lib/sprockets/static_compiler.rb +0 -3
  124. metadata +57 -86
  125. checksums.yaml +0 -7
  126. data/lib/action_dispatch/middleware/closed_error.rb +0 -7
  127. data/lib/action_dispatch/routing/route.rb +0 -67
  128. data/lib/action_view/template/handler.rb +0 -49
@@ -1,4 +1,5 @@
1
- require "active_support/core_ext/object/blank"
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'active_support/core_ext/hash/keys'
2
3
 
3
4
  module ActionDispatch
4
5
  class Request
@@ -59,7 +60,7 @@ module ActionDispatch
59
60
  # The option symbols for setting cookies are:
60
61
  #
61
62
  # * <tt>:value</tt> - The cookie's value or list of values (as an array).
62
- # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
63
+ # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
63
64
  # of the application.
64
65
  # * <tt>:domain</tt> - The domain for which this cookie applies so you can
65
66
  # restrict to the domain level. If you use a schema like www.example.com
@@ -120,10 +121,6 @@ module ActionDispatch
120
121
  @cookies = {}
121
122
  end
122
123
 
123
- attr_reader :closed
124
- alias :closed? :closed
125
- def close!; @closed = true end
126
-
127
124
  def each(&block)
128
125
  @cookies.each(&block)
129
126
  end
@@ -139,7 +136,7 @@ module ActionDispatch
139
136
  alias :has_key? :key?
140
137
 
141
138
  def update(other_hash)
142
- @cookies.update other_hash
139
+ @cookies.update other_hash.stringify_keys
143
140
  self
144
141
  end
145
142
 
@@ -164,7 +161,6 @@ module ActionDispatch
164
161
  # Sets the cookie named +name+. The second argument may be the very cookie
165
162
  # value, or a hash of options as documented above.
166
163
  def []=(key, options)
167
- raise ClosedError, :cookies if closed?
168
164
  if options.is_a?(Hash)
169
165
  options.symbolize_keys!
170
166
  value = options[:value]
@@ -173,7 +169,7 @@ module ActionDispatch
173
169
  options = { :value => value }
174
170
  end
175
171
 
176
- value = @cookies[key.to_s] = value
172
+ @cookies[key.to_s] = value
177
173
 
178
174
  handle_options(options)
179
175
 
@@ -195,6 +191,11 @@ module ActionDispatch
195
191
  value
196
192
  end
197
193
 
194
+ # Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
195
+ def clear(options = {})
196
+ @cookies.each_key{ |k| delete(k, options) }
197
+ end
198
+
198
199
  # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
199
200
  #
200
201
  # cookies.permanent[:prefers_open_id] = true
@@ -232,10 +233,18 @@ module ActionDispatch
232
233
  @delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
233
234
  end
234
235
 
236
+ def recycle! #:nodoc:
237
+ @set_cookies.clear
238
+ @delete_cookies.clear
239
+ end
240
+
241
+ mattr_accessor :always_write_cookie
242
+ self.always_write_cookie = false
243
+
235
244
  private
236
245
 
237
246
  def write_cookie?(cookie)
238
- @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development?
247
+ @secure || !cookie[:secure] || always_write_cookie
239
248
  end
240
249
  end
241
250
 
@@ -245,7 +254,6 @@ module ActionDispatch
245
254
  end
246
255
 
247
256
  def []=(key, options)
248
- raise ClosedError, :cookies if closed?
249
257
  if options.is_a?(Hash)
250
258
  options.symbolize_keys!
251
259
  else
@@ -284,7 +292,6 @@ module ActionDispatch
284
292
  end
285
293
 
286
294
  def []=(key, options)
287
- raise ClosedError, :cookies if closed?
288
295
  if options.is_a?(Hash)
289
296
  options.symbolize_keys!
290
297
  options[:value] = @verifier.generate(options[:value])
@@ -315,7 +322,7 @@ module ActionDispatch
315
322
 
316
323
  if secret.length < SECRET_MIN_LENGTH
317
324
  raise ArgumentError, "Secret should be something secure, " +
318
- "like \"#{SecureRandom.hex(16)}\". The value you " +
325
+ "like \"#{SecureRandom.hex(16)}\". The value you " +
319
326
  "provided, \"#{secret}\", is shorter than the minimum length " +
320
327
  "of #{SECRET_MIN_LENGTH} characters"
321
328
  end
@@ -338,9 +345,6 @@ module ActionDispatch
338
345
  end
339
346
 
340
347
  [status, headers, body]
341
- ensure
342
- cookie_jar = ActionDispatch::Request.new(env).cookie_jar unless cookie_jar
343
- cookie_jar.close!
344
348
  end
345
349
  end
346
350
  end
@@ -0,0 +1,82 @@
1
+ require 'action_dispatch/http/request'
2
+ require 'action_dispatch/middleware/exception_wrapper'
3
+
4
+ module ActionDispatch
5
+ # This middleware is responsible for logging exceptions and
6
+ # showing a debugging page in case the request is local.
7
+ class DebugExceptions
8
+ RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
9
+
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ def call(env)
15
+ begin
16
+ response = @app.call(env)
17
+
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
25
+ end
26
+
27
+ exception ? render_exception(env, exception) : response
28
+ end
29
+
30
+ private
31
+
32
+ def render_exception(env, exception)
33
+ wrapper = ExceptionWrapper.new(env, exception)
34
+ log_error(env, wrapper)
35
+
36
+ if env['action_dispatch.show_detailed_exceptions']
37
+ 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
43
+ )
44
+
45
+ file = "rescues/#{wrapper.rescue_template}"
46
+ body = template.render(:template => file, :layout => 'rescues/layout')
47
+ render(wrapper.status_code, body)
48
+ else
49
+ raise exception
50
+ end
51
+ end
52
+
53
+ def render(status, body)
54
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
55
+ end
56
+
57
+ def log_error(env, wrapper)
58
+ logger = logger(env)
59
+ return unless logger
60
+
61
+ exception = wrapper.exception
62
+
63
+ trace = wrapper.application_trace
64
+ trace = wrapper.framework_trace if trace.empty?
65
+
66
+ ActiveSupport::Deprecation.silence do
67
+ message = "\n#{exception.class} (#{exception.message}):\n"
68
+ message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
69
+ message << " " << trace.join("\n ")
70
+ logger.fatal("#{message}\n\n")
71
+ end
72
+ end
73
+
74
+ def logger(env)
75
+ env['action_dispatch.logger'] || stderr_logger
76
+ end
77
+
78
+ def stderr_logger
79
+ @stderr_logger ||= Logger.new($stderr)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,78 @@
1
+ require 'action_controller/metal/exceptions'
2
+ require 'active_support/core_ext/exception'
3
+
4
+ module ActionDispatch
5
+ class ExceptionWrapper
6
+ cattr_accessor :rescue_responses
7
+ @@rescue_responses = Hash.new(:internal_server_error)
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
14
+ )
15
+
16
+ cattr_accessor :rescue_templates
17
+ @@rescue_templates = Hash.new('diagnostics')
18
+ @@rescue_templates.merge!(
19
+ 'ActionView::MissingTemplate' => 'missing_template',
20
+ 'ActionController::RoutingError' => 'routing_error',
21
+ 'AbstractController::ActionNotFound' => 'unknown_action',
22
+ 'ActionView::Template::Error' => 'template_error'
23
+ )
24
+
25
+ attr_reader :env, :exception
26
+
27
+ def initialize(env, exception)
28
+ @env = env
29
+ @exception = original_exception(exception)
30
+ end
31
+
32
+ def rescue_template
33
+ @@rescue_templates[@exception.class.name]
34
+ end
35
+
36
+ def status_code
37
+ Rack::Utils.status_code(@@rescue_responses[@exception.class.name])
38
+ end
39
+
40
+ def application_trace
41
+ clean_backtrace(:silent)
42
+ end
43
+
44
+ def framework_trace
45
+ clean_backtrace(:noise)
46
+ end
47
+
48
+ def full_trace
49
+ clean_backtrace(:all)
50
+ end
51
+
52
+ private
53
+
54
+ def original_exception(exception)
55
+ if registered_original_exception?(exception)
56
+ exception.original_exception
57
+ else
58
+ exception
59
+ end
60
+ end
61
+
62
+ def registered_original_exception?(exception)
63
+ exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
64
+ end
65
+
66
+ def clean_backtrace(*args)
67
+ if backtrace_cleaner
68
+ backtrace_cleaner.clean(@exception.backtrace, *args)
69
+ else
70
+ @exception.backtrace
71
+ end
72
+ end
73
+
74
+ def backtrace_cleaner
75
+ @backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
76
+ end
77
+ end
78
+ end
@@ -4,7 +4,7 @@ module ActionDispatch
4
4
  # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
5
5
  # to put a new one.
6
6
  def flash
7
- @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new).tap(&:sweep)
7
+ @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
8
8
  end
9
9
  end
10
10
 
@@ -93,7 +93,6 @@ module ActionDispatch
93
93
  end
94
94
 
95
95
  def []=(k, v) #:nodoc:
96
- raise ClosedError, :flash if closed?
97
96
  keep(k)
98
97
  @flashes[k] = v
99
98
  end
@@ -159,10 +158,6 @@ module ActionDispatch
159
158
  @now ||= FlashNow.new(self)
160
159
  end
161
160
 
162
- attr_reader :closed
163
- alias :closed? :closed
164
- def close!; @closed = true; end
165
-
166
161
  # Keeps either the entire current flash or a specific flash entry available for the next action:
167
162
  #
168
163
  # flash.keep # keeps the entire flash
@@ -240,6 +235,10 @@ module ActionDispatch
240
235
  end
241
236
 
242
237
  def call(env)
238
+ if (session = env['rack.session']) && (flash = session['flash'])
239
+ flash.sweep
240
+ end
241
+
243
242
  @app.call(env)
244
243
  ensure
245
244
  session = env['rack.session'] || {}
@@ -254,11 +253,9 @@ module ActionDispatch
254
253
  end
255
254
 
256
255
  env[KEY] = new_hash
257
- new_hash.close!
258
256
  end
259
257
 
260
- if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
261
- session.key?('flash') && session['flash'].empty?
258
+ if session.key?('flash') && session['flash'].empty?
262
259
  session.delete('flash')
263
260
  end
264
261
  end
@@ -38,7 +38,7 @@ module ActionDispatch
38
38
  when Proc
39
39
  strategy.call(request.raw_post)
40
40
  when :xml_simple, :xml_node
41
- data = request.deep_munge(Hash.from_xml(request.body.read) || {})
41
+ data = Hash.from_xml(request.body.read) || {}
42
42
  request.body.rewind if request.body.respond_to?(:rewind)
43
43
  data.with_indifferent_access
44
44
  when :yaml
@@ -47,19 +47,14 @@ module ActionDispatch
47
47
  data = ActiveSupport::JSON.decode(request.body)
48
48
  request.body.rewind if request.body.respond_to?(:rewind)
49
49
  data = {:_json => data} unless data.is_a?(Hash)
50
- request.deep_munge(data).with_indifferent_access
50
+ data.with_indifferent_access
51
51
  else
52
52
  false
53
53
  end
54
54
  rescue Exception => e # YAML, XML or Ruby code block errors
55
- logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
55
+ logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
56
56
 
57
- raise
58
- { "body" => request.raw_post,
59
- "content_type" => request.content_mime_type,
60
- "content_length" => request.content_length,
61
- "exception" => "#{e.message} (#{e.class})",
62
- "backtrace" => e.backtrace }
57
+ raise e
63
58
  end
64
59
 
65
60
  def content_type_from_legacy_post_data_format_header(env)
@@ -73,8 +68,8 @@ module ActionDispatch
73
68
  nil
74
69
  end
75
70
 
76
- def logger
77
- defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
71
+ def logger(env)
72
+ env['action_dispatch.logger'] || Logger.new($stderr)
78
73
  end
79
74
  end
80
75
  end
@@ -0,0 +1,30 @@
1
+ module ActionDispatch
2
+ # A simple Rack application that renders exceptions in the given public path.
3
+ class PublicExceptions
4
+ attr_accessor :public_path
5
+
6
+ def initialize(public_path)
7
+ @public_path = public_path
8
+ end
9
+
10
+ def call(env)
11
+ status = env["PATH_INFO"][1..-1]
12
+ locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
13
+ path = "#{public_path}/#{status}.html"
14
+
15
+ if locale_path && File.exist?(locale_path)
16
+ render(status, File.read(locale_path))
17
+ elsif File.exist?(path)
18
+ render(status, File.read(path))
19
+ else
20
+ [404, { "X-Cascade" => "pass" }, []]
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def render(status, body)
27
+ [status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
28
+ end
29
+ end
30
+ end
@@ -43,34 +43,58 @@ module ActionDispatch
43
43
 
44
44
  # Execute all prepare callbacks.
45
45
  def self.prepare!
46
- new(nil).run_callbacks :prepare
46
+ new(nil).prepare!
47
47
  end
48
48
 
49
49
  # Execute all cleanup callbacks.
50
50
  def self.cleanup!
51
- new(nil).run_callbacks :cleanup
51
+ new(nil).cleanup!
52
52
  end
53
53
 
54
- def initialize(app)
54
+ def initialize(app, condition=nil)
55
55
  @app = app
56
- end
57
-
58
- module CleanupOnClose
59
- def close
60
- super if defined?(super)
61
- ensure
62
- ActionDispatch::Reloader.cleanup!
63
- end
56
+ @condition = condition || lambda { true }
57
+ @validated = true
64
58
  end
65
59
 
66
60
  def call(env)
67
- run_callbacks :prepare
61
+ @validated = @condition.call
62
+ prepare!
68
63
  response = @app.call(env)
69
- response[2].extend(CleanupOnClose)
64
+ response[2].extend(module_hook)
70
65
  response
71
66
  rescue Exception
72
- run_callbacks :cleanup
67
+ cleanup!
73
68
  raise
74
69
  end
70
+
71
+ def prepare! #:nodoc:
72
+ run_callbacks :prepare if validated?
73
+ end
74
+
75
+ def cleanup! #:nodoc:
76
+ run_callbacks :cleanup if validated?
77
+ ensure
78
+ @validated = true
79
+ end
80
+
81
+ private
82
+
83
+ def validated? #:nodoc:
84
+ @validated
85
+ end
86
+
87
+ def module_hook #:nodoc:
88
+ middleware = self
89
+ Module.new do
90
+ define_method :close do
91
+ begin
92
+ super() if defined?(super)
93
+ ensure
94
+ middleware.cleanup!
95
+ end
96
+ end
97
+ end
98
+ end
75
99
  end
76
100
  end