actionpack 1.8.1 → 1.9.0
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 +309 -16
- data/README +1 -1
- data/lib/action_controller.rb +5 -0
- data/lib/action_controller/assertions.rb +57 -12
- data/lib/action_controller/auto_complete.rb +47 -0
- data/lib/action_controller/base.rb +288 -258
- data/lib/action_controller/benchmarking.rb +8 -3
- data/lib/action_controller/caching.rb +88 -42
- data/lib/action_controller/cgi_ext/cgi_ext.rb +1 -1
- data/lib/action_controller/cgi_ext/cgi_methods.rb +41 -11
- data/lib/action_controller/cgi_ext/multipart_progress.rb +169 -0
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +30 -12
- data/lib/action_controller/cgi_process.rb +39 -11
- data/lib/action_controller/code_generation.rb +235 -0
- data/lib/action_controller/cookies.rb +14 -8
- data/lib/action_controller/deprecated_renders_and_redirects.rb +76 -0
- data/lib/action_controller/filters.rb +8 -7
- data/lib/action_controller/helpers.rb +41 -6
- data/lib/action_controller/layout.rb +45 -16
- data/lib/action_controller/request.rb +86 -23
- data/lib/action_controller/rescue.rb +1 -0
- data/lib/action_controller/response.rb +1 -1
- data/lib/action_controller/routing.rb +536 -272
- data/lib/action_controller/scaffolding.rb +30 -25
- data/lib/action_controller/session/active_record_store.rb +251 -50
- data/lib/action_controller/streaming.rb +133 -0
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/edit.rhtml +2 -2
- data/lib/action_controller/templates/scaffolds/layout.rhtml +22 -18
- data/lib/action_controller/templates/scaffolds/list.rhtml +3 -3
- data/lib/action_controller/templates/scaffolds/new.rhtml +2 -2
- data/lib/action_controller/templates/scaffolds/show.rhtml +1 -1
- data/lib/action_controller/test_process.rb +68 -47
- data/lib/action_controller/upload_progress.rb +421 -0
- data/lib/action_controller/url_rewriter.rb +8 -11
- data/lib/action_controller/vendor/html-scanner/html/document.rb +6 -5
- data/lib/action_controller/vendor/html-scanner/html/node.rb +70 -14
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +17 -10
- data/lib/action_controller/vendor/html-scanner/html/version.rb +3 -3
- data/lib/action_controller/vendor/xml_simple.rb +1019 -0
- data/lib/action_controller/verification.rb +36 -30
- data/lib/action_view/base.rb +21 -14
- data/lib/action_view/helpers/active_record_helper.rb +15 -13
- data/lib/action_view/helpers/asset_tag_helper.rb +26 -9
- data/lib/action_view/helpers/benchmark_helper.rb +24 -0
- data/lib/action_view/helpers/capture_helper.rb +7 -5
- data/lib/action_view/helpers/date_helper.rb +63 -46
- data/lib/action_view/helpers/form_helper.rb +7 -1
- data/lib/action_view/helpers/form_options_helper.rb +19 -11
- data/lib/action_view/helpers/form_tag_helper.rb +5 -1
- data/lib/action_view/helpers/javascript_helper.rb +403 -35
- data/lib/action_view/helpers/javascripts/controls.js +261 -0
- data/lib/action_view/helpers/javascripts/dragdrop.js +476 -0
- data/lib/action_view/helpers/javascripts/effects.js +570 -0
- data/lib/action_view/helpers/javascripts/prototype.js +633 -371
- data/lib/action_view/helpers/number_helper.rb +11 -13
- data/lib/action_view/helpers/tag_helper.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +69 -6
- data/lib/action_view/helpers/upload_progress_helper.rb +433 -0
- data/lib/action_view/helpers/url_helper.rb +98 -3
- data/lib/action_view/partials.rb +14 -8
- data/lib/action_view/vendor/builder/xmlmarkup.rb +11 -0
- data/rakefile +13 -5
- data/test/abstract_unit.rb +1 -1
- data/test/controller/action_pack_assertions_test.rb +52 -9
- data/test/controller/active_record_assertions_test.rb +119 -120
- data/test/controller/active_record_store_test.rb +111 -0
- data/test/controller/addresses_render_test.rb +45 -0
- data/test/controller/caching_filestore.rb +92 -0
- data/test/controller/capture_test.rb +39 -0
- data/test/controller/cgi_test.rb +40 -3
- data/test/controller/helper_test.rb +65 -13
- data/test/controller/multipart_progress_testx.rb +365 -0
- data/test/controller/new_render_test.rb +263 -0
- data/test/controller/redirect_test.rb +64 -0
- data/test/controller/render_test.rb +20 -21
- data/test/controller/request_test.rb +83 -3
- data/test/controller/routing_test.rb +702 -0
- data/test/controller/send_file_test.rb +2 -0
- data/test/controller/test_test.rb +44 -8
- data/test/controller/upload_progress_testx.rb +89 -0
- data/test/controller/verification_test.rb +94 -29
- data/test/fixtures/addresses/list.rhtml +1 -0
- data/test/fixtures/test/capturing.rhtml +4 -0
- data/test/fixtures/test/list.rhtml +1 -1
- data/test/fixtures/test/update_element_with_capture.rhtml +9 -0
- data/test/template/active_record_helper_test.rb +30 -15
- data/test/template/asset_tag_helper_test.rb +12 -5
- data/test/template/benchmark_helper_test.rb +72 -0
- data/test/template/date_helper_test.rb +69 -0
- data/test/template/form_helper_test.rb +18 -10
- data/test/template/form_options_helper_test.rb +40 -5
- data/test/template/javascript_helper.rb +149 -2
- data/test/template/number_helper_test.rb +2 -0
- data/test/template/tag_helper_test.rb +4 -0
- data/test/template/text_helper_test.rb +36 -0
- data/test/template/upload_progress_helper_testx.rb +272 -0
- data/test/template/url_helper_test.rb +30 -0
- metadata +30 -6
- data/test/controller/layout_test.rb +0 -49
- data/test/controller/routing_tests.rb +0 -543
@@ -0,0 +1,421 @@
|
|
1
|
+
# Unfortunately we need to require multipart_progress here and not in
|
2
|
+
# uplaod_status_for because if the upload happens to hit a fresh FCGI instance
|
3
|
+
# the upload_status_for method will be called after the CGI object is created
|
4
|
+
# Requiring here means that multipart progress will be enabled for all multipart
|
5
|
+
# postings.
|
6
|
+
require 'action_controller/cgi_ext/multipart_progress'
|
7
|
+
|
8
|
+
module ActionController #:nodoc:
|
9
|
+
# == THIS IS AN EXPERIMENTAL FEATURE
|
10
|
+
#
|
11
|
+
# Which means that it doesn't yet work on all systems. We're still working on full
|
12
|
+
# compatibility. It's thus not advised to use this unless you've verified it to work
|
13
|
+
# fully on all the systems that is a part of your environment. Consider this an extended
|
14
|
+
# preview.
|
15
|
+
#
|
16
|
+
# == Action Pack Upload Progress for multipart uploads
|
17
|
+
#
|
18
|
+
# The UploadProgress module aids in the process of viewing an Ajax driven
|
19
|
+
# upload status when working with multipart forms. It offers a macro that
|
20
|
+
# will prepare an action for handling the cleanup of the Ajax updating including
|
21
|
+
# passing the redirect URL and custom parameters to the Javascript finish handler.
|
22
|
+
#
|
23
|
+
# UploadProgress is available for all multipart uploads when the +upload_status_for+
|
24
|
+
# macro is called in one of your controllers.
|
25
|
+
#
|
26
|
+
# The progress is stored as an UploadProgress::Progress object in the session and
|
27
|
+
# is accessible in the controller and view with the +upload_progress+ method.
|
28
|
+
#
|
29
|
+
# For help rendering the UploadProgress enabled form and supported elements, see
|
30
|
+
# ActionView::Helpers::UploadProgressHelper.
|
31
|
+
#
|
32
|
+
# === Automatic updating on upload actions
|
33
|
+
#
|
34
|
+
# class DocumentController < ApplicationController
|
35
|
+
# upload_status_for :create
|
36
|
+
#
|
37
|
+
# def create
|
38
|
+
# # ... Your document creation action
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# The +upload_status_for+ macro will override the rendering of the action passed
|
43
|
+
# if +upload_id+ is found in the query string. This allows for default
|
44
|
+
# behavior if Javascript is disabled. If you are tracking the upload progress
|
45
|
+
# then +create+ will now return the cleanup scripts that will terminate the polling
|
46
|
+
# of the upload status.
|
47
|
+
#
|
48
|
+
# === Customized status rendering
|
49
|
+
#
|
50
|
+
# class DocumentController < ApplicationController
|
51
|
+
# upload_status_for :create, :status => :custom_status
|
52
|
+
#
|
53
|
+
# def create
|
54
|
+
# # ... Your document creation action
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# def custom_status
|
58
|
+
# # ... Override this action to return content to be replaced in
|
59
|
+
# # the status container
|
60
|
+
# render :inline => "<%= upload_progress.completed_percent rescue 0 %> % complete", :layout => false
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# The default status action is +upload_status+. The results of this action
|
64
|
+
# are added used to replace the contents of the HTML elements defined in
|
65
|
+
# +upload_status_tag+. Within +upload_status+, you can load the Progress
|
66
|
+
# object from the session with the +upload_progress+ method and display your own
|
67
|
+
# results.
|
68
|
+
#
|
69
|
+
# Completion of the upload status updating occurs automatically with an +after_filter+ call to
|
70
|
+
# +finish_upload_status+. Because the upload must be posted into a hidden IFRAME to enable
|
71
|
+
# Ajax updates during the upload, +finish_upload_status+ overwrites the results of any previous
|
72
|
+
# +render+ or +redirect_to+ so it can render the necessary Javascript that will properly terminate
|
73
|
+
# the status updating loop, trigger the completion callback or redirect to the appropriate URL.
|
74
|
+
#
|
75
|
+
# ==== Basic Example (View):
|
76
|
+
#
|
77
|
+
# <%= form_tag_with_upload_progress({:action => 'create'}, {:finish => 'alert("Document Uploaded")'}) %>
|
78
|
+
# <%= upload_status_tag %>
|
79
|
+
# <%= file_field 'document', 'file' %>
|
80
|
+
# <%= end_form_tag %>
|
81
|
+
#
|
82
|
+
# ==== Basic Example (Controller):
|
83
|
+
#
|
84
|
+
# class DocumentController < ApplicationController
|
85
|
+
# upload_status_for :create
|
86
|
+
#
|
87
|
+
# def create
|
88
|
+
# @document = Document.create(params[:document])
|
89
|
+
# end
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# ==== Extended Example (View):
|
93
|
+
#
|
94
|
+
# <%= form_tag_with_upload_progress({:action => 'create'}, {}, {:action => :custom_status}) %>
|
95
|
+
# <%= upload_status_tag %>
|
96
|
+
# <%= file_field 'document', 'file' %>
|
97
|
+
# <%= submit_tag "Upload" %>
|
98
|
+
# <%= end_form_tag %>
|
99
|
+
#
|
100
|
+
# <%= form_tag_with_upload_progress({:action => 'add_preview'}, {:finish => 'alert(arguments[0])'}, {:action => :custom_status}) %>
|
101
|
+
# <%= upload_status_tag %>
|
102
|
+
# <%= submit_tag "Upload" %>
|
103
|
+
# <%= file_field 'preview', 'file' %>
|
104
|
+
# <%= end_form_tag %>
|
105
|
+
#
|
106
|
+
# ==== Extended Example (Controller):
|
107
|
+
#
|
108
|
+
# class DocumentController < ApplicationController
|
109
|
+
# upload_status_for :add_preview, :create, {:status => :custom_status}
|
110
|
+
#
|
111
|
+
# def add_preview
|
112
|
+
# @document = Document.find(params[:id])
|
113
|
+
# @document.preview = Preview.create(params[:preview])
|
114
|
+
# if @document.save
|
115
|
+
# finish_upload_status "'Preview added'"
|
116
|
+
# else
|
117
|
+
# finish_upload_status "'Preview not added'"
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# def create
|
122
|
+
# @document = Document.new(params[:document])
|
123
|
+
#
|
124
|
+
# upload_progress.message = "Processing document..."
|
125
|
+
# session.update
|
126
|
+
#
|
127
|
+
# @document.save
|
128
|
+
# redirect_to :action => 'show', :id => @document.id
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# def custom_status
|
132
|
+
# render :inline => '<%= upload_progress_status %> <div>Updated at <%= Time.now %></div>', :layout => false
|
133
|
+
# end
|
134
|
+
module UploadProgress
|
135
|
+
def self.append_features(base) #:nodoc:
|
136
|
+
super
|
137
|
+
base.extend(ClassMethods)
|
138
|
+
base.helper_method :upload_progress, :next_upload_id, :last_upload_id, :current_upload_id
|
139
|
+
end
|
140
|
+
|
141
|
+
module ClassMethods #:nodoc:
|
142
|
+
# Creates an +after_filter+ which will call +finish_upload_status+
|
143
|
+
# creating the document that will be loaded into the hidden IFRAME, terminating
|
144
|
+
# the status polling forms created with +form_with_upload_progress+.
|
145
|
+
#
|
146
|
+
# Also defines an action +upload_status+ or a action name passed as
|
147
|
+
# the <tt>:status</tt> option. This status action must match the one expected
|
148
|
+
# in the +form_tag_with_upload_progress+ helper.
|
149
|
+
#
|
150
|
+
def upload_status_for(*actions)
|
151
|
+
after_filter :finish_upload_status, :only => actions
|
152
|
+
|
153
|
+
define_method(actions.last.is_a?(Hash) && actions.last[:status] || :upload_status) do
|
154
|
+
render(:inline => '<%= upload_progress_status %>', :layout => false)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Overwrites the body rendered if the upload comes from a form that tracks
|
160
|
+
# the progress of the upload. After clearing the body and any redirects, this
|
161
|
+
# method then renders the helper +finish_upload_status+
|
162
|
+
#
|
163
|
+
# This method only needs to be called if you wish to pass a
|
164
|
+
# javascript parameter to your finish event handler that you optionally
|
165
|
+
# define in +form_with_upload_progress+
|
166
|
+
#
|
167
|
+
# === Parameter:
|
168
|
+
#
|
169
|
+
# client_js_argument:: a string containing a Javascript expression that will
|
170
|
+
# be evaluated and passed to your +finish+ handler of
|
171
|
+
# +form_tag_with_upload_progress+.
|
172
|
+
#
|
173
|
+
# You can pass a String, Number or Boolean.
|
174
|
+
#
|
175
|
+
# === Strings
|
176
|
+
#
|
177
|
+
# Strings contain Javascript code that will be evaluated on the client. If you
|
178
|
+
# wish to pass a string to the client finish callback, you will need to include
|
179
|
+
# quotes in the +client_js_argument+ you pass to this method.
|
180
|
+
#
|
181
|
+
# ==== Example
|
182
|
+
#
|
183
|
+
# finish_upload_status("\"Finished\"")
|
184
|
+
# finish_upload_status("'Finished #{@document.title}'")
|
185
|
+
# finish_upload_status("{success: true, message: 'Done!'}")
|
186
|
+
# finish_upload_status("function() { alert('Uploaded!'); }")
|
187
|
+
#
|
188
|
+
# === Numbers / Booleans
|
189
|
+
#
|
190
|
+
# Numbers and Booleans can either be passed as Number objects or string versions
|
191
|
+
# of number objects as they are evaluated by Javascript the same way as in Ruby.
|
192
|
+
#
|
193
|
+
# ==== Example
|
194
|
+
#
|
195
|
+
# finish_upload_status(0)
|
196
|
+
# finish_upload_status(@document.file.size)
|
197
|
+
# finish_upload_status("10")
|
198
|
+
#
|
199
|
+
# === Nil
|
200
|
+
#
|
201
|
+
# To pass +nil+ to the finish callback, use a string "undefined"
|
202
|
+
#
|
203
|
+
# ==== Example
|
204
|
+
#
|
205
|
+
# finish_upload_status(@message || "undefined")
|
206
|
+
#
|
207
|
+
# == Redirection
|
208
|
+
#
|
209
|
+
# If you action performs a redirection then +finish_upload_status+ will recognize
|
210
|
+
# the redirection and properly create the Javascript to perform the redirection in
|
211
|
+
# the proper location.
|
212
|
+
#
|
213
|
+
# It is possible to redirect and pass a parameter to the finish callback.
|
214
|
+
#
|
215
|
+
# ==== Example
|
216
|
+
#
|
217
|
+
# redirect_to :action => 'show', :id => @document.id
|
218
|
+
# finish_upload_status("'Redirecting you to your new file'")
|
219
|
+
#
|
220
|
+
#
|
221
|
+
def finish_upload_status(client_js_argument='')
|
222
|
+
if not @rendered_finish_upload_status and params[:upload_id]
|
223
|
+
@rendered_finish_upload_status = true
|
224
|
+
|
225
|
+
erase_render_results
|
226
|
+
location = erase_redirect_results || ''
|
227
|
+
|
228
|
+
## TODO determine if #inspect is the appropriate way to marshall values
|
229
|
+
## in inline templates
|
230
|
+
|
231
|
+
template = "<%= finish_upload_status({"
|
232
|
+
template << ":client_js_argument => #{client_js_argument.inspect}, "
|
233
|
+
template << ":redirect_to => #{location.to_s.inspect}, "
|
234
|
+
template << "}) %>"
|
235
|
+
|
236
|
+
render({ :inline => template, :layout => false })
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Returns and saves the next unique +upload_id+ in the instance variable
|
241
|
+
# <tt>@upload_id</tt>
|
242
|
+
def next_upload_id
|
243
|
+
@upload_id = last_upload_id.succ
|
244
|
+
end
|
245
|
+
|
246
|
+
# Either returns the last saved +upload_id+ or looks in the session
|
247
|
+
# for the last used +upload_id+ and saves it as the intance variable
|
248
|
+
# <tt>@upload_id</tt>
|
249
|
+
def last_upload_id
|
250
|
+
@upload_id ||= ((session[:uploads] || {}).keys.map{|k| k.to_i}.sort.last || 0).to_s
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns the +upload_id+ from the query parameters or if it cannot be found
|
254
|
+
# in the query parameters, then return the +last_upload_id+
|
255
|
+
def current_upload_id
|
256
|
+
params[:upload_id] or last_upload_id
|
257
|
+
end
|
258
|
+
|
259
|
+
# Get the UploadProgress::Progress object for the supplied +upload_id+ from the
|
260
|
+
# session. If no +upload_id+ is given, then use the +current_upload_id+
|
261
|
+
#
|
262
|
+
# If an UploadProgress::Progress object cannot be found, a new instance will be
|
263
|
+
# returned with <code>total_bytes == 0</code>, <code>started? == false</code>,
|
264
|
+
# and <code>finished? == true</code>.
|
265
|
+
def upload_progress(upload_id = nil)
|
266
|
+
upload_id ||= current_upload_id
|
267
|
+
session[:uploads] && session[:uploads][upload_id] || UploadProgress::Progress.new(0)
|
268
|
+
end
|
269
|
+
|
270
|
+
# == THIS IS AN EXPERIMENTAL FEATURE
|
271
|
+
#
|
272
|
+
# Which means that it doesn't yet work on all systems. We're still working on full
|
273
|
+
# compatibility. It's thus not advised to use this unless you've verified it to work
|
274
|
+
# fully on all the systems that is a part of your environment. Consider this an extended
|
275
|
+
# preview.
|
276
|
+
#
|
277
|
+
# Upload Progress abstracts the progress of an upload. It's used by the
|
278
|
+
# multipart progress IO that keeps track of the upload progress and creating
|
279
|
+
# the application depends on. It contians methods to update the progress
|
280
|
+
# during an upload and read the statistics such as +received_bytes+,
|
281
|
+
# +total_bytes+, +completed_percent+, +bitrate+, and
|
282
|
+
# +remaining_seconds+
|
283
|
+
#
|
284
|
+
# You can get the current +Progress+ object by calling +upload_progress+ instance
|
285
|
+
# method in your controller or view.
|
286
|
+
#
|
287
|
+
class Progress
|
288
|
+
unless const_defined? :MIN_SAMPLE_TIME
|
289
|
+
# Number of seconds between bitrate samples. Updates that occur more
|
290
|
+
# frequently than +MIN_SAMPLE_TIME+ will not be queued until this
|
291
|
+
# time passes. This behavior gives a good balance of accuracy and load
|
292
|
+
# for both fast and slow transfers.
|
293
|
+
MIN_SAMPLE_TIME = 0.150
|
294
|
+
|
295
|
+
# Number of seconds between updates before giving up to try and calculate
|
296
|
+
# bitrate anymore
|
297
|
+
MIN_STALL_TIME = 10.0
|
298
|
+
|
299
|
+
# Number of samples used to calculate bitrate
|
300
|
+
MAX_SAMPLES = 20
|
301
|
+
end
|
302
|
+
|
303
|
+
# Number bytes received from the multipart post
|
304
|
+
attr_reader :received_bytes
|
305
|
+
|
306
|
+
# Total number of bytes expected from the mutlipart post
|
307
|
+
attr_reader :total_bytes
|
308
|
+
|
309
|
+
# The last time the upload history was updated
|
310
|
+
attr_reader :last_update_time
|
311
|
+
|
312
|
+
# A message you can set from your controller or view to be rendered in the
|
313
|
+
# +upload_status_text+ helper method. If you set a messagein a controller
|
314
|
+
# then call <code>session.update</code> to make that message available to
|
315
|
+
# your +upload_status+ action.
|
316
|
+
attr_accessor :message
|
317
|
+
|
318
|
+
# Create a new Progress object passing the expected number of bytes to receive
|
319
|
+
def initialize(total)
|
320
|
+
@total_bytes = total
|
321
|
+
reset!
|
322
|
+
end
|
323
|
+
|
324
|
+
# Resets the received_bytes, last_update_time, message and bitrate, but
|
325
|
+
# but maintains the total expected bytes
|
326
|
+
def reset!
|
327
|
+
@received_bytes, @last_update_time, @stalled, @message = 0, 0, false, ''
|
328
|
+
reset_history
|
329
|
+
end
|
330
|
+
|
331
|
+
# Number of bytes left for this upload
|
332
|
+
def remaining_bytes
|
333
|
+
@total_bytes - @received_bytes
|
334
|
+
end
|
335
|
+
|
336
|
+
# Completed percent in integer form from 0..100
|
337
|
+
def completed_percent
|
338
|
+
(@received_bytes * 100 / @total_bytes).to_i rescue 0
|
339
|
+
end
|
340
|
+
|
341
|
+
# Updates this UploadProgress object with the number of bytes received
|
342
|
+
# since last update time and the absolute number of seconds since the
|
343
|
+
# beginning of the upload.
|
344
|
+
#
|
345
|
+
# This method is used by the +MultipartProgress+ module and should
|
346
|
+
# not be called directly.
|
347
|
+
def update!(bytes, elapsed_seconds)#:nodoc:
|
348
|
+
if @received_bytes + bytes > @total_bytes
|
349
|
+
#warn "Progress#update received bytes exceeds expected bytes"
|
350
|
+
bytes = @total_bytes - @received_bytes
|
351
|
+
end
|
352
|
+
|
353
|
+
@received_bytes += bytes
|
354
|
+
|
355
|
+
# Age is the duration of time since the last update to the history
|
356
|
+
age = elapsed_seconds - @last_update_time
|
357
|
+
|
358
|
+
# Record the bytes received in the first element of the history
|
359
|
+
# in case the sample rate is exceeded and we shouldn't record at this
|
360
|
+
# time
|
361
|
+
@history.first[0] += bytes
|
362
|
+
@history.first[1] += age
|
363
|
+
|
364
|
+
history_age = @history.first[1]
|
365
|
+
|
366
|
+
@history.pop while @history.size > MAX_SAMPLES
|
367
|
+
@history.unshift([0,0]) if history_age > MIN_SAMPLE_TIME
|
368
|
+
|
369
|
+
if history_age > MIN_STALL_TIME
|
370
|
+
@stalled = true
|
371
|
+
reset_history
|
372
|
+
else
|
373
|
+
@stalled = false
|
374
|
+
end
|
375
|
+
|
376
|
+
@last_update_time = elapsed_seconds
|
377
|
+
|
378
|
+
self
|
379
|
+
end
|
380
|
+
|
381
|
+
# Calculates the bitrate in bytes/second. If the transfer is stalled or
|
382
|
+
# just started, the bitrate will be 0
|
383
|
+
def bitrate
|
384
|
+
history_bytes, history_time = @history.transpose.map { |vals| vals.inject { |sum, v| sum + v } }
|
385
|
+
history_bytes / history_time rescue 0
|
386
|
+
end
|
387
|
+
|
388
|
+
# Number of seconds elapsed since the start of the upload
|
389
|
+
def elapsed_seconds
|
390
|
+
@last_update_time
|
391
|
+
end
|
392
|
+
|
393
|
+
# Calculate the seconds remaining based on the current bitrate. Returns
|
394
|
+
# O seconds if stalled or if no bytes have been received
|
395
|
+
def remaining_seconds
|
396
|
+
remaining_bytes / bitrate rescue 0
|
397
|
+
end
|
398
|
+
|
399
|
+
# Returns true if there are bytes pending otherwise returns false
|
400
|
+
def finished?
|
401
|
+
remaining_bytes <= 0
|
402
|
+
end
|
403
|
+
|
404
|
+
# Returns true if some bytes have been received
|
405
|
+
def started?
|
406
|
+
@received_bytes > 0
|
407
|
+
end
|
408
|
+
|
409
|
+
# Returns true if there has been a delay in receiving bytes. The delay
|
410
|
+
# is set by the constant MIN_STALL_TIME
|
411
|
+
def stalled?
|
412
|
+
@stalled
|
413
|
+
end
|
414
|
+
|
415
|
+
private
|
416
|
+
def reset_history
|
417
|
+
@history = [[0,0]]
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
@@ -12,7 +12,7 @@ module ActionController
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def to_str
|
15
|
-
|
15
|
+
"#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
|
16
16
|
end
|
17
17
|
|
18
18
|
alias_method :to_s, :to_str
|
@@ -28,7 +28,7 @@ module ActionController
|
|
28
28
|
rewritten_url << '/' if options[:trailing_slash]
|
29
29
|
rewritten_url << "##{options[:anchor]}" if options[:anchor]
|
30
30
|
|
31
|
-
|
31
|
+
rewritten_url
|
32
32
|
end
|
33
33
|
|
34
34
|
def rewrite_path(options)
|
@@ -38,17 +38,15 @@ module ActionController
|
|
38
38
|
path, extras = Routing::Routes.generate(options, @request)
|
39
39
|
|
40
40
|
if extras[:overwrite_params]
|
41
|
-
params_copy = @request.parameters.reject { |k,v|
|
41
|
+
params_copy = @request.parameters.reject { |k,v| %w(controller action).include? k }
|
42
42
|
params_copy.update extras[:overwrite_params]
|
43
43
|
extras.delete(:overwrite_params)
|
44
44
|
extras.update(params_copy)
|
45
45
|
end
|
46
46
|
|
47
|
-
path
|
48
|
-
path = '/' if path.empty?
|
49
|
-
path += build_query_string(extras)
|
47
|
+
path << build_query_string(extras) unless extras.empty?
|
50
48
|
|
51
|
-
|
49
|
+
path
|
52
50
|
end
|
53
51
|
|
54
52
|
# Returns a query string with escaped keys and values from the passed hash. If the passed hash contains an "id" it'll
|
@@ -58,15 +56,14 @@ module ActionController
|
|
58
56
|
query_string = ""
|
59
57
|
|
60
58
|
hash.each do |key, value|
|
61
|
-
key = key.to_s
|
62
|
-
key
|
63
|
-
key += '[]' if value.class == Array
|
59
|
+
key = CGI.escape key.to_s
|
60
|
+
key << '[]' if value.class == Array
|
64
61
|
value = [ value ] unless value.class == Array
|
65
62
|
value.each { |val| elements << "#{key}=#{Routing.extract_parameter_value(val)}" }
|
66
63
|
end
|
67
64
|
|
68
65
|
query_string << ("?" + elements.join("&")) unless elements.empty?
|
69
|
-
|
66
|
+
query_string
|
70
67
|
end
|
71
68
|
end
|
72
69
|
end
|