actionpack 1.9.1 → 1.10.1
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.
- data/CHANGELOG +237 -0
- data/README +12 -12
- data/lib/action_controller.rb +17 -12
- data/lib/action_controller/assertions.rb +119 -67
- data/lib/action_controller/base.rb +184 -102
- data/lib/action_controller/benchmarking.rb +35 -6
- data/lib/action_controller/caching.rb +115 -58
- data/lib/action_controller/cgi_ext/cgi_methods.rb +54 -21
- data/lib/action_controller/cgi_ext/cookie_performance_fix.rb +39 -35
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +34 -21
- data/lib/action_controller/cgi_process.rb +23 -20
- data/lib/action_controller/components.rb +11 -2
- data/lib/action_controller/dependencies.rb +0 -5
- data/lib/action_controller/deprecated_redirects.rb +17 -0
- data/lib/action_controller/filters.rb +13 -9
- data/lib/action_controller/flash.rb +7 -7
- data/lib/action_controller/helpers.rb +1 -14
- data/lib/action_controller/layout.rb +40 -29
- data/lib/action_controller/macros/auto_complete.rb +52 -0
- data/lib/action_controller/macros/in_place_editing.rb +32 -0
- data/lib/action_controller/pagination.rb +44 -28
- data/lib/action_controller/request.rb +54 -40
- data/lib/action_controller/rescue.rb +8 -6
- data/lib/action_controller/routing.rb +77 -28
- data/lib/action_controller/scaffolding.rb +10 -14
- data/lib/action_controller/session/active_record_store.rb +36 -7
- data/lib/action_controller/session_management.rb +126 -0
- data/lib/action_controller/streaming.rb +14 -5
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +1 -1
- data/lib/action_controller/templates/rescues/_trace.rhtml +24 -0
- data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -13
- data/lib/action_controller/templates/rescues/template_error.rhtml +4 -2
- data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
- data/lib/action_controller/test_process.rb +35 -17
- data/lib/action_controller/upload_progress.rb +52 -0
- data/lib/action_controller/url_rewriter.rb +21 -16
- data/lib/action_controller/vendor/html-scanner/html/document.rb +2 -2
- data/lib/action_controller/vendor/html-scanner/html/node.rb +30 -3
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +204 -60
- data/lib/action_view/compiled_templates.rb +70 -0
- data/lib/action_view/helpers/active_record_helper.rb +7 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +22 -12
- data/lib/action_view/helpers/capture_helper.rb +2 -10
- data/lib/action_view/helpers/date_helper.rb +21 -13
- data/lib/action_view/helpers/form_helper.rb +14 -10
- data/lib/action_view/helpers/form_options_helper.rb +4 -4
- data/lib/action_view/helpers/form_tag_helper.rb +59 -25
- data/lib/action_view/helpers/java_script_macros_helper.rb +188 -0
- data/lib/action_view/helpers/javascript_helper.rb +68 -133
- data/lib/action_view/helpers/javascripts/controls.js +427 -165
- data/lib/action_view/helpers/javascripts/dragdrop.js +256 -277
- data/lib/action_view/helpers/javascripts/effects.js +766 -277
- data/lib/action_view/helpers/javascripts/prototype.js +906 -218
- data/lib/action_view/helpers/javascripts/slider.js +258 -0
- data/lib/action_view/helpers/number_helper.rb +4 -3
- data/lib/action_view/helpers/pagination_helper.rb +42 -27
- data/lib/action_view/helpers/tag_helper.rb +25 -11
- data/lib/action_view/helpers/text_helper.rb +119 -13
- data/lib/action_view/helpers/upload_progress_helper.rb +2 -2
- data/lib/action_view/helpers/url_helper.rb +68 -21
- data/lib/action_view/partials.rb +17 -6
- data/lib/action_view/template_error.rb +19 -24
- data/rakefile +4 -3
- data/test/abstract_unit.rb +2 -1
- data/test/controller/action_pack_assertions_test.rb +62 -2
- data/test/controller/active_record_assertions_test.rb +5 -6
- data/test/controller/active_record_store_test.rb +23 -1
- data/test/controller/addresses_render_test.rb +4 -0
- data/test/controller/{base_tests.rb → base_test.rb} +4 -3
- data/test/controller/benchmark_test.rb +36 -0
- data/test/controller/caching_filestore.rb +22 -40
- data/test/controller/capture_test.rb +10 -1
- data/test/controller/cgi_test.rb +145 -23
- data/test/controller/components_test.rb +50 -0
- data/test/controller/custom_handler_test.rb +3 -3
- data/test/controller/fake_controllers.rb +24 -0
- data/test/controller/filters_test.rb +6 -6
- data/test/controller/flash_test.rb +6 -6
- data/test/controller/fragment_store_setting_test.rb +45 -0
- data/test/controller/helper_test.rb +1 -3
- data/test/controller/new_render_test.rb +119 -7
- data/test/controller/redirect_test.rb +11 -1
- data/test/controller/render_test.rb +34 -1
- data/test/controller/request_test.rb +14 -5
- data/test/controller/routing_test.rb +238 -42
- data/test/controller/send_file_test.rb +11 -10
- data/test/controller/session_management_test.rb +94 -0
- data/test/controller/test_test.rb +194 -5
- data/test/controller/url_rewriter_test.rb +46 -0
- data/test/fixtures/layouts/talk_from_action.rhtml +2 -0
- data/test/fixtures/layouts/yield.rhtml +2 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/large_text_file +10 -0
- data/test/fixtures/multipart/mixed_files +0 -0
- data/test/fixtures/multipart/single_parameter +5 -0
- data/test/fixtures/multipart/text_file +10 -0
- data/test/fixtures/test/_customer_greeting.rhtml +1 -0
- data/test/fixtures/test/_hash_object.rhtml +1 -0
- data/test/fixtures/test/_person.rhtml +2 -0
- data/test/fixtures/test/action_talk_to_layout.rhtml +2 -0
- data/test/fixtures/test/content_for.rhtml +2 -0
- data/test/fixtures/test/potential_conflicts.rhtml +4 -0
- data/test/template/active_record_helper_test.rb +15 -8
- data/test/template/asset_tag_helper_test.rb +40 -16
- data/test/template/compiled_templates_tests.rb +63 -0
- data/test/template/date_helper_test.rb +80 -4
- data/test/template/form_helper_test.rb +48 -42
- data/test/template/form_options_helper_test.rb +40 -40
- data/test/template/form_tag_helper_test.rb +21 -15
- data/test/template/java_script_macros_helper_test.rb +56 -0
- data/test/template/javascript_helper_test.rb +70 -47
- data/test/template/number_helper_test.rb +2 -0
- data/test/template/tag_helper_test.rb +9 -0
- data/test/template/text_helper_test.rb +146 -1
- data/test/template/upload_progress_helper_testx.rb +11 -147
- data/test/template/url_helper_test.rb +90 -22
- data/test/testing_sandbox.rb +26 -0
- metadata +37 -7
- data/lib/action_controller/auto_complete.rb +0 -47
- data/lib/action_controller/deprecated_renders_and_redirects.rb +0 -76
- data/lib/action_controller/session.rb +0 -14
@@ -6,17 +6,45 @@ module ActionController #:nodoc:
|
|
6
6
|
module Benchmarking #:nodoc:
|
7
7
|
def self.append_features(base)
|
8
8
|
super
|
9
|
-
base.
|
9
|
+
base.extend(ClassMethods)
|
10
|
+
base.class_eval do
|
10
11
|
alias_method :perform_action_without_benchmark, :perform_action
|
11
12
|
alias_method :perform_action, :perform_action_with_benchmark
|
12
13
|
|
13
14
|
alias_method :render_without_benchmark, :render
|
14
15
|
alias_method :render, :render_with_benchmark
|
15
|
-
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
# Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it
|
21
|
+
# (unless <tt>use_silence</tt> is set to false).
|
22
|
+
#
|
23
|
+
# The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
|
24
|
+
# easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
|
25
|
+
# will only be conducted if the log level is low enough.
|
26
|
+
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
|
27
|
+
if logger && logger.level == log_level
|
28
|
+
result = nil
|
29
|
+
seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield }
|
30
|
+
logger.add(log_level, "#{title} (#{'%.5f' % seconds})")
|
31
|
+
result
|
32
|
+
else
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Silences the logger for the duration of the block.
|
38
|
+
def silence
|
39
|
+
old_logger_level, logger.level = logger.level, Logger::ERROR if logger
|
40
|
+
yield
|
41
|
+
ensure
|
42
|
+
logger.level = old_logger_level if logger
|
43
|
+
end
|
16
44
|
end
|
17
45
|
|
18
|
-
def render_with_benchmark(options =
|
19
|
-
|
46
|
+
def render_with_benchmark(options = nil, deprecated_status = nil)
|
47
|
+
unless logger
|
20
48
|
render_without_benchmark(options, deprecated_status)
|
21
49
|
else
|
22
50
|
db_runtime = ActiveRecord::Base.connection.reset_runtime if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
|
@@ -35,14 +63,15 @@ module ActionController #:nodoc:
|
|
35
63
|
end
|
36
64
|
|
37
65
|
def perform_action_with_benchmark
|
38
|
-
|
66
|
+
unless logger
|
39
67
|
perform_action_without_benchmark
|
40
68
|
else
|
41
69
|
runtime = [Benchmark::measure{ perform_action_without_benchmark }.real, 0.0001].max
|
42
70
|
log_message = "Completed in #{sprintf("%.5f", runtime)} (#{(1 / runtime).floor} reqs/sec)"
|
43
71
|
log_message << rendering_runtime(runtime) if @rendering_runtime
|
44
72
|
log_message << active_record_runtime(runtime) if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
|
45
|
-
log_message << "
|
73
|
+
log_message << " | #{headers["Status"]}"
|
74
|
+
log_message << " [#{complete_request_uri rescue "unknown"}]"
|
46
75
|
logger.info(log_message)
|
47
76
|
end
|
48
77
|
end
|
@@ -57,8 +57,7 @@ module ActionController #:nodoc:
|
|
57
57
|
# By default, the cache extension is .html, which makes it easy for the cached files to be picked up by the web server. If you want
|
58
58
|
# something else, like .php or .shtml, just set Base.page_cache_extension.
|
59
59
|
module Pages
|
60
|
-
def self.
|
61
|
-
super
|
60
|
+
def self.included(base) #:nodoc:
|
62
61
|
base.extend(ClassMethods)
|
63
62
|
base.class_eval do
|
64
63
|
@@page_cache_directory = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : ""
|
@@ -74,17 +73,21 @@ module ActionController #:nodoc:
|
|
74
73
|
# expire_page "/lists/show"
|
75
74
|
def expire_page(path)
|
76
75
|
return unless perform_caching
|
77
|
-
|
78
|
-
|
76
|
+
|
77
|
+
benchmark "Expired page: #{page_cache_file(path)}" do
|
78
|
+
File.delete(page_cache_path(path)) if File.exists?(page_cache_path(path))
|
79
|
+
end
|
79
80
|
end
|
80
81
|
|
81
82
|
# Manually cache the +content+ in the key determined by +path+. Example:
|
82
83
|
# cache_page "I'm the cached content", "/lists/show"
|
83
84
|
def cache_page(content, path)
|
84
85
|
return unless perform_caching
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
|
87
|
+
benchmark "Cached page: #{page_cache_file(path)}" do
|
88
|
+
FileUtils.makedirs(File.dirname(page_cache_path(path)))
|
89
|
+
File.open(page_cache_path(path), "wb+") { |f| f.write(content) }
|
90
|
+
end
|
88
91
|
end
|
89
92
|
|
90
93
|
# Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
|
@@ -98,7 +101,7 @@ module ActionController #:nodoc:
|
|
98
101
|
|
99
102
|
private
|
100
103
|
def page_cache_file(path)
|
101
|
-
name = ((path.empty? || path == "/") ? "/index" : path)
|
104
|
+
name = ((path.empty? || path == "/") ? "/index" : URI.unescape(path))
|
102
105
|
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
103
106
|
return name
|
104
107
|
end
|
@@ -232,27 +235,32 @@ module ActionController #:nodoc:
|
|
232
235
|
# up a lot of memory since each process keeps all the caches in memory.
|
233
236
|
# * DRbStore: Keeps the fragments in the memory of a separate, shared DRb process. This works for all environments and only keeps one cache
|
234
237
|
# around for all processes, but requires that you run and manage a separate DRb process.
|
235
|
-
# *
|
238
|
+
# * MemCacheStore: Works like DRbStore, but uses Danga's MemCache instead.
|
236
239
|
#
|
237
240
|
# Configuration examples (MemoryStore is the default):
|
238
241
|
#
|
239
|
-
# ActionController::Base.fragment_cache_store =
|
240
|
-
#
|
241
|
-
#
|
242
|
-
# ActionController::Base.fragment_cache_store =
|
243
|
-
#
|
244
|
-
#
|
245
|
-
# ActionController::Base.fragment_cache_store =
|
246
|
-
# ActionController::Caching::Fragments::DRbStore.new("druby://localhost:9192")
|
247
|
-
#
|
248
|
-
# ActionController::Base.fragment_cache_store =
|
249
|
-
# ActionController::Caching::Fragments::FileStore.new("localhost")
|
242
|
+
# ActionController::Base.fragment_cache_store = :memory_store
|
243
|
+
# ActionController::Base.fragment_cache_store = :file_store, "/path/to/cache/directory"
|
244
|
+
# ActionController::Base.fragment_cache_store = :drb_store, "druby://localhost:9192"
|
245
|
+
# ActionController::Base.fragment_cache_store = :mem_cache_store, "localhost"
|
246
|
+
# ActionController::Base.fragment_cache_store = MyOwnStore.new("parameter")
|
250
247
|
module Fragments
|
251
248
|
def self.append_features(base) #:nodoc:
|
252
249
|
super
|
253
250
|
base.class_eval do
|
254
251
|
@@fragment_cache_store = MemoryStore.new
|
255
|
-
|
252
|
+
cattr_reader :fragment_cache_store
|
253
|
+
|
254
|
+
def self.fragment_cache_store=(store_option)
|
255
|
+
store, *parameters = *([ store_option ].flatten)
|
256
|
+
@@fragment_cache_store = if store.is_a?(Symbol)
|
257
|
+
store_class_name = (store == :drb_store ? "DRbStore" : store.to_s.camelize)
|
258
|
+
store_class = ActionController::Caching::Fragments.const_get(store_class_name)
|
259
|
+
parameters.empty? ? store.new : store_class.new(*parameters)
|
260
|
+
else
|
261
|
+
store
|
262
|
+
end
|
263
|
+
end
|
256
264
|
end
|
257
265
|
end
|
258
266
|
|
@@ -261,7 +269,7 @@ module ActionController #:nodoc:
|
|
261
269
|
end
|
262
270
|
|
263
271
|
# Called by CacheHelper#cache
|
264
|
-
def cache_erb_fragment(block, name = {}, options =
|
272
|
+
def cache_erb_fragment(block, name = {}, options = nil)
|
265
273
|
unless perform_caching then block.call; return end
|
266
274
|
|
267
275
|
buffer = eval("_erbout", block.binding)
|
@@ -275,24 +283,22 @@ module ActionController #:nodoc:
|
|
275
283
|
end
|
276
284
|
end
|
277
285
|
|
278
|
-
def write_fragment(name, content, options =
|
286
|
+
def write_fragment(name, content, options = nil)
|
279
287
|
return unless perform_caching
|
280
288
|
|
281
289
|
key = fragment_cache_key(name)
|
282
|
-
|
283
|
-
|
290
|
+
self.class.benchmark "Cached fragment: #{key}" do
|
291
|
+
fragment_cache_store.write(key, content, options)
|
292
|
+
end
|
284
293
|
content
|
285
294
|
end
|
286
295
|
|
287
|
-
def read_fragment(name, options =
|
296
|
+
def read_fragment(name, options = nil)
|
288
297
|
return unless perform_caching
|
289
298
|
|
290
299
|
key = fragment_cache_key(name)
|
291
|
-
|
292
|
-
|
293
|
-
cache
|
294
|
-
else
|
295
|
-
false
|
300
|
+
self.class.benchmark "Fragment read: #{key}" do
|
301
|
+
fragment_cache_store.read(key, options)
|
296
302
|
end
|
297
303
|
end
|
298
304
|
|
@@ -300,79 +306,120 @@ module ActionController #:nodoc:
|
|
300
306
|
# * String: This would normally take the form of a path like "pages/45/notes"
|
301
307
|
# * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 }
|
302
308
|
# * Regexp: Will destroy all the matched fragments, example: %r{pages/\d*/notes}
|
303
|
-
def expire_fragment(name, options =
|
309
|
+
def expire_fragment(name, options = nil)
|
304
310
|
return unless perform_caching
|
305
311
|
|
306
312
|
key = fragment_cache_key(name)
|
307
313
|
|
308
314
|
if key.is_a?(Regexp)
|
309
|
-
|
310
|
-
|
315
|
+
self.class.benchmark "Expired fragments matching: #{key.source}" do
|
316
|
+
fragment_cache_store.delete_matched(key, options)
|
317
|
+
end
|
311
318
|
else
|
312
|
-
|
313
|
-
|
319
|
+
self.class.benchmark "Expired fragment: #{key}" do
|
320
|
+
fragment_cache_store.delete(key, options)
|
321
|
+
end
|
314
322
|
end
|
315
323
|
end
|
316
324
|
|
317
325
|
# Deprecated -- just call expire_fragment with a regular expression
|
318
|
-
def expire_matched_fragments(matcher = /.*/, options =
|
326
|
+
def expire_matched_fragments(matcher = /.*/, options = nil) #:nodoc:
|
319
327
|
expire_fragment(matcher, options)
|
320
328
|
end
|
321
329
|
|
322
|
-
|
323
|
-
|
324
|
-
|
330
|
+
|
331
|
+
class UnthreadedMemoryStore #:nodoc:
|
332
|
+
def initialize #:nodoc:
|
333
|
+
@data = {}
|
325
334
|
end
|
326
335
|
|
327
|
-
def read(name, options
|
328
|
-
@
|
336
|
+
def read(name, options=nil) #:nodoc:
|
337
|
+
@data[name]
|
329
338
|
end
|
330
339
|
|
331
|
-
def write(name, value, options
|
332
|
-
@
|
340
|
+
def write(name, value, options=nil) #:nodoc:
|
341
|
+
@data[name] = value
|
333
342
|
end
|
334
343
|
|
335
|
-
def delete(name, options
|
336
|
-
@
|
344
|
+
def delete(name, options=nil) #:nodoc:
|
345
|
+
@data.delete(name)
|
337
346
|
end
|
338
347
|
|
339
|
-
def delete_matched(matcher, options) #:nodoc:
|
340
|
-
@
|
348
|
+
def delete_matched(matcher, options=nil) #:nodoc:
|
349
|
+
@data.delete_if { |k,v| k =~ matcher }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
module ThreadSafety #:nodoc:
|
354
|
+
def read(name, options=nil) #:nodoc:
|
355
|
+
@mutex.synchronize { super }
|
356
|
+
end
|
357
|
+
|
358
|
+
def write(name, value, options=nil) #:nodoc:
|
359
|
+
@mutex.synchronize { super }
|
360
|
+
end
|
361
|
+
|
362
|
+
def delete(name, options=nil) #:nodoc:
|
363
|
+
@mutex.synchronize { super }
|
364
|
+
end
|
365
|
+
|
366
|
+
def delete_matched(matcher, options=nil) #:nodoc:
|
367
|
+
@mutex.synchronize { super }
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class MemoryStore < UnthreadedMemoryStore #:nodoc:
|
372
|
+
def initialize #:nodoc:
|
373
|
+
super
|
374
|
+
if ActionController::Base.allow_concurrency
|
375
|
+
@mutex = Mutex.new
|
376
|
+
MemoryStore.send(:include, ThreadSafety)
|
377
|
+
end
|
341
378
|
end
|
342
379
|
end
|
343
380
|
|
344
381
|
class DRbStore < MemoryStore #:nodoc:
|
382
|
+
attr_reader :address
|
383
|
+
|
345
384
|
def initialize(address = 'druby://localhost:9192')
|
346
|
-
|
385
|
+
super()
|
386
|
+
@address = address
|
387
|
+
@data = DRbObject.new(nil, address)
|
347
388
|
end
|
348
389
|
end
|
349
390
|
|
350
391
|
class MemCacheStore < MemoryStore #:nodoc:
|
392
|
+
attr_reader :address
|
393
|
+
|
351
394
|
def initialize(address = 'localhost')
|
352
|
-
|
395
|
+
super()
|
396
|
+
@address = address
|
397
|
+
@data = MemCache.new(address)
|
353
398
|
end
|
354
399
|
end
|
355
400
|
|
356
|
-
class
|
401
|
+
class UnthreadedFileStore #:nodoc:
|
402
|
+
attr_reader :cache_path
|
403
|
+
|
357
404
|
def initialize(cache_path)
|
358
405
|
@cache_path = cache_path
|
359
406
|
end
|
360
407
|
|
361
|
-
def write(name, value, options =
|
408
|
+
def write(name, value, options = nil) #:nodoc:
|
362
409
|
ensure_cache_path(File.dirname(real_file_path(name)))
|
363
|
-
File.open(real_file_path(name), "
|
410
|
+
File.open(real_file_path(name), "wb+") { |f| f.write(value) }
|
364
411
|
rescue => e
|
365
|
-
Base.logger.
|
412
|
+
Base.logger.error "Couldn't create cache directory: #{name} (#{e.message})" if Base.logger
|
366
413
|
end
|
367
414
|
|
368
|
-
def read(name, options =
|
415
|
+
def read(name, options = nil) #:nodoc:
|
369
416
|
IO.read(real_file_path(name)) rescue nil
|
370
417
|
end
|
371
418
|
|
372
419
|
def delete(name, options) #:nodoc:
|
373
420
|
File.delete(real_file_path(name))
|
374
421
|
rescue SystemCallError => e
|
375
|
-
|
422
|
+
# If there's no cache, then there's nothing to complain about
|
376
423
|
end
|
377
424
|
|
378
425
|
def delete_matched(matcher, options) #:nodoc:
|
@@ -381,7 +428,7 @@ module ActionController #:nodoc:
|
|
381
428
|
begin
|
382
429
|
File.delete(f)
|
383
430
|
rescue Object => e
|
384
|
-
|
431
|
+
# If there's no cache, then there's nothing to complain about
|
385
432
|
end
|
386
433
|
end
|
387
434
|
end
|
@@ -407,7 +454,17 @@ module ActionController #:nodoc:
|
|
407
454
|
end
|
408
455
|
end
|
409
456
|
end
|
410
|
-
|
457
|
+
end
|
458
|
+
|
459
|
+
class FileStore < UnthreadedFileStore #:nodoc:
|
460
|
+
def initialize(cache_path)
|
461
|
+
super(cache_path)
|
462
|
+
if ActionController::Base.allow_concurrency
|
463
|
+
@mutex = Mutex.new
|
464
|
+
FileStore.send(:include, ThreadSafety)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
411
468
|
end
|
412
469
|
|
413
470
|
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
@@ -11,23 +11,30 @@ class CGIMethods #:nodoc:
|
|
11
11
|
parsed_params = {}
|
12
12
|
|
13
13
|
query_string.split(/[&;]/).each { |p|
|
14
|
+
# Ignore repeated delimiters.
|
15
|
+
next if p.empty?
|
16
|
+
|
14
17
|
k, v = p.split('=',2)
|
15
|
-
v = nil if (
|
18
|
+
v = nil if (v && v.empty?)
|
16
19
|
|
17
|
-
k = CGI.unescape(k)
|
18
|
-
v = CGI.unescape(v)
|
20
|
+
k = CGI.unescape(k) if k
|
21
|
+
v = CGI.unescape(v) if v
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
unless k.include?(?[)
|
24
|
+
parsed_params[k] = v
|
25
|
+
else
|
26
|
+
keys = split_key(k)
|
27
|
+
last_key = keys.pop
|
28
|
+
last_key = keys.pop if (use_array = last_key.empty?)
|
29
|
+
parent = keys.inject(parsed_params) {|h, k| h[k] ||= {}}
|
30
|
+
|
31
|
+
if use_array then (parent[last_key] ||= []) << v
|
32
|
+
else parent[last_key] = v
|
33
|
+
end
|
27
34
|
end
|
28
35
|
}
|
29
36
|
|
30
|
-
|
37
|
+
parsed_params
|
31
38
|
end
|
32
39
|
|
33
40
|
# Returns the request (POST/GET) parameters in a parsed form where pairs such as "customer[address][street]" /
|
@@ -38,14 +45,16 @@ class CGIMethods #:nodoc:
|
|
38
45
|
|
39
46
|
for key, value in params
|
40
47
|
value = [value] if key =~ /.*\[\]$/
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
unless key.include?('[')
|
49
|
+
# much faster to test for the most common case first (GET)
|
50
|
+
# and avoid the call to build_deep_hash
|
51
|
+
parsed_params[key] = get_typed_value(value[0])
|
52
|
+
else
|
53
|
+
build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key))
|
54
|
+
end
|
46
55
|
end
|
47
56
|
|
48
|
-
|
57
|
+
parsed_params
|
49
58
|
end
|
50
59
|
|
51
60
|
def self.parse_formatted_request_parameters(format, raw_post_data)
|
@@ -72,23 +81,47 @@ class CGIMethods #:nodoc:
|
|
72
81
|
keys.concat($2[1..-2].split(']['))
|
73
82
|
keys << '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings
|
74
83
|
|
75
|
-
|
84
|
+
keys
|
76
85
|
else
|
77
|
-
|
86
|
+
[key]
|
78
87
|
end
|
79
88
|
end
|
80
89
|
|
81
90
|
def CGIMethods.get_typed_value(value)
|
82
|
-
|
91
|
+
# test most frequent case first
|
92
|
+
if value.is_a?(String)
|
93
|
+
value
|
94
|
+
elsif value.respond_to?(:content_type) && ! value.content_type.blank?
|
83
95
|
# Uploaded file
|
96
|
+
unless value.respond_to?(:full_original_filename)
|
97
|
+
class << value
|
98
|
+
alias_method :full_original_filename, :original_filename
|
99
|
+
|
100
|
+
# Take the basename of the upload's original filename.
|
101
|
+
# This handles the full Windows paths given by Internet Explorer
|
102
|
+
# (and perhaps other broken user agents) without affecting
|
103
|
+
# those which give the lone filename.
|
104
|
+
# The Windows regexp is adapted from Perl's File::Basename.
|
105
|
+
def original_filename
|
106
|
+
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
107
|
+
md.captures.first
|
108
|
+
else
|
109
|
+
File.basename full_original_filename
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return the same value after overriding original_filename.
|
84
116
|
value
|
117
|
+
|
85
118
|
elsif value.respond_to?(:read)
|
86
119
|
# Value as part of a multipart request
|
87
120
|
value.read
|
88
121
|
elsif value.class == Array
|
89
122
|
value.collect { |v| CGIMethods.get_typed_value(v) }
|
90
123
|
else
|
91
|
-
#
|
124
|
+
# other value (neither string nor a multipart request)
|
92
125
|
value.to_s
|
93
126
|
end
|
94
127
|
end
|